Skip to content

Commit

Permalink
Tune the bonuses, update the logic a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
oleg-derevenetz committed Jun 1, 2024
1 parent 0633eab commit 0e85e39
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 40 deletions.
13 changes: 5 additions & 8 deletions src/fheroes2/castle/castle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
9 changes: 7 additions & 2 deletions src/fheroes2/castle/castle.h
Original file line number Diff line number Diff line change
Expand Up @@ -413,9 +413,14 @@ namespace CastleDialog

struct VecCastles : public std::vector<Castle *>
{
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
Expand Down
40 changes: 32 additions & 8 deletions src/fheroes2/game/difficulty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
#include <cassert>
#include <vector>

#include "kingdom.h"
#include "profit.h"
#include "race.h"
#include "translations.h"
#include "world.h"

std::string Difficulty::String( int difficulty )
{
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/fheroes2/game/difficulty.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
51 changes: 30 additions & 21 deletions src/fheroes2/kingdom/kingdom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>( Battle::RESULT_LOSS ) );
}
assert( hero->GetColor() == GetColor() );

if ( !castles.empty() ) {
castles.ChangeColors( GetColor(), Color::NONE );
castles.clear();
}
hero->Dismiss( static_cast<int>( 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()
Expand Down Expand Up @@ -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() ) {
Expand All @@ -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 );
}
}
}

Expand Down Expand Up @@ -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() );

Expand Down

0 comments on commit 0e85e39

Please sign in to comment.