1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Extract battleStackIsImmune from battleIsImmune

This fix possible problems with rising spells as now immunty is handled on stack level not on hex level
* battleIsImmune in now protected - only used in canCastThisSpellHere
This commit is contained in:
AlexVinS
2014-05-19 13:44:38 +04:00
parent c22908651c
commit 6f65d2484b
4 changed files with 116 additions and 83 deletions

View File

@@ -459,9 +459,9 @@ void CBattleAI::attemptCastingSpell()
int damageDealt = 0, damageReceived = 0;
auto stacksSuffering = cb->getAffectedCreatures(ps.spell, skillLevel, playerID, ps.dest);
vstd::erase_if(stacksSuffering, [&](const CStack *stack) -> bool
vstd::erase_if(stacksSuffering, [&](const CStack * s) -> bool
{
return cb->battleIsImmune(hero, ps.spell, ECastingMode::HERO_CASTING, ps.dest);
return ESpellCastProblem::OK != cb->battleStackIsImmune(hero, ps.spell, ECastingMode::HERO_CASTING, s);
});
if(stacksSuffering.empty())

View File

@@ -1569,87 +1569,33 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
{
RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
// Get stack at destination hex -> subject of our spell.
const CStack * subject = battleGetStackByPos(dest, !spell->isRisingSpell()); //only alive if not rising spell
if(subject)
// Get all stacks at destination hex -> subject of our spell. only alive if not rising spell
TStacks stacks = battleGetStacksIf([=](const CStack * s){
return s->coversPos(dest) && (spell->isRisingSpell() || s->alive());
});
if(!stacks.empty())
{
if (spell->isPositive() && subject->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells
return ESpellCastProblem::OK;
if (spell->isImmuneBy(subject)) //TODO: move all logic to spellhandler
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
switch (spell->id) //TODO: more general logic for new spells?
bool allImmune = true;
ESpellCastProblem::ESpellCastProblem problem;
for(auto s : stacks)
{
case SpellID::CLONE:
{
//can't clone already cloned creature
if (vstd::contains(subject->state, EBattleStackState::CLONED))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
//TODO: how about stacks casting Clone?
//currently Clone casted by stack is assumed Expert level
ui8 schoolLevel;
if (caster)
{
schoolLevel = caster->getSpellSchoolLevel(spell);
}
else
{
schoolLevel = 3;
}
if (schoolLevel < 3)
{
int maxLevel = (std::max(schoolLevel, (ui8)1) + 4);
int creLevel = subject->getCreature()->level;
if (maxLevel < creLevel) //tier 1-5 for basic, 1-6 for advanced, any level for expert
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
}
break;
case SpellID::DISPEL_HELPFUL_SPELLS:
{
TBonusListPtr spellBon = subject->getSpellBonuses();
bool hasPositiveSpell = false;
for(const Bonus * b : *spellBon)
{
if(SpellID(b->sid).toSpell()->isPositive())
{
hasPositiveSpell = true;
break;
}
}
if(!hasPositiveSpell)
{
return ESpellCastProblem::NO_SPELLS_TO_DISPEL;
}
}
break;
}
if (spell->isRisingSpell())
{
if(subject->count >= subject->baseAmount)
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
ESpellCastProblem::ESpellCastProblem res = battleStackIsImmune(caster,spell,mode,s);
if (caster) //FIXME: Archangels can cast immune stack
if(res == ESpellCastProblem::OK)
{
auto maxHealth = calculateHealedHP (caster, spell, subject);
if (maxHealth < subject->MaxHealth()) //must be able to rise at least one full creature
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
allImmune = false;
}
else
{
problem = res;
}
}
else if(spell->id == SpellID::HYPNOTIZE && caster) //do not resist hypnotize casted after attack, for example
{
//TODO: what with other creatures casting hypnotize, Faerie Dragons style?
ui64 subjectHealth = (subject->count - 1) * subject->MaxHealth() + subject->firstHPleft;
//apply 'damage' bonus for hypnotize, including hero specialty
ui64 maxHealth = calculateSpellBonus (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
* spell->power + spell->getPower(caster->getSpellSchoolLevel(spell)), spell, caster, subject);
if (subjectHealth > maxHealth)
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
if(allImmune)
return problem;
}
else //no target stack on this tile
{
@@ -1673,6 +1619,88 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
return ESpellCastProblem::OK;
}
ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleStackIsImmune(const CGHeroInstance * caster, const CSpell * spell, ECastingMode::ECastingMode mode, const CStack * subject) const
{
if (spell->isPositive() && subject->hasBonusOfType(Bonus::RECEPTIVE)) //accept all positive spells
return ESpellCastProblem::OK;
if (spell->isImmuneBy(subject)) //TODO: move all logic to spellhandler
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
switch (spell->id) //TODO: more general logic for new spells?
{
case SpellID::CLONE:
{
//can't clone already cloned creature
if (vstd::contains(subject->state, EBattleStackState::CLONED))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
//TODO: how about stacks casting Clone?
//currently Clone casted by stack is assumed Expert level
ui8 schoolLevel;
if (caster)
{
schoolLevel = caster->getSpellSchoolLevel(spell);
}
else
{
schoolLevel = 3;
}
if (schoolLevel < 3)
{
int maxLevel = (std::max(schoolLevel, (ui8)1) + 4);
int creLevel = subject->getCreature()->level;
if (maxLevel < creLevel) //tier 1-5 for basic, 1-6 for advanced, any level for expert
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
}
break;
case SpellID::DISPEL_HELPFUL_SPELLS:
{
TBonusListPtr spellBon = subject->getSpellBonuses();
bool hasPositiveSpell = false;
for(const Bonus * b : *spellBon)
{
if(SpellID(b->sid).toSpell()->isPositive())
{
hasPositiveSpell = true;
break;
}
}
if(!hasPositiveSpell)
{
return ESpellCastProblem::NO_SPELLS_TO_DISPEL;
}
}
break;
}
if (spell->isRisingSpell())
{
if(subject->count >= subject->baseAmount)
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
if (caster) //FIXME: Archangels can cast immune stack
{
auto maxHealth = calculateHealedHP (caster, spell, subject);
if (maxHealth < subject->MaxHealth()) //must be able to rise at least one full creature
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
}
else if(spell->id == SpellID::HYPNOTIZE && caster) //do not resist hypnotize casted after attack, for example
{
//TODO: what with other creatures casting hypnotize, Faerie Dragons style?
ui64 subjectHealth = (subject->count - 1) * subject->MaxHealth() + subject->firstHPleft;
//apply 'damage' bonus for hypnotize, including hero specialty
ui64 maxHealth = calculateSpellBonus (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
* spell->power + spell->getPower(caster->getSpellSchoolLevel(spell)), spell, caster, subject);
if (subjectHealth > maxHealth)
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
return ESpellCastProblem::OK;
}
ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell( PlayerColor player, const CSpell * spell, ECastingMode::ECastingMode mode ) const
{
RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
@@ -1715,7 +1743,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks();
for(auto stack : stacks)
{
if(!battleIsImmune(castingHero, spell, mode, stack->position))
if( ESpellCastProblem::OK == battleStackIsImmune(castingHero, spell, mode, stack))
{
allStacksImmune = false;
break;
@@ -1757,7 +1785,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
bool targetExists = false;
for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway
{
bool immune = battleIsImmune(caster, spell, mode, stack->position) != ESpellCastProblem::OK;
bool immune = ESpellCastProblem::OK != battleStackIsImmune(caster, spell, mode, stack);
bool casterStack = stack->owner == caster->getOwner();
if(!immune)
@@ -1812,7 +1840,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor
for(const CStack * stack : battleAliveStacks())
{
bool immune = battleIsImmune(caster, spell, mode, stack->position) != ESpellCastProblem::OK;
bool immune = ESpellCastProblem::OK != battleStackIsImmune(caster, spell, mode, stack);
bool casterStack = stack->owner == caster->getOwner();
if(!immune)

View File

@@ -293,7 +293,7 @@ public:
SpellID getRandomCastedSpell(const CStack * caster) const; //called at the beginning of turn for Faerie Dragon
//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.
ESpellCastProblem::ESpellCastProblem battleIsImmune(const CGHeroInstance * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const;
ESpellCastProblem::ESpellCastProblem battleStackIsImmune(const CGHeroInstance * caster, const CSpell * spell, ECastingMode::ECastingMode mode, const CStack * subject) const;
const CStack * getStackIf(std::function<bool(const CStack*)> pred) const;
@@ -321,6 +321,11 @@ public:
AccessibilityInfo getAccesibility(const std::vector<BattleHex> &accessibleHexes) const; //given hexes will be marked as accessible
std::pair<const CStack *, BattleHex> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const;
protected:
//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.
ESpellCastProblem::ESpellCastProblem battleIsImmune(const CGHeroInstance * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const;
ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters &params) const;
ReachabilityInfo makeBFS(const AccessibilityInfo &accessibility, const ReachabilityInfo::Parameters &params) const;
ReachabilityInfo makeBFS(const CStack *stack) const; //uses default parameters -> stack position and owner's perspective

View File

@@ -4050,7 +4050,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
}
vstd::erase_if(attackedCres,[=](const CStack * s){
return gs->curB->battleIsImmune(caster,spell,mode,s->position);
return ESpellCastProblem::OK != gs->curB->battleStackIsImmune(caster,spell,mode,s);
});
for (auto cre : attackedCres)
@@ -4463,7 +4463,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
{
if(battleStack->owner == gs->curB->sides.at(casterSide).color) //get enemy stacks which can be affected by this spell
{
if (!gs->curB->battleIsImmune(nullptr, spell, ECastingMode::MAGIC_MIRROR, battleStack->position))
if (ESpellCastProblem::OK == gs->curB->battleStackIsImmune(nullptr, spell, ECastingMode::MAGIC_MIRROR, battleStack))
mirrorTargets.push_back(battleStack);
}
}