Skip to content

Commit

Permalink
AI should keep some creatures in reserve for castle defense (ihhub#5571)
Browse files Browse the repository at this point in the history
  • Loading branch information
idshibanov authored Jun 28, 2022
1 parent 60bc59a commit 6b9da53
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 25 deletions.
1 change: 0 additions & 1 deletion src/fheroes2/ai/ai.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ namespace AI
bool BuildIfAvailable( Castle & castle, int building );
bool BuildIfEnoughResources( Castle & castle, int building, uint32_t minimumMultiplicator );
uint32_t GetResourceMultiplier( uint32_t min, uint32_t max );
void ReinforceHeroInCastle( Heroes & hero, Castle & castle, const Funds & budget );
void OptimizeTroopsOrder( Army & hero );

StreamBase & operator<<( StreamBase &, const AI::Base & );
Expand Down
20 changes: 0 additions & 20 deletions src/fheroes2/ai/ai_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,26 +57,6 @@ namespace AI
return Rand::Get( min, max );
}

void ReinforceHeroInCastle( Heroes & hero, Castle & castle, const Funds & budget )
{
if ( !hero.HaveSpellBook() && castle.GetLevelMageGuild() > 0 && !hero.IsFullBagArtifacts() ) {
// this call will check if AI kingdom have enough resources to buy book
hero.BuySpellBook( &castle );
}

Army & heroArmy = hero.GetArmy();
const double armyStrength = heroArmy.GetStrength();

heroArmy.UpgradeTroops( castle );
castle.recruitBestAvailable( budget );
heroArmy.JoinStrongestFromArmy( castle.GetArmy() );
OptimizeTroopsOrder( heroArmy );

if ( std::fabs( armyStrength - heroArmy.GetStrength() ) > 0.001 ) {
hero.unmarkHeroMeeting();
}
}

void OptimizeTroopsOrder( Army & army )
{
// Optimize troops placement before the battle
Expand Down
1 change: 1 addition & 0 deletions src/fheroes2/ai/normal/ai_normal.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ namespace AI
void HeroesActionComplete( Heroes & hero, int32_t tileIndex, const MP2::MapObjectType objectType ) override;

bool recruitHero( Castle & castle, bool buyArmy, bool underThreat );
void reinforceHeroInCastle( Heroes & hero, Castle & castle, const Funds & budget );
void evaluateRegionSafety();
std::set<int> findCastlesInDanger( const KingdomCastles & castles, const std::vector<std::pair<int, const Army *>> & enemyArmies, int myColor );
std::vector<AICastle> getSortedCastleList( const KingdomCastles & castles, const std::set<int> & castlesInDanger );
Expand Down
2 changes: 1 addition & 1 deletion src/fheroes2/ai/normal/ai_normal_hero.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1357,7 +1357,7 @@ namespace AI
{
Castle * castle = hero.inCastleMutable();
if ( castle ) {
ReinforceHeroInCastle( hero, *castle, castle->GetKingdom().GetFunds() );
reinforceHeroInCastle( hero, *castle, castle->GetKingdom().GetFunds() );
}

if ( isMonsterStrengthCacheable( objectType ) ) {
Expand Down
66 changes: 65 additions & 1 deletion src/fheroes2/ai/normal/ai_normal_kingdom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,76 @@ namespace AI

if ( recruit && buyArmy ) {
CastleTurn( castle, underThreat );
ReinforceHeroInCastle( *recruit, castle, kingdom.GetFunds() );
reinforceHeroInCastle( *recruit, castle, kingdom.GetFunds() );
}

return recruit != nullptr;
}

void Normal::reinforceHeroInCastle( Heroes & hero, Castle & castle, const Funds & budget )
{
if ( !hero.HaveSpellBook() && castle.GetLevelMageGuild() > 0 && !hero.IsFullBagArtifacts() ) {
// this call will check if AI kingdom have enough resources to buy book
hero.BuySpellBook( &castle );
}

Army & heroArmy = hero.GetArmy();
Army & garrison = castle.GetArmy();
const double armyStrength = heroArmy.GetStrength();

heroArmy.UpgradeTroops( castle );
castle.recruitBestAvailable( budget );
heroArmy.JoinStrongestFromArmy( garrison );

const uint32_t regionID = world.GetTiles( castle.GetIndex() ).GetRegion();
// check if we should leave some troops in the garrison
// TODO: amount of troops left could depend on region's safetyFactor
if ( castle.isCastle() && _regions[regionID].safetyFactor <= 100 && !garrison.isValid() ) {
const Heroes::Role heroRole = hero.getAIRole();
const bool isFigtherHero = ( heroRole == Heroes::Role::FIGHTER || heroRole == Heroes::Role::CHAMPION );

bool onlyHalf = false;
Troop * unitToSwap = heroArmy.GetSlowestTroop();
if ( unitToSwap ) {
const double significanceRatio = isFigtherHero ? 20.0 : 10.0;
if ( unitToSwap->GetStrength() > armyStrength / significanceRatio ) {
Troop * weakest = heroArmy.GetWeakestTroop();

assert( weakest != nullptr );
if ( weakest ) {
unitToSwap = weakest;
if ( weakest->GetStrength() > armyStrength / significanceRatio ) {
if ( isFigtherHero ) {
// if it's an important hero and all troops are significant - keep the army
unitToSwap = nullptr;
}
else {
onlyHalf = true;
}
}
}
}
}
if ( unitToSwap ) {
const uint32_t count = unitToSwap->GetCount();
const uint32_t toMove = onlyHalf ? count / 2 : count;
if ( garrison.JoinTroop( unitToSwap->GetMonster(), toMove, true ) ) {
if ( !onlyHalf ) {
unitToSwap->Reset();
}
else {
unitToSwap->SetCount( count - toMove );
}
}
}
}

OptimizeTroopsOrder( heroArmy );
if ( std::fabs( armyStrength - heroArmy.GetStrength() ) > 0.001 ) {
hero.unmarkHeroMeeting();
}
}

void Normal::evaluateRegionSafety()
{
std::vector<std::pair<size_t, int>> regionsToCheck;
Expand Down
2 changes: 1 addition & 1 deletion src/fheroes2/army/army.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ Troop * Troops::GetWeakestTroop() const
return *lowest;
}

const Troop * Troops::GetSlowestTroop() const
Troop * Troops::GetSlowestTroop() const
{
const_iterator first = begin();
const_iterator last = end();
Expand Down
2 changes: 1 addition & 1 deletion src/fheroes2/army/army.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class Troops : protected std::vector<Troop *>

Troop * GetFirstValid();
Troop * GetWeakestTroop() const;
const Troop * GetSlowestTroop() const;
Troop * GetSlowestTroop() const;

void SortStrongest();
void ArrangeForBattle( bool = false );
Expand Down

0 comments on commit 6b9da53

Please sign in to comment.