diff --git a/src/fheroes2/dialog/dialog_interface_settings.cpp b/src/fheroes2/dialog/dialog_interface_settings.cpp index 95a913b18d..2a8d7c5463 100644 --- a/src/fheroes2/dialog/dialog_interface_settings.cpp +++ b/src/fheroes2/dialog/dialog_interface_settings.cpp @@ -1,6 +1,6 @@ /*************************************************************************** * fheroes2: https://github.com/ihhub/fheroes2 * - * Copyright (C) 2023 - 2024 * + * Copyright (C) 2023 - 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 * @@ -63,18 +63,31 @@ namespace void drawInterfaceType( const fheroes2::Rect & optionRoi ) { const Settings & conf = Settings::Get(); - const bool isEvilInterface = conf.isEvilInterfaceEnabled(); - const fheroes2::Sprite & interfaceThemeIcon = fheroes2::AGG::GetICN( ICN::SPANEL, isEvilInterface ? 17 : 16 ); + const InterfaceType interfaceType = conf.getInterfaceType(); + uint32_t icnInx; std::string value; - if ( isEvilInterface ) { - value = _( "Evil" ); - } - else { + switch ( interfaceType ) { + case DYNAMIC: + icnInx = 15; + value = _( "Dynamic" ); + break; + case GOOD: + icnInx = 16; value = _( "Good" ); + break; + case EVIL: + icnInx = 17; + value = _( "Evil" ); + break; + default: + icnInx = 15; + value = _( "Dynamic" ); + assert( 0 ); } - fheroes2::drawOption( optionRoi, interfaceThemeIcon, _( "Interface Type" ), std::move( value ), fheroes2::UiOptionTextWidth::TWO_ELEMENTS_ROW ); + fheroes2::drawOption( optionRoi, fheroes2::AGG::GetICN( ICN::SPANEL, icnInx ), _( "Interface Type" ), std::move( value ), + fheroes2::UiOptionTextWidth::TWO_ELEMENTS_ROW ); } void drawInterfacePresence( const fheroes2::Rect & optionRoi ) @@ -287,7 +300,7 @@ namespace fheroes2 windowType = showConfigurationWindow( saveConfiguration ); break; case SelectedWindow::InterfaceType: - conf.setEvilInterface( !conf.isEvilInterfaceEnabled() ); + conf.setInterfaceType( static_cast( ( conf.getInterfaceType() + 1 ) % InterfaceType::COUNT ) ); updateUI(); saveConfiguration = true; diff --git a/src/fheroes2/game/game_campaign.cpp b/src/fheroes2/game/game_campaign.cpp index 7952a104bc..3ca488de0b 100644 --- a/src/fheroes2/game/game_campaign.cpp +++ b/src/fheroes2/game/game_campaign.cpp @@ -1,6 +1,6 @@ /*************************************************************************** * fheroes2: https://github.com/ihhub/fheroes2 * - * Copyright (C) 2020 - 2024 * + * Copyright (C) 2020 - 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 * @@ -1307,7 +1307,7 @@ fheroes2::GameMode Game::SelectCampaignScenario( const fheroes2::GameMode prevMo const std::vector & scenarios = campaignData.getAllScenarios(); const Campaign::ScenarioData & scenario = scenarios[currentScenarioInfoId.scenarioId]; - const fheroes2::GameInterfaceTypeRestorer gameInterfaceRestorer( chosenCampaignID != Campaign::ROLAND_CAMPAIGN ); + const fheroes2::GameInterfaceTypeRestorer gameInterfaceRestorer( chosenCampaignID == Campaign::ROLAND_CAMPAIGN ? InterfaceType::GOOD : InterfaceType::EVIL ); if ( !allowToRestart ) { playCurrentScenarioVideo(); diff --git a/src/fheroes2/game/game_startgame.cpp b/src/fheroes2/game/game_startgame.cpp index 0f992f8e94..6be7a2547f 100644 --- a/src/fheroes2/game/game_startgame.cpp +++ b/src/fheroes2/game/game_startgame.cpp @@ -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) 2009 by Andrey Afletdinov * @@ -735,6 +735,12 @@ fheroes2::GameMode Interface::AdventureMap::StartGame() // Fully update fog directions if there will be only one human player. Interface::GameArea::updateMapFogDirections(); + + if ( conf.getInterfaceType() == InterfaceType::DYNAMIC ) { + reset(); + redraw( Interface::REDRAW_RADAR ); + redraw( Interface::REDRAW_ALL & ( ~Interface::REDRAW_RADAR ) ); + } } while ( res == fheroes2::GameMode::END_TURN ) { @@ -784,7 +790,15 @@ fheroes2::GameMode Interface::AdventureMap::StartGame() // Reset environment sounds and music theme at the beginning of the human turn AudioManager::ResetAudio(); + conf.SetCurrentColor( playerColor ); + if ( isHotSeatGame ) { + if ( conf.getInterfaceType() == InterfaceType::DYNAMIC ) { + reset(); + redraw( Interface::REDRAW_RADAR ); + redraw( Interface::REDRAW_ALL & ( ~Interface::REDRAW_RADAR ) ); + } + _iconsPanel.hideIcons( ICON_ANY ); _statusPanel.Reset(); @@ -804,8 +818,6 @@ fheroes2::GameMode Interface::AdventureMap::StartGame() Game::DialogPlayers( playerColor, "", _( "%{color} player's turn." ) ); } - conf.SetCurrentColor( playerColor ); - kingdom.ActionBeforeTurn(); _iconsPanel.showIcons( ICON_ANY ); diff --git a/src/fheroes2/gui/ui_tool.cpp b/src/fheroes2/gui/ui_tool.cpp index e038207b08..c75f0a482e 100644 --- a/src/fheroes2/gui/ui_tool.cpp +++ b/src/fheroes2/gui/ui_tool.cpp @@ -1,6 +1,6 @@ /*************************************************************************** * fheroes2: https://github.com/ihhub/fheroes2 * - * Copyright (C) 2020 - 2024 * + * Copyright (C) 2020 - 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 * @@ -296,19 +296,19 @@ namespace fheroes2 Display::instance().changePalette( palette ); } - GameInterfaceTypeRestorer::GameInterfaceTypeRestorer( const bool isEvilInterface_ ) - : isEvilInterface( isEvilInterface_ ) - , isOriginalEvilInterface( Settings::Get().isEvilInterfaceEnabled() ) + GameInterfaceTypeRestorer::GameInterfaceTypeRestorer( const InterfaceType interfaceType_ ) + : interfaceType( interfaceType_ ) + , originalInterfaceType( Settings::Get().getInterfaceType() ) { - if ( isEvilInterface != isOriginalEvilInterface ) { - Settings::Get().setEvilInterface( isEvilInterface ); + if ( interfaceType != originalInterfaceType ) { + Settings::Get().setInterfaceType( interfaceType_ ); } } GameInterfaceTypeRestorer::~GameInterfaceTypeRestorer() { - if ( isEvilInterface != isOriginalEvilInterface ) { - Settings::Get().setEvilInterface( isOriginalEvilInterface ); + if ( interfaceType != originalInterfaceType ) { + Settings::Get().setInterfaceType( originalInterfaceType ); } } diff --git a/src/fheroes2/gui/ui_tool.h b/src/fheroes2/gui/ui_tool.h index 86fa78f31a..dba3df1dc0 100644 --- a/src/fheroes2/gui/ui_tool.h +++ b/src/fheroes2/gui/ui_tool.h @@ -1,6 +1,6 @@ /*************************************************************************** * fheroes2: https://github.com/ihhub/fheroes2 * - * Copyright (C) 2020 - 2024 * + * Copyright (C) 2020 - 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 * @@ -35,6 +35,8 @@ #include "ui_base.h" #include "ui_text.h" +enum InterfaceType : uint8_t; + namespace fheroes2 { class MovableSprite : public Sprite @@ -174,7 +176,7 @@ namespace fheroes2 struct GameInterfaceTypeRestorer { GameInterfaceTypeRestorer() = delete; - explicit GameInterfaceTypeRestorer( const bool isEvilInterface_ ); + explicit GameInterfaceTypeRestorer( const InterfaceType interfaceType_ ); GameInterfaceTypeRestorer( const GameInterfaceTypeRestorer & ) = delete; @@ -182,8 +184,8 @@ namespace fheroes2 GameInterfaceTypeRestorer & operator=( const GameInterfaceTypeRestorer & ) = delete; - const bool isEvilInterface; - const bool isOriginalEvilInterface; + const InterfaceType interfaceType; + const InterfaceType originalInterfaceType; }; // Fade display image colors to grayscale part of default game palette. diff --git a/src/fheroes2/kingdom/race.cpp b/src/fheroes2/kingdom/race.cpp index 6b6c8c407f..dba03127d2 100644 --- a/src/fheroes2/kingdom/race.cpp +++ b/src/fheroes2/kingdom/race.cpp @@ -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) 2009 by Andrey Afletdinov * @@ -126,6 +126,27 @@ bool Race::isMagicalRace( const int race ) return false; } +bool Race::isEvilRace( const int race ) +{ + switch ( race ) { + case BARB: + case WRLK: + case NECR: + return true; + case KNGT: + case SORC: + case WZRD: + case MULT: + case RAND: + return false; + default: + assert( 0 ); + break; + } + + return false; +} + uint8_t Race::IndexToRace( const int index ) { switch ( index ) { diff --git a/src/fheroes2/kingdom/race.h b/src/fheroes2/kingdom/race.h index 7aeedb064e..b427d05eac 100644 --- a/src/fheroes2/kingdom/race.h +++ b/src/fheroes2/kingdom/race.h @@ -53,4 +53,5 @@ namespace Race int getPreviousRace( const int race ); bool isMagicalRace( const int race ); + bool isEvilRace( const int race ); } diff --git a/src/fheroes2/system/settings.cpp b/src/fheroes2/system/settings.cpp index 20d5e9636e..4cb2b092fb 100644 --- a/src/fheroes2/system/settings.cpp +++ b/src/fheroes2/system/settings.cpp @@ -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) 2009 by Andrey Afletdinov * @@ -22,6 +22,7 @@ ***************************************************************************/ #include +#include #include #include #include @@ -36,6 +37,7 @@ #include "game.h" #include "game_io.h" #include "logging.h" +#include "race.h" #include "render_processor.h" #include "save_format_version.h" #include "screen.h" @@ -235,8 +237,17 @@ bool Settings::Read( const std::string & filePath ) setBattleShowTurnOrder( config.StrParams( "battle turn order" ) == "on" ); } - if ( config.Exists( "use evil interface" ) ) { - setEvilInterface( config.StrParams( "use evil interface" ) == "on" ); + if ( config.Exists( "interface type" ) ) { + const std::string interfaceType = config.StrParams( "interface type" ); + if ( interfaceType == "Good" ) { + setInterfaceType( InterfaceType::GOOD ); + } + else if ( interfaceType == "Evil" ) { + setInterfaceType( InterfaceType::EVIL ); + } + else { + setInterfaceType( InterfaceType::DYNAMIC ); + } } if ( config.Exists( "hide interface" ) ) { @@ -432,8 +443,20 @@ std::string Settings::String() const os << std::endl << "# show turn order during battle: on/off" << std::endl; os << "battle turn order = " << ( _gameOptions.Modes( GAME_BATTLE_SHOW_TURN_ORDER ) ? "on" : "off" ) << std::endl; - os << std::endl << "# use evil interface style: on/off" << std::endl; - os << "use evil interface = " << ( _gameOptions.Modes( GAME_EVIL_INTERFACE ) ? "on" : "off" ) << std::endl; + os << std::endl << "# interface type (Good/Evil/Dynamic)" << std::endl; + switch ( _interfaceType ) { + case GOOD: + os << "interface type = Good" << std::endl; + break; + case EVIL: + os << "interface type = Evil" << std::endl; + break; + case DYNAMIC: + os << "interface type = Dynamic" << std::endl; + break; + default: + assert( 0 ); + } os << std::endl << "# hide interface elements on the adventure map: on/off" << std::endl; os << "hide interface = " << ( _gameOptions.Modes( GAME_HIDE_INTERFACE ) ? "on" : "off" ) << std::endl; @@ -816,16 +839,6 @@ void Settings::setHideInterface( const bool enable ) } } -void Settings::setEvilInterface( const bool enable ) -{ - if ( enable ) { - _gameOptions.SetModes( GAME_EVIL_INTERFACE ); - } - else { - _gameOptions.ResetModes( GAME_EVIL_INTERFACE ); - } -} - void Settings::setScreenScalingTypeNearest( const bool enable ) { if ( enable ) { @@ -883,9 +896,46 @@ bool Settings::isHideInterfaceEnabled() const return _gameOptions.Modes( GAME_HIDE_INTERFACE ); } +void Settings::setInterfaceType( InterfaceType type ) +{ + assert( type >= InterfaceType::GOOD && type <= InterfaceType::DYNAMIC ); + _interfaceType = type; +} + +InterfaceType Settings::getInterfaceType() const +{ + return _interfaceType; +} + bool Settings::isEvilInterfaceEnabled() const { - return _gameOptions.Modes( GAME_EVIL_INTERFACE ); + switch ( _interfaceType ) { + case InterfaceType::GOOD: + return false; + case InterfaceType::EVIL: + return true; + case InterfaceType::DYNAMIC: { + Player * player = Settings::Get().GetPlayers().GetCurrent(); + if ( !player ) + return false; + + if ( player->isControlHuman() ) { + return Race::isEvilRace( player->GetRace() ); + } + + // Keep the UI of the last player during the AI turn + for ( auto iter = Settings::Get().GetPlayers().rbegin(); iter < Settings::Get().GetPlayers().rend(); ++iter ) { + if ( *iter && ( *iter )->isControlHuman() ) { + return Race::isEvilRace( ( *iter )->GetRace() ); + } + } + break; + } + default: + assert( 0 ); + } + + return false; } bool Settings::isEditorAnimationEnabled() const diff --git a/src/fheroes2/system/settings.h b/src/fheroes2/system/settings.h index ff30338888..a113a4ac13 100644 --- a/src/fheroes2/system/settings.h +++ b/src/fheroes2/system/settings.h @@ -64,6 +64,14 @@ enum class ZoomLevel : uint8_t ZoomLevel3 = 3, // Max zoom, but should only exists for debug builds }; +enum InterfaceType : uint8_t +{ + GOOD = 0, + EVIL = 1, + DYNAMIC = 2, + COUNT +}; + class Settings { public: @@ -191,6 +199,9 @@ class Settings bool isHideInterfaceEnabled() const; bool isEvilInterfaceEnabled() const; + void setInterfaceType( InterfaceType type ); + InterfaceType getInterfaceType() const; + bool isEditorAnimationEnabled() const; bool isEditorPassabilityEnabled() const; @@ -252,7 +263,6 @@ class Settings void setAutoSaveAtBeginningOfTurn( const bool enable ); void setBattleDamageInfo( const bool enable ); void setHideInterface( const bool enable ); - void setEvilInterface( const bool enable ); void setScreenScalingTypeNearest( const bool enable ); void SetSoundVolume( int v ); @@ -379,6 +389,7 @@ class Settings int ai_speed; int scroll_speed; int battle_speed; + InterfaceType _interfaceType; int game_type; ZoomLevel _viewWorldZoomLevel{ ZoomLevel::ZoomLevel1 };