mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Fixed Genie spellcasting logic to account for spell immunities
This commit is contained in:
parent
822677af77
commit
85de3143ff
@ -70,8 +70,9 @@ std::vector<BattleHex> BattleEvaluator::getBrokenWallMoatHexes() const
|
||||
std::optional<PossibleSpellcast> BattleEvaluator::findBestCreatureSpell(const CStack *stack)
|
||||
{
|
||||
//TODO: faerie dragon type spell should be selected by server
|
||||
SpellID creatureSpellToCast = cb->getBattle(battleID)->battleGetRandomStackSpell(CRandomGenerator::getDefault(), stack, CBattleInfoCallback::RANDOM_AIMED);
|
||||
if(stack->hasBonusOfType(BonusType::SPELLCASTER) && stack->canCast() && creatureSpellToCast != SpellID::NONE)
|
||||
SpellID creatureSpellToCast = cb->getBattle(battleID)->getRandomCastedSpell(CRandomGenerator::getDefault(), stack);
|
||||
|
||||
if(stack->canCast() && creatureSpellToCast != SpellID::NONE)
|
||||
{
|
||||
const CSpell * spell = creatureSpellToCast.toSpell();
|
||||
|
||||
|
@ -616,8 +616,8 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
|
||||
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
|
||||
if(targetStack && targetStackOwned && targetStack != owner.stacksController->getActiveStack() && targetStack->alive()) //only positive spells for other allied creatures
|
||||
{
|
||||
int spellID = owner.getBattle()->battleGetRandomStackSpell(CRandomGenerator::getDefault(), targetStack, CBattleInfoCallback::RANDOM_GENIE);
|
||||
return spellID > -1;
|
||||
SpellID spellID = owner.getBattle()->getRandomBeneficialSpell(CRandomGenerator::getDefault(), owner.stacksController->getActiveStack(), targetStack);
|
||||
return spellID != SpellID::NONE;
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -887,7 +887,7 @@ void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterS
|
||||
{
|
||||
// faerie dragon can cast only one, randomly selected spell until their next move
|
||||
//TODO: faerie dragon type spell should be selected by server
|
||||
const auto * spellToCast = owner.getBattle()->battleGetRandomStackSpell(CRandomGenerator::getDefault(), casterStack, CBattleInfoCallback::RANDOM_AIMED).toSpell();
|
||||
const auto * spellToCast = owner.getBattle()->getRandomCastedSpell(CRandomGenerator::getDefault(), casterStack).toSpell();
|
||||
|
||||
if (spellToCast)
|
||||
creatureSpells.push_back(spellToCast);
|
||||
|
@ -324,22 +324,6 @@ std::set<BattleHex> CBattleInfoCallback::battleGetAttackedHexes(const battle::Un
|
||||
return attackedHexes;
|
||||
}
|
||||
|
||||
SpellID CBattleInfoCallback::battleGetRandomStackSpell(CRandomGenerator & rand, const CStack * stack, ERandomSpell mode) const
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case RANDOM_GENIE:
|
||||
return getRandomBeneficialSpell(rand, stack); //target
|
||||
break;
|
||||
case RANDOM_AIMED:
|
||||
return getRandomCastedSpell(rand, stack); //caster
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("Incorrect mode of battleGetRandomSpell (%d)", static_cast<int>(mode));
|
||||
return SpellID::NONE;
|
||||
}
|
||||
}
|
||||
|
||||
const CStack* CBattleInfoCallback::battleGetStackByPos(BattleHex pos, bool onlyAlive) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(nullptr);
|
||||
@ -1610,7 +1594,7 @@ std::set<const battle::Unit *> CBattleInfoCallback::battleAdjacentUnits(const ba
|
||||
return ret;
|
||||
}
|
||||
|
||||
SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, const CStack * subject) const
|
||||
SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, const battle::Unit * caster, const battle::Unit * subject) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(SpellID::NONE);
|
||||
//This is complete list. No spells from mods.
|
||||
@ -1658,9 +1642,19 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c
|
||||
std::stringstream cachingStr;
|
||||
cachingStr << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT) << "id_" << spellID.num;
|
||||
|
||||
if(subject->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(spellID)), Selector::all, cachingStr.str())
|
||||
//TODO: this ability has special limitations
|
||||
|| !(spellID.toSpell()->canBeCast(this, spells::Mode::CREATURE_ACTIVE, subject)))
|
||||
if(subject->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(spellID)), Selector::all, cachingStr.str()))
|
||||
continue;
|
||||
|
||||
auto spellPtr = spellID.toSpell();
|
||||
spells::Target target;
|
||||
target.emplace_back(subject);
|
||||
|
||||
spells::BattleCast cast(this, caster, spells::Mode::CREATURE_ACTIVE, spellPtr);
|
||||
|
||||
auto m = spellPtr->battleMechanics(&cast);
|
||||
spells::detail::ProblemImpl problem;
|
||||
|
||||
if (!m->canBeCastAt(target, problem))
|
||||
continue;
|
||||
|
||||
switch (spellID.toEnum())
|
||||
@ -1703,7 +1697,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c
|
||||
case SpellID::CURE: //only damaged units
|
||||
{
|
||||
//do not cast on affected by debuffs
|
||||
if(!subject->canBeHealed())
|
||||
if(subject->getFirstHPleft() == subject->getMaxHealth())
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
@ -52,11 +52,6 @@ struct DLL_LINKAGE BattleClientInterfaceData
|
||||
class DLL_LINKAGE CBattleInfoCallback : public virtual CBattleInfoEssentials
|
||||
{
|
||||
public:
|
||||
enum ERandomSpell
|
||||
{
|
||||
RANDOM_GENIE, RANDOM_AIMED
|
||||
};
|
||||
|
||||
std::optional<int> battleIsFinished() const override; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw
|
||||
|
||||
std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const override;
|
||||
@ -121,8 +116,7 @@ public:
|
||||
int32_t battleGetSpellCost(const spells::Spell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
|
||||
ESpellCastProblem battleCanCastSpell(const spells::Caster * caster, spells::Mode mode) const; //returns true if there are no general issues preventing from casting a spell
|
||||
|
||||
SpellID battleGetRandomStackSpell(CRandomGenerator & rand, const CStack * stack, ERandomSpell mode) const;
|
||||
SpellID getRandomBeneficialSpell(CRandomGenerator & rand, const CStack * subject) const;
|
||||
SpellID getRandomBeneficialSpell(CRandomGenerator & rand, const battle::Unit * caster, const battle::Unit * target) const;
|
||||
SpellID getRandomCastedSpell(CRandomGenerator & rand, const CStack * caster) const; //called at the beginning of turn for Faerie Dragon
|
||||
|
||||
std::vector<PossiblePlayerBattleAction> getClientActionsForStack(const CStack * stack, const BattleClientInterfaceData & data);
|
||||
|
@ -411,24 +411,48 @@ bool BattleActionProcessor::doUnitSpellAction(const CBattleInfoCallback & battle
|
||||
std::shared_ptr<const Bonus> randSpellcaster = stack->getBonus(Selector::type()(BonusType::RANDOM_SPELLCASTER));
|
||||
std::shared_ptr<const Bonus> spellcaster = stack->getBonus(Selector::typeSubtype(BonusType::SPELLCASTER, BonusSubtypeID(spellID)));
|
||||
|
||||
//TODO special bonus for genies ability
|
||||
if (randSpellcaster && battle.battleGetRandomStackSpell(gameHandler->getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_AIMED) == SpellID::NONE)
|
||||
spellID = battle.battleGetRandomStackSpell(gameHandler->getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_GENIE);
|
||||
|
||||
if (spellID == SpellID::NONE)
|
||||
gameHandler->complain("That stack can't cast spells!");
|
||||
else
|
||||
if (!spellcaster && !randSpellcaster)
|
||||
{
|
||||
const CSpell * spell = SpellID(spellID).toSpell();
|
||||
spells::BattleCast parameters(&battle, stack, spells::Mode::CREATURE_ACTIVE, spell);
|
||||
int32_t spellLvl = 0;
|
||||
if(spellcaster)
|
||||
vstd::amax(spellLvl, spellcaster->val);
|
||||
if(randSpellcaster)
|
||||
vstd::amax(spellLvl, randSpellcaster->val);
|
||||
parameters.setSpellLevel(spellLvl);
|
||||
parameters.cast(gameHandler->spellEnv, target);
|
||||
gameHandler->complain("That stack can't cast spells!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (randSpellcaster)
|
||||
{
|
||||
if (target.size() != 1)
|
||||
{
|
||||
gameHandler->complain("Invalid target for random spellcaster!");
|
||||
return false;
|
||||
}
|
||||
|
||||
const battle::Unit * subject = target[0].unitValue;
|
||||
if (target[0].unitValue == nullptr)
|
||||
subject = battle.battleGetStackByPos(target[0].hexValue, true);
|
||||
|
||||
if (subject == nullptr)
|
||||
{
|
||||
gameHandler->complain("Invalid target for random spellcaster!");
|
||||
return false;
|
||||
}
|
||||
|
||||
spellID = battle.getRandomBeneficialSpell(gameHandler->getRandomGenerator(), stack, subject);
|
||||
|
||||
if (spellID == SpellID::NONE)
|
||||
{
|
||||
gameHandler->complain("That stack can't cast spells!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const CSpell * spell = SpellID(spellID).toSpell();
|
||||
spells::BattleCast parameters(&battle, stack, spells::Mode::CREATURE_ACTIVE, spell);
|
||||
int32_t spellLvl = 0;
|
||||
if(spellcaster)
|
||||
vstd::amax(spellLvl, spellcaster->val);
|
||||
if(randSpellcaster)
|
||||
vstd::amax(spellLvl, randSpellcaster->val);
|
||||
parameters.setSpellLevel(spellLvl);
|
||||
parameters.cast(gameHandler->spellEnv, target);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user