diff --git a/src/fheroes2/castle/castle.cpp b/src/fheroes2/castle/castle.cpp index 431ac749182..f8f3cbfda9d 100644 --- a/src/fheroes2/castle/castle.cpp +++ b/src/fheroes2/castle/castle.cpp @@ -2414,15 +2414,12 @@ void Castle::ActionAfterBattle( bool attacker_wins ) Castle * VecCastles::GetFirstCastle() const { - const_iterator it = std::find_if( begin(), end(), []( const Castle * castle ) { return castle->isCastle(); } ); - return end() != it ? *it : nullptr; -} + const_iterator iter = std::find_if( begin(), end(), []( const Castle * castle ) { return castle->isCastle(); } ); + if ( iter == end() ) { + return nullptr; + } -void VecCastles::ChangeColors( int col1, int col2 ) -{ - for ( iterator it = begin(); it != end(); ++it ) - if ( ( *it )->GetColor() == col1 ) - ( *it )->ChangeColor( col2 ); + return *iter; } AllCastles::AllCastles() diff --git a/src/fheroes2/castle/castle.h b/src/fheroes2/castle/castle.h index 38e94737af7..c163b0c64d7 100644 --- a/src/fheroes2/castle/castle.h +++ b/src/fheroes2/castle/castle.h @@ -413,9 +413,14 @@ namespace CastleDialog struct VecCastles : public std::vector { - Castle * GetFirstCastle() const; + VecCastles() = default; + VecCastles( const VecCastles & ) = delete; + + ~VecCastles() = default; - void ChangeColors( int, int ); + VecCastles & operator=( const VecCastles & ) = delete; + + Castle * GetFirstCastle() const; }; class AllCastles diff --git a/src/fheroes2/game/difficulty.cpp b/src/fheroes2/game/difficulty.cpp index f3f3e06a504..ca56678ec4e 100644 --- a/src/fheroes2/game/difficulty.cpp +++ b/src/fheroes2/game/difficulty.cpp @@ -26,9 +26,11 @@ #include #include +#include "kingdom.h" #include "profit.h" #include "race.h" #include "translations.h" +#include "world.h" std::string Difficulty::String( int difficulty ) { @@ -68,8 +70,10 @@ int Difficulty::GetScoutingBonusForAI( int difficulty ) return 0; } -Funds Difficulty::getResourceIncomeBonusForAI( const int difficulty, const VecCastles & castles ) +Funds Difficulty::getResourceIncomeBonusForAI( const int difficulty, const Kingdom & kingdom ) { + assert( kingdom.isControlAI() ); + const auto getIncomeFromSetsOfResourceMines = []( const int resourceTypes, const uint32_t numOfSets ) { Funds result; @@ -78,16 +82,28 @@ Funds Difficulty::getResourceIncomeBonusForAI( const int difficulty, const VecCa return result * numOfSets; }; - const auto getBonusForCastles = [&castles]() { + const auto getBonusForCastles = [kingdomColor = kingdom.GetColor(), &kingdomCastles = kingdom.GetCastles()]() { Funds result; - for ( const Castle * castle : castles ) { + const bool kingdomHasMarketplace = std::any_of( kingdomCastles.begin(), kingdomCastles.end(), []( const Castle * castle ) { + assert( castle != nullptr ); + + return castle->isBuild( BUILD_MARKETPLACE ); + } ); + + // Additional rare resources for hiring units from higher-level dwellings can only be provided if the kingdom already has some source of those resources - either + // through trade or through mining + const auto canKingdomGetResource = [kingdomColor, kingdomHasMarketplace]( const int resourceType ) { + return ( kingdomHasMarketplace || world.CountCapturedMines( resourceType, kingdomColor ) > 0 ); + }; + + for ( const Castle * castle : kingdomCastles ) { assert( castle != nullptr ); // AI at higher difficulty levels should be able to fully redeem the weekly unit growth in its castles result += ProfitConditions::FromMine( Resource::GOLD ); - // Offer additional resources only if there are higher-level dwellings in the castle to avoid distortions in the castle's development rate + // Provide additional resources only if there are higher-level dwellings in the castle to avoid distortions in the castle's development rate if ( !castle->isBuild( DWELLING_MONSTER6 ) ) { continue; } @@ -98,18 +114,26 @@ Funds Difficulty::getResourceIncomeBonusForAI( const int difficulty, const VecCa // Rare resources are not required to hire maximum-level units in these castles break; case Race::BARB: - result += ProfitConditions::FromMine( Resource::CRYSTAL ); + if ( canKingdomGetResource( Resource::CRYSTAL ) ) { + result += ProfitConditions::FromMine( Resource::CRYSTAL ); + } break; case Race::SORC: - result += ProfitConditions::FromMine( Resource::MERCURY ); + if ( canKingdomGetResource( Resource::MERCURY ) ) { + result += ProfitConditions::FromMine( Resource::MERCURY ); + } break; case Race::WRLK: - result += ProfitConditions::FromMine( Resource::SULFUR ); + if ( canKingdomGetResource( Resource::SULFUR ) ) { + result += ProfitConditions::FromMine( Resource::SULFUR ); + } // The maximum level units in this castle are more expensive than in others result += ProfitConditions::FromMine( Resource::GOLD ); break; case Race::WZRD: - result += ProfitConditions::FromMine( Resource::GEMS ); + if ( canKingdomGetResource( Resource::GEMS ) ) { + result += ProfitConditions::FromMine( Resource::GEMS ); + } // The maximum level units in this castle are more expensive than in others result += ProfitConditions::FromMine( Resource::GOLD ); break; diff --git a/src/fheroes2/game/difficulty.h b/src/fheroes2/game/difficulty.h index 3704c2b0974..66043766054 100644 --- a/src/fheroes2/game/difficulty.h +++ b/src/fheroes2/game/difficulty.h @@ -47,7 +47,7 @@ namespace Difficulty int GetScoutingBonusForAI( int difficulty ); // Returns an extra resource bonus for AI based on difficulty level. - Funds getResourceIncomeBonusForAI( const int difficulty, const VecCastles & castles ); + Funds getResourceIncomeBonusForAI( const int difficulty, const Kingdom & kingdom ); // Returns an extra gold bonus modifier for AI based on difficulty level. This modifier is applied after applying the resource income bonus. double getGoldIncomeBonusForAI( const int difficulty ); diff --git a/src/fheroes2/kingdom/kingdom.cpp b/src/fheroes2/kingdom/kingdom.cpp index c7d2e000242..27f25f88611 100644 --- a/src/fheroes2/kingdom/kingdom.cpp +++ b/src/fheroes2/kingdom/kingdom.cpp @@ -178,25 +178,30 @@ bool Kingdom::isPlay() const void Kingdom::LossPostActions() { - if ( isPlay() ) { - Players::SetPlayerInGame( color, false ); + if ( !isPlay() ) { + return; + } - // Heroes::Dismiss() calls Kingdom::RemoveHero(), which eventually calls heroes.erase() - while ( !heroes.empty() ) { - Heroes * hero = heroes.back(); + Players::SetPlayerInGame( color, false ); - assert( hero->GetColor() == GetColor() ); + // Heroes::Dismiss() calls Kingdom::RemoveHero(), which eventually calls heroes.erase() + while ( !heroes.empty() ) { + Heroes * hero = heroes.back(); - hero->Dismiss( static_cast( Battle::RESULT_LOSS ) ); - } + assert( hero->GetColor() == GetColor() ); - if ( !castles.empty() ) { - castles.ChangeColors( GetColor(), Color::NONE ); - castles.clear(); - } + hero->Dismiss( static_cast( Battle::RESULT_LOSS ) ); + } + + for ( Castle * castle : castles ) { + assert( castle != nullptr && castle->GetColor() == GetColor() ); - world.ResetCapturedObjects( GetColor() ); + castle->ChangeColor( Color::NONE ); } + + castles.clear(); + + world.ResetCapturedObjects( GetColor() ); } void Kingdom::ActionBeforeTurn() @@ -598,20 +603,22 @@ bool Kingdom::AllowRecruitHero( bool check_payment ) const void Kingdom::ApplyPlayWithStartingHero() { - if ( !isPlay() || castles.empty() ) + if ( !isPlay() || castles.empty() ) { return; + } bool foundHeroes = false; for ( const Castle * castle : castles ) { - if ( castle == nullptr ) + if ( castle == nullptr ) { continue; + } - // check manual set hero (castle position + point(0, 1))? + // Check if there is a hero placed by the map creator near the castle entrance (castle position + point(0, 1)) const fheroes2::Point & cp = castle->GetCenter(); Heroes * hero = world.GetTiles( cp.x, cp.y + 1 ).getHero(); - // and move manual set hero to castle + // If there is, move it to the castle if ( hero && hero->GetColor() == GetColor() ) { const bool patrol = hero->Modes( Heroes::PATROL ); if ( hero->isValid() ) { @@ -626,19 +633,21 @@ void Kingdom::ApplyPlayWithStartingHero() hero->SetModes( Heroes::PATROL ); hero->SetPatrolCenter( cp ); } + foundHeroes = true; } } if ( !foundHeroes && Settings::Get().getCurrentMapInfo().startWithHeroInEachCastle ) { - // get first castle const Castle * first = castles.GetFirstCastle(); - if ( nullptr == first ) + if ( first == nullptr ) { first = castles.front(); + } Heroes * hero = world.GetHeroForHire( first->GetRace() ); - if ( hero && AllowRecruitHero( false ) ) + if ( hero && AllowRecruitHero( false ) ) { hero->Recruit( *first ); + } } } @@ -707,7 +716,7 @@ Funds Kingdom::GetIncome( int type /* = INCOME_ALL */ ) const } if ( isControlAI() ) { - const Funds incomeBonus = Difficulty::getResourceIncomeBonusForAI( Game::getDifficulty(), GetCastles() ); + const Funds incomeBonus = Difficulty::getResourceIncomeBonusForAI( Game::getDifficulty(), *this ); if ( incomeBonus.GetValidItemsCount() != 0 ) { DEBUG_LOG( DBG_AI, DBG_TRACE, "AI bonus to the resource income has been applied to " << Color::String( color ) << ": " << incomeBonus.String() );