Skip to content

Commit

Permalink
Add dialog to choose between Auto Combat and Quick Combat (#9180)
Browse files Browse the repository at this point in the history
  • Loading branch information
zenseii authored Jan 18, 2025
1 parent fa79da9 commit 2d4b7df
Show file tree
Hide file tree
Showing 15 changed files with 265 additions and 100 deletions.
34 changes: 32 additions & 2 deletions src/fheroes2/agg/agg_image.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2021 - 2024 *
* Copyright (C) 2021 - 2025 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
Expand Down Expand Up @@ -188,7 +188,11 @@ namespace
ICN::BUTTON_EVENTS_GOOD,
ICN::BUTTON_EVENTS_EVIL,
ICN::BUTTON_LANGUAGE_GOOD,
ICN::BUTTON_LANGUAGE_EVIL };
ICN::BUTTON_LANGUAGE_EVIL,
ICN::BUTTON_AUTO_COMBAT_GOOD,
ICN::BUTTON_AUTO_COMBAT_EVIL,
ICN::BUTTON_QUICK_COMBAT_GOOD,
ICN::BUTTON_QUICK_COMBAT_EVIL };

#ifndef NDEBUG
bool isLanguageDependentIcnId( const int id )
Expand Down Expand Up @@ -2127,6 +2131,28 @@ namespace

break;
}
case ICN::BUTTON_AUTO_COMBAT_GOOD:
case ICN::BUTTON_AUTO_COMBAT_EVIL: {
_icnVsSprite[id].resize( 2 );

const bool isEvilInterface = ( id == ICN::BUTTON_AUTO_COMBAT_EVIL );

getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "AUTO\nCOMBAT" ),
isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK );

break;
}
case ICN::BUTTON_QUICK_COMBAT_GOOD:
case ICN::BUTTON_QUICK_COMBAT_EVIL: {
_icnVsSprite[id].resize( 2 );

const bool isEvilInterface = ( id == ICN::BUTTON_QUICK_COMBAT_EVIL );

getTextAdaptedButton( _icnVsSprite[id][0], _icnVsSprite[id][1], gettext_noop( "QUICK\nCOMBAT" ),
isEvilInterface ? ICN::EMPTY_EVIL_BUTTON : ICN::EMPTY_GOOD_BUTTON, isEvilInterface ? ICN::STONEBAK_EVIL : ICN::STONEBAK );

break;
}
default:
// You're calling this function for non-specified ICN id. Check your logic!
// Did you add a new image for one language without generating a default
Expand Down Expand Up @@ -2839,6 +2865,10 @@ namespace
case ICN::BUTTON_EVENTS_EVIL:
case ICN::BUTTON_LANGUAGE_GOOD:
case ICN::BUTTON_LANGUAGE_EVIL:
case ICN::BUTTON_AUTO_COMBAT_GOOD:
case ICN::BUTTON_AUTO_COMBAT_EVIL:
case ICN::BUTTON_QUICK_COMBAT_GOOD:
case ICN::BUTTON_QUICK_COMBAT_EVIL:
generateLanguageSpecificImages( id );
return true;
case ICN::PHOENIX:
Expand Down
5 changes: 5 additions & 0 deletions src/fheroes2/agg/icn.h
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,11 @@ namespace ICN
BUTTON_LANGUAGE_GOOD,
BUTTON_LANGUAGE_EVIL,

BUTTON_AUTO_COMBAT_GOOD,
BUTTON_AUTO_COMBAT_EVIL,
BUTTON_QUICK_COMBAT_GOOD,
BUTTON_QUICK_COMBAT_EVIL,

SCENIBKG_EVIL,

