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

Rewrite Beneficial spell selection

This commit is contained in:
AlexVinS 2014-12-02 00:24:36 +03:00
parent b0df8172f9
commit 64dccfec80

View File

@ -1891,93 +1891,126 @@ std::set<const CStack*> CBattleInfoCallback:: batteAdjacentCreatures(const CStac
SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) const SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) const
{ {
RETURN_IF_NOT_BATTLE(SpellID::NONE); RETURN_IF_NOT_BATTLE(SpellID::NONE);
std::vector<SpellID> possibleSpells; //This is complete list. No spells from mods.
//todo: this should be Spellbook of caster Stack
for(const CSpell *spell : VLC->spellh->objects) static const std::set<SpellID> allPossibleSpells =
{ {
if (spell->isPositive() && !spell->isRisingSpell()) //only positive and not rising SpellID::AIR_SHIELD,
SpellID::ANTI_MAGIC,
SpellID::BLESS,
SpellID::BLOODLUST,
SpellID::COUNTERSTRIKE,
SpellID::CURE,
SpellID::FIRE_SHIELD,
SpellID::FORTUNE,
SpellID::HASTE,
SpellID::MAGIC_MIRROR,
SpellID::MIRTH,
SpellID::PRAYER,
SpellID::PRECISION,
SpellID::PROTECTION_FROM_AIR,
SpellID::PROTECTION_FROM_EARTH,
SpellID::PROTECTION_FROM_FIRE,
SpellID::PROTECTION_FROM_WATER,
SpellID::SHIELD,
SpellID::SLAYER,
SpellID::STONE_SKIN
};
std::vector<SpellID> beneficialSpells;
auto getAliveEnemy = [=](const std::function<bool(const CStack * )> & pred)
{
return getStackIf([=](const CStack * stack)
{ {
if (subject->hasBonusFrom(Bonus::SPELL_EFFECT, spell->id) return pred(stack) && stack->owner != subject->owner && stack->alive();
|| battleCanCastThisSpellHere(subject->owner, spell, ECastingMode::CREATURE_ACTIVE_CASTING, subject->position) != ESpellCastProblem::OK) });
continue; };
switch (spell->id) for(const SpellID spellID : allPossibleSpells)
{ {
case SpellID::SHIELD: if (subject->hasBonusFrom(Bonus::SPELL_EFFECT, spellID)
case SpellID::FIRE_SHIELD: // not if all enemy units are shooters //TODO: this ability has special limitations
{ || battleCanCastThisSpellHere(subject->owner, spellID.toSpell(), ECastingMode::CREATURE_ACTIVE_CASTING, subject->position) != ESpellCastProblem::OK)
auto walker = getStackIf([&](const CStack *stack) //look for enemy, non-shooting stack continue;
{
return stack->owner != subject->owner && !stack->shots;
});
if (!walker) switch (spellID)
continue; {
} case SpellID::SHIELD:
break; case SpellID::FIRE_SHIELD: // not if all enemy units are shooters
case SpellID::AIR_SHIELD: //only against active shooters {
auto walker = getAliveEnemy([&](const CStack * stack) //look for enemy, non-shooting stack
{ {
return !stack->shots;
});
auto shooter = getStackIf([&](const CStack *stack) //look for enemy, non-shooting stack if (!walker)
{ continue;
return stack->owner != subject->owner && stack->hasBonusOfType(Bonus::SHOOTER) && stack->shots;
});
if (!shooter)
continue;
}
break;
case SpellID::ANTI_MAGIC:
case SpellID::MAGIC_MIRROR:
{
if (!battleHasHero(subject->attackerOwned)) //only if there is enemy hero
continue;
}
break;
case SpellID::CURE: //only damaged units - what about affected by curse?
{
if (subject->firstHPleft >= subject->MaxHealth())
continue;
}
break;
case SpellID::BLOODLUST:
{
if (subject->shots) //if can shoot - only if enemy uits are adjacent
continue;
}
break;
case SpellID::PRECISION:
{
if (!(subject->hasBonusOfType(Bonus::SHOOTER) && subject->shots))
continue;
}
break;
case SpellID::SLAYER://only if monsters are present
{
auto kingMonster = getStackIf([&](const CStack *stack) -> bool //look for enemy, non-shooting stack
{
const auto isKing = Selector::type(Bonus::KING1)
.Or(Selector::type(Bonus::KING2))
.Or(Selector::type(Bonus::KING3));
return stack->owner != subject->owner && stack->hasBonus(isKing);
});
if (!kingMonster)
continue;
}
break;
case SpellID::TELEPORT: //issue 1928
case SpellID::CLONE: //not allowed
continue;
break;
} }
possibleSpells.push_back(spell->id); break;
case SpellID::AIR_SHIELD: //only against active shooters
{
auto shooter = getAliveEnemy([&](const CStack * stack) //look for enemy, non-shooting stack
{
return stack->hasBonusOfType(Bonus::SHOOTER) && stack->shots;
});
if (!shooter)
continue;
}
break;
case SpellID::ANTI_MAGIC:
case SpellID::MAGIC_MIRROR:
case SpellID::PROTECTION_FROM_AIR:
case SpellID::PROTECTION_FROM_EARTH:
case SpellID::PROTECTION_FROM_FIRE:
case SpellID::PROTECTION_FROM_WATER:
{
const ui8 enemySide = (ui8)subject->attackerOwned;
//todo: only if enemy has spellbook
if (!battleHasHero(enemySide)) //only if there is enemy hero
continue;
}
break;
case SpellID::CURE: //only damaged units
{
//do not cast on affected by debuffs
if (subject->firstHPleft >= subject->MaxHealth())
continue;
}
break;
case SpellID::BLOODLUST:
{
if (subject->shots) //if can shoot - only if enemy uits are adjacent
continue;
}
break;
case SpellID::PRECISION:
{
if (!(subject->hasBonusOfType(Bonus::SHOOTER) && subject->shots))
continue;
}
break;
case SpellID::SLAYER://only if monsters are present
{
auto kingMonster = getAliveEnemy([&](const CStack *stack) -> bool //look for enemy, non-shooting stack
{
const auto isKing = Selector::type(Bonus::KING1)
.Or(Selector::type(Bonus::KING2))
.Or(Selector::type(Bonus::KING3));
return stack->hasBonus(isKing);
});
if (!kingMonster)
continue;
}
break;
} }
beneficialSpells.push_back(spellID);
} }
if(!possibleSpells.empty()) if(!beneficialSpells.empty())
{ {
return *RandomGeneratorUtil::nextItem(possibleSpells, gs->getRandomGenerator()); return *RandomGeneratorUtil::nextItem(beneficialSpells, gs->getRandomGenerator());
} }
else else
{ {