2121#include < algorithm>
2222#include < cassert>
2323#include < cmath>
24+ #include < cstddef>
2425#include < cstdint>
2526#include < ostream>
2627#include < vector>
@@ -58,74 +59,15 @@ namespace
5859 return result;
5960 }
6061
61- bool isSpellcastUselessForUnit ( const Battle::Unit & unit, const Spell & spell )
62+ int32_t getSpellPower ( const HeroBase * hero )
6263 {
63- const int spellID = spell. GetID ( );
64+ assert ( hero != nullptr );
6465
65- if ( unit.isImmovable () && spellID != Spell::ANTIMAGIC ) {
66- return true ;
67- }
68-
69- switch ( spellID ) {
70- case Spell::BLESS :
71- case Spell::MASSBLESS :
72- return unit.Modes ( Battle::SP_BLESS );
73-
74- case Spell::BLOODLUST :
75- return unit.Modes ( Battle::SP_BLOODLUST );
76-
77- case Spell::CURSE :
78- case Spell::MASSCURSE :
79- return unit.Modes ( Battle::SP_CURSE );
80-
81- case Spell::HASTE :
82- case Spell::MASSHASTE :
83- return unit.Modes ( Battle::SP_HASTE );
84-
85- case Spell::SHIELD :
86- case Spell::MASSSHIELD :
87- return unit.Modes ( Battle::SP_SHIELD );
88-
89- case Spell::SLOW :
90- case Spell::MASSSLOW :
91- return unit.Modes ( Battle::SP_SLOW );
92-
93- case Spell::STONESKIN :
94- case Spell::STEELSKIN :
95- return unit.Modes ( Battle::SP_STONESKIN | Battle::SP_STEELSKIN );
96-
97- case Spell::BLIND :
98- case Spell::PARALYZE :
99- case Spell::PETRIFY :
100- return unit.Modes ( Battle::SP_BLIND | Battle::SP_PARALYZE | Battle::SP_STONE );
101-
102- case Spell::DRAGONSLAYER :
103- return unit.Modes ( Battle::SP_DRAGONSLAYER );
104-
105- case Spell::ANTIMAGIC :
106- return unit.Modes ( Battle::SP_ANTIMAGIC );
107-
108- case Spell::BERSERKER :
109- return unit.Modes ( Battle::SP_BERSERKER );
110-
111- case Spell::HYPNOTIZE :
112- return unit.Modes ( Battle::SP_HYPNOTIZE );
113-
114- case Spell::MIRRORIMAGE :
115- return unit.Modes ( Battle::CAP_MIRROROWNER );
116-
117- case Spell::DISRUPTINGRAY :
118- return unit.GetDefense () <= 1 ;
119-
120- default :
121- break ;
122- }
123-
124- return false ;
66+ return hero->GetPower () + hero->GetBagArtifacts ().getTotalArtifactEffectValue ( fheroes2::ArtifactBonusType::EVERY_COMBAT_SPELL_DURATION );
12567 }
12668}
12769
128- AI ::SpellSelection AI::BattlePlanner::selectBestSpell ( Battle::Arena & arena, const Battle::Unit & currentUnit, bool retreating ) const
70+ AI ::SpellSelection AI::BattlePlanner::selectBestSpell ( Battle::Arena & arena, const Battle::Unit & currentUnit, const bool retreating ) const
12971{
13072 SpellSelection bestSpell;
13173
@@ -197,10 +139,10 @@ AI::SpellSelection AI::BattlePlanner::selectBestSpell( Battle::Arena & arena, co
197139 checkSelectBestSpell ( spell, spellEarthquakeValue ( arena, spell, friendly ) );
198140 }
199141 else if ( spell.isApplyToFriends () ) {
200- checkSelectBestSpell ( spell, spellEffectValue ( spell, trueFriendly ) );
142+ checkSelectBestSpell ( spell, spellEffectValue ( spell, trueFriendly, enemies ) );
201143 }
202144 else if ( spell.isApplyToEnemies () ) {
203- checkSelectBestSpell ( spell, spellEffectValue ( spell, trueEnemies ) );
145+ checkSelectBestSpell ( spell, spellEffectValue ( spell, trueEnemies, enemies ) );
204146 }
205147 }
206148
@@ -324,8 +266,7 @@ AI::SpellcastOutcome AI::BattlePlanner::spellDamageValue( const Spell & spell, B
324266
325267int32_t AI::BattlePlanner::spellDurationMultiplier ( const Battle::Unit & target ) const
326268{
327- const int32_t duration
328- = _commander->GetPower () + _commander->GetBagArtifacts ().getTotalArtifactEffectValue ( fheroes2::ArtifactBonusType::EVERY_COMBAT_SPELL_DURATION );
269+ const int32_t duration = getSpellPower ( _commander );
329270
330271 if ( duration < 2 && target.Modes ( Battle::TR_MOVED ) ) {
331272 return 0 ;
@@ -402,12 +343,13 @@ double AI::BattlePlanner::getSpellHasteRatio( const Battle::Unit & target ) cons
402343 return ratio;
403344}
404345
405- double AI::BattlePlanner::spellEffectValue ( const Spell & spell, const Battle::Unit & target, bool targetIsLast, bool forDispel ) const
346+ double AI::BattlePlanner::spellEffectValue ( const Spell & spell, const Battle::Unit & target, const Battle::Units & enemies, bool targetIsLast,
347+ const bool forDispel ) const
406348{
407349 const int spellID = spell.GetID ();
408350
409351 // Make sure that this spell makes sense to apply (skip this check to evaluate the effect of dispelling)
410- if ( !forDispel && ( isSpellcastUselessForUnit ( target, spell ) || !target.AllowApplySpell ( spell, _commander ) ) ) {
352+ if ( !forDispel && ( isSpellcastUselessForUnit ( target, enemies, spell ) || !target.AllowApplySpell ( spell, _commander ) ) ) {
411353 return 0.0 ;
412354 }
413355
@@ -535,15 +477,15 @@ double AI::BattlePlanner::spellEffectValue( const Spell & spell, const Battle::U
535477 return target.GetStrength () * ratio * spellDurationMultiplier ( target );
536478}
537479
538- AI ::SpellcastOutcome AI::BattlePlanner::spellEffectValue ( const Spell & spell, const Battle::Units & targets ) const
480+ AI ::SpellcastOutcome AI::BattlePlanner::spellEffectValue ( const Spell & spell, const Battle::Units & targets, const Battle::Units & enemies ) const
539481{
540482 const bool isSingleTargetLeft = targets.size () == 1 ;
541483 const bool isMassSpell = spell.isMassActions ();
542484
543485 SpellcastOutcome bestOutcome;
544486
545487 for ( const Battle::Unit * unit : targets ) {
546- bestOutcome.updateOutcome ( spellEffectValue ( spell, *unit, isSingleTargetLeft, false ), unit->GetHeadIndex (), isMassSpell );
488+ bestOutcome.updateOutcome ( spellEffectValue ( spell, *unit, enemies, isSingleTargetLeft, false ), unit->GetHeadIndex (), isMassSpell );
547489 }
548490
549491 return bestOutcome;
@@ -565,7 +507,7 @@ AI::SpellcastOutcome AI::BattlePlanner::spellDispelValue( const Spell & spell, c
565507 double unitValue = 0 ;
566508 const std::vector<Spell> & spellList = unit->getCurrentSpellEffects ();
567509 for ( const Spell & spellOnFriend : spellList ) {
568- const double effectValue = spellEffectValue ( spellOnFriend, *unit, false , true );
510+ const double effectValue = spellEffectValue ( spellOnFriend, *unit, enemies, false , true );
569511 if ( spellOnFriend.isApplyToEnemies () ) {
570512 unitValue += effectValue;
571513 }
@@ -587,7 +529,7 @@ AI::SpellcastOutcome AI::BattlePlanner::spellDispelValue( const Spell & spell, c
587529 double unitValue = 0 ;
588530 const std::vector<Spell> & spellList = unit->getCurrentSpellEffects ();
589531 for ( const Spell & spellOnEnemy : spellList ) {
590- const double effectValue = spellEffectValue ( spellOnEnemy, *unit, enemyLastUnit, true );
532+ const double effectValue = spellEffectValue ( spellOnEnemy, *unit, enemies, enemyLastUnit, true );
591533 unitValue += spellOnEnemy.isApplyToFriends () ? effectValue : -effectValue;
592534 }
593535
@@ -716,7 +658,7 @@ AI::SpellcastOutcome AI::BattlePlanner::spellDragonSlayerValue( const Spell & sp
716658 SpellcastOutcome bestOutcome;
717659
718660 for ( const Battle::Unit * unit : friendly ) {
719- if ( isSpellcastUselessForUnit ( *unit, spell ) ) {
661+ if ( isSpellcastUselessForUnit ( *unit, enemies, spell ) ) {
720662 continue ;
721663 }
722664
@@ -727,6 +669,89 @@ AI::SpellcastOutcome AI::BattlePlanner::spellDragonSlayerValue( const Spell & sp
727669 return bestOutcome;
728670}
729671
672+ bool AI::BattlePlanner::isSpellcastUselessForUnit ( const Battle::Unit & unit, const Battle::Units & enemies, const Spell & spell ) const
673+ {
674+ const int spellID = spell.GetID ();
675+
676+ if ( unit.isImmovable () && spellID != Spell::ANTIMAGIC ) {
677+ return true ;
678+ }
679+
680+ switch ( spellID ) {
681+ case Spell::BLESS :
682+ case Spell::MASSBLESS :
683+ return unit.Modes ( Battle::SP_BLESS );
684+
685+ case Spell::BLOODLUST :
686+ return unit.Modes ( Battle::SP_BLOODLUST );
687+
688+ case Spell::CURSE :
689+ case Spell::MASSCURSE :
690+ return unit.Modes ( Battle::SP_CURSE );
691+
692+ case Spell::HASTE :
693+ case Spell::MASSHASTE :
694+ return unit.Modes ( Battle::SP_HASTE );
695+
696+ case Spell::SHIELD :
697+ case Spell::MASSSHIELD :
698+ // If a spell duration is just 1 round and all shooters already completed their turn
699+ // then this spell is useless.
700+ if ( getSpellPower ( _commander ) == 1 ) {
701+ size_t activeShooters{ 0 };
702+
703+ for ( const auto * enemy : enemies ) {
704+ if ( enemy->isArchers () && !enemy->isImmovable () && !enemy->Modes ( Battle::TR_MOVED ) ) {
705+ ++activeShooters;
706+ }
707+ }
708+
709+ if ( activeShooters == 0 ) {
710+ // No shooters are going to make their move.
711+ return true ;
712+ }
713+ }
714+
715+ return unit.Modes ( Battle::SP_SHIELD );
716+
717+ case Spell::SLOW :
718+ case Spell::MASSSLOW :
719+ return unit.Modes ( Battle::SP_SLOW );
720+
721+ case Spell::STONESKIN :
722+ case Spell::STEELSKIN :
723+ return unit.Modes ( Battle::SP_STONESKIN | Battle::SP_STEELSKIN );
724+
725+ case Spell::BLIND :
726+ case Spell::PARALYZE :
727+ case Spell::PETRIFY :
728+ return unit.Modes ( Battle::SP_BLIND | Battle::SP_PARALYZE | Battle::SP_STONE );
729+
730+ case Spell::DRAGONSLAYER :
731+ return unit.Modes ( Battle::SP_DRAGONSLAYER );
732+
733+ case Spell::ANTIMAGIC :
734+ return unit.Modes ( Battle::SP_ANTIMAGIC );
735+
736+ case Spell::BERSERKER :
737+ return unit.Modes ( Battle::SP_BERSERKER );
738+
739+ case Spell::HYPNOTIZE :
740+ return unit.Modes ( Battle::SP_HYPNOTIZE );
741+
742+ case Spell::MIRRORIMAGE :
743+ return unit.Modes ( Battle::CAP_MIRROROWNER );
744+
745+ case Spell::DISRUPTINGRAY :
746+ return unit.GetDefense () <= 1 ;
747+
748+ default :
749+ break ;
750+ }
751+
752+ return false ;
753+ }
754+
730755AI ::SpellcastOutcome AI::BattlePlanner::spellTeleportValue ( Battle::Arena & arena, const Spell & spell, const Battle::Unit & currentUnit,
731756 const Battle::Units & enemies ) const
732757{
@@ -739,7 +764,7 @@ AI::SpellcastOutcome AI::BattlePlanner::spellTeleportValue( Battle::Arena & aren
739764 return {};
740765 }
741766
742- if ( isSpellcastUselessForUnit ( currentUnit, spell ) ) {
767+ if ( isSpellcastUselessForUnit ( currentUnit, enemies, spell ) ) {
743768 return {};
744769 }
745770
0 commit comments