// IMPORTANT! Put any new entry just above this one.
Expand Down
14 changes: 7 additions & 7 deletions src/fheroes2/ai/ai_battle.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2024 *
* Copyright (C) 2024 - 2025 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
Expand Down Expand Up @@ -659,13 +659,13 @@ bool AI::BattlePlanner::isLimitOfTurnsExceeded( const Battle::Arena & arena, Bat

// We have gone beyond the limit on the number of turns without deaths and have to stop
if ( _numberOfRemainingTurnsWithoutDeaths == 0 ) {
// If this is an auto battle (and not the instant battle, because the battle UI is present), then turn it off until the end of the battle
if ( arena.AutoBattleInProgress() && Battle::Arena::GetInterface() != nullptr ) {
assert( arena.CanToggleAutoBattle() );
// If this is an auto combat (and not a quick combat, because the battle UI is present), then turn it off until the end of the battle
if ( arena.AutoCombatInProgress() && Battle::Arena::GetInterface() != nullptr ) {
assert( arena.CanToggleAutoCombat() );

actions.emplace_back( Battle::Command::AUTO_SWITCH, currentColor );
actions.emplace_back( Battle::Command::TOGGLE_AUTO_COMBAT, currentColor );

DEBUG_LOG( DBG_BATTLE, DBG_INFO, Color::String( currentColor ) << " has used up the limit of turns without deaths, auto battle is turned off" )
DEBUG_LOG( DBG_BATTLE, DBG_INFO, Color::String( currentColor ) << " has used up the limit of turns without deaths, auto combat is turned off" )
}
// Otherwise the attacker's hero should retreat
else {
Expand Down Expand Up @@ -711,7 +711,7 @@ Battle::Actions AI::BattlePlanner::planUnitTurn( Battle::Arena & arena, const Ba
return Outcome::ContinueBattle;
}

// Human-controlled heroes should not retreat or surrender during auto/instant battles
// Human-controlled heroes should not retreat or surrender during auto/quick combat
if ( actualHero->isControlHuman() ) {
return Outcome::ContinueBattle;
}
Expand Down
4 changes: 2 additions & 2 deletions src/fheroes2/ai/ai_battle.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2024 *
* Copyright (C) 2024 - 2025 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
Expand Down Expand Up @@ -112,7 +112,7 @@ namespace AI
int32_t spellDurationMultiplier( const Battle::Unit & target ) const;

// When this limit of turns without deaths is exceeded for an attacking AI-controlled hero,
// the auto battle should be interrupted (one way or another)
// the auto combat should be interrupted (one way or another)
static const uint32_t MAX_TURNS_WITHOUT_DEATHS = 50;

// Member variables related to the logic of checking the limit of the number of turns
Expand Down
24 changes: 12 additions & 12 deletions src/fheroes2/battle/battle_action.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2019 - 2024 *
* Copyright (C) 2019 - 2025 *
* *
* Free Heroes2 Engine: http://sourceforge.net/projects/fheroes2 *
* Copyright (C) 2010 by Andrey Afletdinov <[email protected]> *
Expand Down Expand Up @@ -424,11 +424,11 @@ void Battle::Arena::ApplyAction( Command & cmd )
ApplyActionSurrender( cmd );
break;

case CommandType::AUTO_SWITCH:
ApplyActionAutoSwitch( cmd );
case CommandType::TOGGLE_AUTO_COMBAT:
ApplyActionToggleAutoCombat( cmd );
break;
case CommandType::AUTO_FINISH:
ApplyActionAutoFinish( cmd );
case CommandType::QUICK_COMBAT:
ApplyActionQuickCombat( cmd );
break;

default:
Expand Down Expand Up @@ -1325,7 +1325,7 @@ void Battle::Arena::ApplyActionCatapult( Command & cmd )
}
}

void Battle::Arena::ApplyActionAutoSwitch( Command & cmd )
void Battle::Arena::ApplyActionToggleAutoCombat( Command & cmd )
{
const auto checkParameters = []( const int color ) {
const Arena * arena = GetArena();
Expand Down Expand Up @@ -1355,22 +1355,22 @@ void Battle::Arena::ApplyActionAutoSwitch( Command & cmd )
return;
}

_autoBattleColors ^= color;
_autoCombatColors ^= color;

DEBUG_LOG( DBG_BATTLE, DBG_TRACE, "color: " << Color::String( color ) << ", status: " << ( ( _autoBattleColors & color ) ? "on" : "off" ) )
DEBUG_LOG( DBG_BATTLE, DBG_TRACE, "color: " << Color::String( color ) << ", status: " << ( ( _autoCombatColors & color ) ? "on" : "off" ) )

if ( _interface ) {
const Player * player = Players::Get( color );
assert( player );

std::string msg = ( _autoBattleColors & color ) ? _( "%{name} has turned on the auto battle" ) : _( "%{name} has turned off the auto battle" );
std::string msg = ( _autoCombatColors & color ) ? _( "%{name} has turned on the auto combat" ) : _( "%{name} has turned off the auto combat" );
StringReplace( msg, "%{name}", player->GetName() );

_interface->setStatus( msg, true );
}
}

void Battle::Arena::ApplyActionAutoFinish( const Command & /* cmd */ )
void Battle::Arena::ApplyActionQuickCombat( const Command & /* cmd */ )
{
const int army1Control = GetForce1().GetControl();
const int army2Control = GetForce2().GetControl();
Expand All @@ -1391,10 +1391,10 @@ void Battle::Arena::ApplyActionAutoFinish( const Command & /* cmd */ )
const int army2Color = GetArmy2Color();

if ( army1Control & CONTROL_HUMAN ) {
_autoBattleColors |= army1Color;
_autoCombatColors |= army1Color;
}
if ( army2Control & CONTROL_HUMAN ) {
_autoBattleColors |= army2Color;
_autoCombatColors |= army2Color;
}

_interface.reset();
Expand Down
27 changes: 13 additions & 14 deletions src/fheroes2/battle/battle_arena.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2019 - 2024 *
* Copyright (C) 2019 - 2025 *
* *
* Free Heroes2 Engine: http://sourceforge.net/projects/fheroes2 *
* Copyright (C) 2010 by Andrey Afletdinov <[email protected]> *
Expand Down Expand Up @@ -362,7 +362,6 @@ Battle::Arena::Arena( Army & army1, Army & army2, const int32_t tileIndex, const
castle = nullptr;
}

// init interface
if ( isShowInterface ) {
_interface = std::make_unique<Interface>( *this, tileIndex );
board.SetArea( _interface->GetArea() );
Expand All @@ -372,12 +371,12 @@ Battle::Arena::Arena( Army & army1, Army & army2, const int32_t tileIndex, const
_interface->SetOrderOfUnits( _orderOfUnits );
}
else {
// no interface - force auto battle mode for human player
// There is no interface - force the auto combat mode for the human player
if ( army1.isControlHuman() ) {
_autoBattleColors |= army1.GetColor();
_autoCombatColors |= army1.GetColor();
}
if ( army2.isControlHuman() ) {
_autoBattleColors |= army2.GetColor();
_autoCombatColors |= army2.GetColor();
}
}

Expand Down Expand Up @@ -484,8 +483,8 @@ void Battle::Arena::UnitTurn( const Units & orderHistory )
}

if ( !actions.empty() ) {
// Pending actions from the user interface (such as toggling auto battle) have "already occurred" and
// therefore should be handled first, before any other actions. Just skip the rest of the branches.
// Pending actions from the user interface (such as toggling the auto combat on/off) have "already occurred"
// and therefore should be handled first, before any other actions. Just skip the rest of the branches.
}
else if ( _currentUnit->GetSpeed() == Speed::STANDING ) {
// Unit has either finished its turn, is dead, or has become immovable due to some spell. Even if the
Expand All @@ -506,7 +505,7 @@ void Battle::Arena::UnitTurn( const Units & orderHistory )
_bridge->SetPassability( *_currentUnit );
}

if ( ( _currentUnit->GetCurrentControl() & CONTROL_AI ) || ( _currentUnit->GetCurrentColor() & _autoBattleColors ) ) {
if ( ( _currentUnit->GetCurrentControl() & CONTROL_AI ) || ( _currentUnit->GetCurrentColor() & _autoCombatColors ) ) {
AI::BattlePlanner::Get().BattleTurn( *this, *_currentUnit, actions );
}
else {
Expand Down Expand Up @@ -1432,14 +1431,14 @@ Battle::Result & Battle::Arena::GetResult()
return result_game;
}

bool Battle::Arena::AutoBattleInProgress() const
bool Battle::Arena::AutoCombatInProgress() const
{
if ( _currentUnit == nullptr ) {
return false;
}

if ( _autoBattleColors & GetCurrentColor() ) {
// Auto battle mode cannot be enabled for a player controlled by AI
if ( _autoCombatColors & GetCurrentColor() ) {
// Auto combat mode cannot be enabled for a player controlled by the AI
assert( !( GetCurrentForce().GetControl() & CONTROL_AI ) );

return true;
Expand All @@ -1448,7 +1447,7 @@ bool Battle::Arena::AutoBattleInProgress() const
return false;
}

bool Battle::Arena::EnemyOfAIHasAutoBattleInProgress() const
bool Battle::Arena::EnemyOfAIHasAutoCombatInProgress() const
{
if ( _currentUnit == nullptr ) {
return false;
Expand All @@ -1464,10 +1463,10 @@ bool Battle::Arena::EnemyOfAIHasAutoBattleInProgress() const
return false;
}

return ( _autoBattleColors & enemyForce.GetColor() );
return ( _autoCombatColors & enemyForce.GetColor() );
}

bool Battle::Arena::CanToggleAutoBattle() const
bool Battle::Arena::CanToggleAutoCombat() const
{
if ( _currentUnit == nullptr ) {
return false;
Expand Down
14 changes: 7 additions & 7 deletions src/fheroes2/battle/battle_arena.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ namespace Battle
void Turns();
bool BattleValid() const;

bool AutoBattleInProgress() const;
bool EnemyOfAIHasAutoBattleInProgress() const;
bool CanToggleAutoBattle() const;
bool AutoCombatInProgress() const;
bool EnemyOfAIHasAutoCombatInProgress() const;
bool CanToggleAutoCombat() const;

uint32_t GetTurnNumber() const
{
Expand Down Expand Up @@ -284,8 +284,8 @@ namespace Battle
void ApplyActionSpellCast( Command & cmd );
void ApplyActionTower( Command & cmd );
void ApplyActionCatapult( Command & cmd );
void ApplyActionAutoSwitch( Command & cmd );
void ApplyActionAutoFinish( const Command & cmd );
void ApplyActionToggleAutoCombat( Command & cmd );
void ApplyActionQuickCombat( const Command & cmd );

void ApplyActionSpellSummonElemental( const Command & cmd, const Spell & spell );
void ApplyActionSpellMirrorImage( Command & cmd );
Expand Down Expand Up @@ -343,8 +343,8 @@ namespace Battle
int _covrIcnId{ ICN::UNKNOWN };

uint32_t _turnNumber{ 0 };
// A set of colors of players for whom the auto-battle mode is enabled
int _autoBattleColors{ 0 };
// A set of colors of players for whom the auto combat mode is enabled
int _autoCombatColors{ 0 };

// This random number generator should only be used in code that is equally used by both AI and the human
// player - that is, in code related to the processing of battle commands. It cannot be safely used in other
Expand Down
6 changes: 3 additions & 3 deletions src/fheroes2/battle/battle_command.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2019 - 2023 *
* Copyright (C) 2019 - 2025 *
* *
* Free Heroes2 Engine: http://sourceforge.net/projects/fheroes2 *
* Copyright (C) 2012 by Andrey Afletdinov <[email protected]> *
Expand Down Expand Up @@ -63,8 +63,8 @@ uint32_t Battle::Command::updateSeed( uint32_t seed ) const
break;

// These commands should never affect the seed generation
case CommandType::AUTO_SWITCH:
case CommandType::AUTO_FINISH:
case CommandType::TOGGLE_AUTO_COMBAT:
case CommandType::QUICK_COMBAT:
break;

default:
Expand Down
10 changes: 5 additions & 5 deletions src/fheroes2/battle/battle_command.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ namespace Battle
RETREAT,
SURRENDER,
SKIP,
AUTO_SWITCH,
AUTO_FINISH
TOGGLE_AUTO_COMBAT,
QUICK_COMBAT
};

class Command final : public std::vector<int>
Expand All @@ -62,8 +62,8 @@ namespace Battle
static constexpr std::integral_constant<CommandType, CommandType::RETREAT> RETREAT{};
static constexpr std::integral_constant<CommandType, CommandType::SURRENDER> SURRENDER{};
static constexpr std::integral_constant<CommandType, CommandType::SKIP> SKIP{};
static constexpr std::integral_constant<CommandType, CommandType::AUTO_SWITCH> AUTO_SWITCH{};
static constexpr std::integral_constant<CommandType, CommandType::AUTO_FINISH> AUTO_FINISH{};
static constexpr std::integral_constant<CommandType, CommandType::TOGGLE_AUTO_COMBAT> TOGGLE_AUTO_COMBAT{};
static constexpr std::integral_constant<CommandType, CommandType::QUICK_COMBAT> QUICK_COMBAT{};

template <CommandType cmd, typename... Types>
explicit Command( std::integral_constant<CommandType, cmd> /* tag */, const Types... params )
Expand Down Expand Up @@ -107,7 +107,7 @@ namespace Battle
// UID
static_assert( sizeof...( params ) == 1 );
}
else if constexpr ( cmd == CommandType::AUTO_SWITCH ) {
else if constexpr ( cmd == CommandType::TOGGLE_AUTO_COMBAT ) {
// Color
static_assert( sizeof...( params ) == 1 );
}
Expand Down
Loading

0 comments on commit 2d4b7df

Please sign in to comment.