diff --git a/src/fheroes2/ai/ai_battle.cpp b/src/fheroes2/ai/ai_battle.cpp index 4caba024a90..4cd392de131 100644 --- a/src/fheroes2/ai/ai_battle.cpp +++ b/src/fheroes2/ai/ai_battle.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include #include +#include "army_troop.h" #include "artifact.h" #include "artifact_info.h" #include "battle.h" @@ -68,6 +70,8 @@ #include "spell_info.h" #include "spell_storage.h" +class Army; + namespace { const std::vector cellsUnderWallsIndexes = { 7, 28, 49, 72, 95 }; @@ -733,6 +737,50 @@ bool AI::BattlePlanner::isLimitOfTurnsExceeded( const Battle::Arena & arena, Bat return false; } +std::optional findNextUnit( const Battle::Arena & arena, const Battle::Unit & currentUnit ); +bool isPostponeRetreat( const Battle::Arena & arena, const Battle::Unit & currentUnit ); + +std::optional findNextUnit( const Battle::Arena & arena, const Battle::Unit & currentUnit ) +{ + const std::shared_ptr order = arena.getOrderOfUnits(); + const Battle::Units * units = order.get(); + if ( units == nullptr ) { + return std::nullopt; + } + + size_t i = 0; + const size_t count = units->size(); + for ( ; i < count; i++ ) { + const Battle::Unit * unit = units->at( i ); + if ( *unit == currentUnit ) { + break; + } + } + i++; + if ( i == count ) { + i = 0; + } + + const Battle::Unit * next_unit = units->at( i ); + assert( next_unit != nullptr ); + return { next_unit }; +} + +bool isPostponeRetreat( const Battle::Arena & arena, const Battle::Unit & currentUnit ) +{ + const std::optional maybeNextUnit = findNextUnit( arena, currentUnit ); + if ( !maybeNextUnit.has_value() ) { + return false; + } + const Battle::Unit * nextUnit = maybeNextUnit.value(); + assert( nextUnit != nullptr ); + + const Army * currentArmy = currentUnit.GetArmy(); + const Army * nextArmy = nextUnit->GetArmy(); + + const bool isSameArmy = ( currentArmy != nullptr ) && ( currentArmy == nextArmy ); + return isSameArmy && ( nextUnit->GetMorale() >= 0 ); +} Battle::Actions AI::BattlePlanner::planUnitTurn( Battle::Arena & arena, const Battle::Unit & currentUnit ) { @@ -757,7 +805,7 @@ Battle::Actions AI::BattlePlanner::planUnitTurn( Battle::Arena & arena, const Ba Surrender }; - const Outcome outcome = [this, &arena, actualHero]() { + const Outcome outcome = [this, &arena, actualHero, ¤tUnit]() { if ( !_considerRetreat ) { return Outcome::ContinueBattle; } @@ -852,7 +900,7 @@ Battle::Actions AI::BattlePlanner::planUnitTurn( Battle::Arena & arena, const Ba // If the hero has valuable artifacts, he should retreat so that these artifacts do not end up at the disposal of the enemy, especially in the case of an // alliance war - if ( hasValuableArtifacts ) { + if ( hasValuableArtifacts && !isPostponeRetreat( arena, currentUnit ) ) { return Outcome::Retreat; } @@ -862,7 +910,7 @@ Battle::Actions AI::BattlePlanner::planUnitTurn( Battle::Arena & arena, const Ba } // Otherwise, if this hero is relatively experienced, then he should retreat so that he can be hired again later - if ( actualHero->getTotalPrimarySkillLevel() >= minHeroTotalPrimarySkillLevelForRetreat ) { + if ( ( actualHero->getTotalPrimarySkillLevel() >= minHeroTotalPrimarySkillLevelForRetreat ) && !( isPostponeRetreat( arena, currentUnit ) ) ) { return Outcome::Retreat; } diff --git a/src/fheroes2/battle/battle_arena.h b/src/fheroes2/battle/battle_arena.h index 3fbcf2a8e94..c0929250189 100644 --- a/src/fheroes2/battle/battle_arena.h +++ b/src/fheroes2/battle/battle_arena.h @@ -1,6 +1,6 @@ /*************************************************************************** * fheroes2: https://github.com/ihhub/fheroes2 * - * Copyright (C) 2019 - 2025 * + * Copyright (C) 2019 - 2026 * * * * Free Heroes2 Engine: http://sourceforge.net/projects/fheroes2 * * Copyright (C) 2010 by Andrey Afletdinov * @@ -152,6 +152,11 @@ namespace Battle const Unit * GetTroopBoard( int32_t ) const; Unit * GetTroopUID( uint32_t ); + std::shared_ptr getOrderOfUnits() const + { + return _orderOfUnits; + } + const Unit * GetTroopUID( uint32_t ) const; const SpellStorage & GetUsedSpells() const;