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

Spellcasting fixes

Allowed the AI to cast spells that are aimed at a location instead of a unit. For example meteor shower.
Fixed an issue that caused the AI to not kill unit-stacks with spells due to only considering stacks where at least one unit survives in it's score-calculations.
This commit is contained in:
Xilmi 2024-07-10 02:40:42 +02:00
parent 13bbb573bd
commit 073c5bee45

View File

@ -392,7 +392,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
vstd::erase_if(possibleSpells, [](const CSpell *s)
{
return spellType(s) != SpellTypes::BATTLE || s->getTargetType() == spells::AimType::LOCATION;
return spellType(s) != SpellTypes::BATTLE;
});
LOGFL("I know how %d of them works.", possibleSpells.size());
@ -403,9 +403,6 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
{
spells::BattleCast temp(cb->getBattle(battleID).get(), hero, spells::Mode::HERO, spell);
if(spell->getTargetType() == spells::AimType::LOCATION)
continue;
const bool FAST = true;
for(auto & target : temp.findPotentialTargets(FAST))
@ -574,7 +571,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
auto & ps = possibleCasts[i];
#if BATTLE_TRACE_LEVEL >= 1
logAi->trace("Evaluating %s", ps.spell->getNameTranslated());
logAi->trace("Evaluating %s to %d", ps.spell->getNameTranslated(), ps.dest.at(0).hexValue.hex );
#endif
auto state = std::make_shared<HypotheticBattle>(env.get(), cb->getBattle(battleID));
@ -582,7 +579,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
spells::BattleCast cast(state.get(), hero, spells::Mode::HERO, ps.spell);
cast.castEval(state->getServerCallback(), ps.dest);
auto allUnits = state->battleGetUnitsIf([](const battle::Unit * u) -> bool { return true; });
auto allUnits = state->battleGetUnitsIf([](const battle::Unit * u) -> bool { return u->isValidTarget(); });
auto needFullEval = vstd::contains_if(allUnits, [&](const battle::Unit * u) -> bool
{
@ -621,11 +618,62 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
ps.value = scoreEvaluator.evaluateExchange(*cachedAttack, 0, *targets, innerCache, state);
}
for(const auto & unit : allUnits)
//! Some units may be dead alltogether. So if they existed before but not now, we know they were killed by the spell
for (const auto& unit : all)
{
if (!unit->isValidTarget())
continue;
bool isDead = true;
for (const auto& remainingUnit : allUnits)
{
if (remainingUnit->unitId() == unit->unitId())
isDead = false;
}
if (isDead)
{
auto newHealth = 0;
auto oldHealth = vstd::find_or(healthOfStack, unit->unitId(), 0);
if (oldHealth != newHealth)
{
auto damage = std::abs(oldHealth - newHealth);
auto originalDefender = cb->getBattle(battleID)->battleGetUnitByID(unit->unitId());
auto dpsReduce = AttackPossibility::calculateDamageReduce(
nullptr,
originalDefender && originalDefender->alive() ? originalDefender : unit,
damage,
innerCache,
state);
auto ourUnit = unit->unitSide() == side ? 1 : -1;
auto goodEffect = newHealth > oldHealth ? 1 : -1;
if (ourUnit * goodEffect == 1)
{
if (ourUnit && goodEffect && (unit->isClone() || unit->isGhost()))
continue;
ps.value += dpsReduce * scoreEvaluator.getPositiveEffectMultiplier();
}
else
ps.value -= dpsReduce * scoreEvaluator.getNegativeEffectMultiplier();
#if BATTLE_TRACE_LEVEL >= 1
logAi->trace(
"Spell %s to %d affects %s (%d), dps: %2f oldHealth: %d newHealth: %d",
ps.spell->getNameTranslated(),
ps.dest.at(0).hexValue.hex,
unit->creatureId().toCreature()->getNameSingularTranslated(),
unit->getCount(),
dpsReduce,
oldHealth,
newHealth);
#endif
}
}
}
for(const auto & unit : allUnits)
{
auto newHealth = unit->getAvailableHealth();
auto oldHealth = vstd::find_or(healthOfStack, unit->unitId(), 0); // old health value may not exist for newly summoned units
@ -656,10 +704,14 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
#if BATTLE_TRACE_LEVEL >= 1
logAi->trace(
"Spell affects %s (%d), dps: %2f",
"Spell %s to %d affects %s (%d), dps: %2f oldHealth: %d newHealth: %d",
ps.spell->getNameTranslated(),
ps.dest.at(0).hexValue.hex,
unit->creatureId().toCreature()->getNameSingularTranslated(),
unit->getCount(),
dpsReduce);
dpsReduce,
oldHealth,
newHealth);
#endif
}
}