Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions src/fheroes2/ai/ai_hero_action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,34 @@ namespace
// Shuffle spells as all of them seem to be equal in power.
Rand::Shuffle( spells );

const int32_t spellMultiplier = Difficulty::getGuardianSpellMultiplier( Game::getDifficulty() );
int32_t spellMultiplier = Difficulty::getGuardianSpellMultiplier( Game::getDifficulty() );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a double value to smooth out the curve. With integer increments of 15 spell points the only situation where it will be cast is impossible difficulty scout or maybe a hunter. Unless boosted by a map maker AI heroes are often underdeveloped.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the double multiplier we can bump Scout role divisor to 5:

Current values in this PR (Scout-Hunter-Champion):
Normal: 45 - 105 - 210
Hard: 45 - 90 - 180
Impossible: 30 - 60 - 120

Normal: 42 - 105 - 210
Hard: 36 - 90 - 180
Impossible: 24 - 60 - 120


// Adjust spell multiplier based on hero's AI role.
switch ( hero.getAIRole() ) {
case Heroes::Role::SCOUT:
// A hero with almost no army risking his life and most likely going to be killed.
spellMultiplier = std::min( 1, spellMultiplier / 4 );
break;
case Heroes::Role::COURIER:
// A hero who usually delivers army. Might have some army to battle with but he is not a fighter and can be easily defeated.
spellMultiplier = std::min( 1, spellMultiplier / 3 );
break;
case Heroes::Role::HUNTER:
// A normal hero with normal stuff to do. Not so strong and not so weak.
spellMultiplier = std::min( 1, spellMultiplier / 2 );
break;
default:
// The rest of heroes are fighters and should keep their spell points for battles.
break;
}

for ( const Spell spell : spells ) {
if ( hero.CanCastSpell( spell ) && hero.GetSpellPoints() > spellMultiplier * spell.spellPoints( &hero ) ) {
if ( hero.CanCastSpell( spell ) && hero.GetSpellPoints() >= spellMultiplier * spell.spellPoints( &hero ) ) {
// Looks like this hero knows the spell and casting it won't take too many spell points.
// So, let's do it!
hero.ActionSpellCast( spell );
return;
if ( hero.ActionSpellCast( spell ) ) {
return;
}
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/fheroes2/game/difficulty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,15 +248,15 @@ int32_t Difficulty::getGuardianSpellMultiplier( const int difficulty )
{
switch ( difficulty ) {
case Difficulty::EASY:
return 20;
return 16;
case Difficulty::NORMAL:
return 18;
case Difficulty::HARD:
return 14;
case Difficulty::EXPERT:
case Difficulty::HARD:
return 12;
case Difficulty::IMPOSSIBLE:
case Difficulty::EXPERT:
return 10;
case Difficulty::IMPOSSIBLE:
return 8;
default:
// Did you add a new difficulty level? Add the logic above!
assert( 0 );
Expand Down
4 changes: 3 additions & 1 deletion src/fheroes2/heroes/heroes.h
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,9 @@ class Heroes final : public HeroBase, public ColorBase

void Action( const int tileIndex );
void ActionNewPosition( const bool allowMonsterAttack );
void ActionSpellCast( const Spell & spell );

// Returns true if the spell is casted successfully.
bool ActionSpellCast( const Spell & spell );

// Update map in the scout area around the Hero on radar (mini-map).
void ScoutRadar() const;
Expand Down
2 changes: 1 addition & 1 deletion src/fheroes2/heroes/heroes_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ bool HeroBase::CanCastSpell( const Spell & spell, std::string * res /* = nullptr
return false;
}
if ( spell != Spell::HAUNT ) {
const uint32_t newCount = fheroes2::getGuardianMonsterCount( spell, hero->GetPower(), hero );
const uint32_t newCount = fheroes2::getGuardianMonsterCount( spell, hero->GetPower() );
const uint32_t currentCount = troop.GetCount();
if ( newCount <= currentCount ) {
if ( res != nullptr ) {
Expand Down
9 changes: 5 additions & 4 deletions src/fheroes2/heroes/heroes_spell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,15 +326,15 @@ namespace
Maps::Tile & tile = world.getTile( hero.GetIndex() );
assert( MP2::OBJ_MINE == tile.getMainObjectType( false ) );

const uint32_t count = fheroes2::getGuardianMonsterCount( spell, hero.GetPower(), &hero );
const uint32_t count = fheroes2::getGuardianMonsterCount( spell, hero.GetPower() );

if ( count == 0 ) {
return false;
}

Maps::setMineSpellOnTile( tile, spell.GetID() );

if ( spell == Spell::HAUNT ) {
if ( spell == Spell::HAUNT && hero.isControlHuman() ) {
world.CaptureObject( tile.GetIndex(), PlayerColor::NONE );

// Update the color of haunted mine on radar.
Expand All @@ -351,7 +351,7 @@ namespace
}
}

void Heroes::ActionSpellCast( const Spell & spell )
bool Heroes::ActionSpellCast( const Spell & spell )
{
assert( spell.isValid() && !spell.isCombat() && CanCastSpell( spell ) );

Expand Down Expand Up @@ -406,10 +406,11 @@ void Heroes::ActionSpellCast( const Spell & spell )
}

if ( !apply ) {
return;
return false;
}

DEBUG_LOG( DBG_GAME, DBG_INFO, GetName() << " cast spell: " << spell.GetName() )

SpellCasted( spell );
return true;
}
6 changes: 2 additions & 4 deletions src/fheroes2/spell/spell_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,8 @@ namespace fheroes2
return resurrectionPoints;
}

uint32_t getGuardianMonsterCount( const Spell & spell, const uint32_t spellPower, const HeroBase * hero )
uint32_t getGuardianMonsterCount( const Spell & spell, const uint32_t spellPower )
{
(void)hero;

assert( spellPower > 0 );

return spell.ExtraValue() * spellPower;
Expand Down Expand Up @@ -272,7 +270,7 @@ namespace fheroes2
return spell.GetDescription();
}

const uint32_t guardianCount = getGuardianMonsterCount( spell, hero->GetPower(), hero );
const uint32_t guardianCount = getGuardianMonsterCount( spell, hero->GetPower() );

description += "\n\n";
description += _( "This spell summons %{count} %{monster} to guard the mine." );
Expand Down
2 changes: 1 addition & 1 deletion src/fheroes2/spell/spell_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ namespace fheroes2

uint32_t getResurrectPoints( const Spell & spell, const uint32_t spellPower, const HeroBase * hero );

uint32_t getGuardianMonsterCount( const Spell & spell, const uint32_t spellPower, const HeroBase * hero );
uint32_t getGuardianMonsterCount( const Spell & spell, const uint32_t spellPower );

uint32_t getHypnotizeMonsterHPPoints( const Spell & spell, const uint32_t spellPower, const HeroBase * hero );

Expand Down