Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added dynamic interface option #9413

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
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: 19 additions & 9 deletions src/fheroes2/dialog/dialog_interface_settings.cpp
Original file line number Diff line number Diff line change
@@ -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 *
Expand Down Expand Up @@ -63,18 +63,28 @@ 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 = 15;
std::string value;
if ( isEvilInterface ) {
value = _( "Evil" );
}
else {
switch ( interfaceType ) {
case DYNAMIC:
value = _( "Dynamic" );
break;
case GOOD:
icnInx = 16;
value = _( "Good" );
break;
case EVIL:
icnInx = 17;
value = _( "Evil" );
break;
default:
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 )
Expand Down Expand Up @@ -287,7 +297,7 @@ namespace fheroes2
windowType = showConfigurationWindow( saveConfiguration );
break;
case SelectedWindow::InterfaceType:
conf.setEvilInterface( !conf.isEvilInterfaceEnabled() );
conf.setInterfaceType( static_cast<InterfaceType>( ( conf.getInterfaceType() + 1 ) % InterfaceType::COUNT ) );
Copy link
Owner

Choose a reason for hiding this comment

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

This is the only place where InterfaceType::COUNT is really needed. We can simply do this for the code to avoid having an extra enumeration entry:

if ( conf.getInterfaceType() == InterfaceType::DYNAMIC ) {
    conf.setInterfaceType( InterfaceType::GOOD );
}
else if ( conf.getInterfaceType() == InterfaceType::GOOD ) {
    conf.setInterfaceType( InterfaceType::BAD );
}
else {
    conf.setInterfaceType( InterfaceType::DYNAMIC );
}

Then we won't need checks in the code for non-existing types.

updateUI();
saveConfiguration = true;

Expand Down
4 changes: 2 additions & 2 deletions src/fheroes2/game/game_campaign.cpp
Original file line number Diff line number Diff line change
@@ -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 *
Expand Down Expand Up @@ -1307,7 +1307,7 @@ fheroes2::GameMode Game::SelectCampaignScenario( const fheroes2::GameMode prevMo
const std::vector<Campaign::ScenarioData> & 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();
Expand Down
18 changes: 15 additions & 3 deletions src/fheroes2/game/game_startgame.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) 2009 by Andrey Afletdinov <[email protected]> *
Expand Down Expand Up @@ -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();
Copy link
Owner

Choose a reason for hiding this comment

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

This doesn't seem right. At line 702 we call reset and then we are calling it here. The code looks more like a hack. Could you please explain why we cannot do the same at line 702?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is because when the game is first drawn (L. 702 - 710), the current player has not been selected yet (L. 734).
This code was added to redraw the interface to make sure that it matches the current player, but you're right, there should be a way to only draw the interface once, I'll check if I can move some code around to make it happen.

redraw( Interface::REDRAW_RADAR );
redraw( Interface::REDRAW_ALL & ( ~Interface::REDRAW_RADAR ) );
}
}

while ( res == fheroes2::GameMode::END_TURN ) {
Expand Down Expand Up @@ -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 ) );
}
Comment on lines +796 to +800
Copy link
Owner

Choose a reason for hiding this comment

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

Do we want to rebuild everything even if the interface type is going to be the same?


_iconsPanel.hideIcons( ICON_ANY );
_statusPanel.Reset();

Expand All @@ -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 );
Expand Down
16 changes: 8 additions & 8 deletions src/fheroes2/gui/ui_tool.cpp
Original file line number Diff line number Diff line change
@@ -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 *
Expand Down Expand Up @@ -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 );
}
}

Expand Down
10 changes: 6 additions & 4 deletions src/fheroes2/gui/ui_tool.h
Original file line number Diff line number Diff line change
@@ -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 *
Expand Down Expand Up @@ -35,6 +35,8 @@
#include "ui_base.h"
#include "ui_text.h"

enum InterfaceType : uint8_t;

namespace fheroes2
{
class MovableSprite : public Sprite
Expand Down Expand Up @@ -174,16 +176,16 @@ namespace fheroes2
struct GameInterfaceTypeRestorer
{
GameInterfaceTypeRestorer() = delete;
explicit GameInterfaceTypeRestorer( const bool isEvilInterface_ );
explicit GameInterfaceTypeRestorer( const InterfaceType interfaceType_ );

GameInterfaceTypeRestorer( const GameInterfaceTypeRestorer & ) = delete;

~GameInterfaceTypeRestorer();

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.
Expand Down
23 changes: 22 additions & 1 deletion src/fheroes2/kingdom/race.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) 2009 by Andrey Afletdinov <[email protected]> *
Expand Down Expand Up @@ -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 ) {
Expand Down
1 change: 1 addition & 0 deletions src/fheroes2/kingdom/race.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ namespace Race
int getPreviousRace( const int race );

bool isMagicalRace( const int race );
bool isEvilRace( const int race );
}
82 changes: 66 additions & 16 deletions src/fheroes2/system/settings.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) 2009 by Andrey Afletdinov <[email protected]> *
Expand All @@ -22,6 +22,7 @@
***************************************************************************/

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <fstream>
#include <sstream>
Expand All @@ -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"
Expand Down Expand Up @@ -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" );
Comment on lines -238 to -239
Copy link
Owner

Choose a reason for hiding this comment

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

Here we are breaking the existing settings players set. We should read the value of the previous setting.

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 );
}
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
}

if ( config.Exists( "hide interface" ) ) {
Expand Down Expand Up @@ -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;
Comment on lines +449 to +455
Copy link
Owner

Choose a reason for hiding this comment

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

None of settings have values starting from a capital letter. Please keep it the same way,.

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;
Expand Down Expand Up @@ -816,16 +839,6 @@ void Settings::setHideInterface( const bool enable )
}
}

void Settings::setEvilInterface( const bool enable )
{
if ( enable ) {
_gameOptions.SetModes( GAME_EVIL_INTERFACE );
Copy link
Owner

Choose a reason for hiding this comment

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

GAME_EVIL_INTERFACE must be renamed into UNUSED_GAME_EVIL_INTERFACE as we are no longer supporting it.

}
else {
_gameOptions.ResetModes( GAME_EVIL_INTERFACE );
}
}

void Settings::setScreenScalingTypeNearest( const bool enable )
{
if ( enable ) {
Expand Down Expand Up @@ -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 );
Copy link
Owner

Choose a reason for hiding this comment

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

Instead of avoid this assertion we simply can remove InterfaceType::COUNT from the enumeration.

_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();
Copy link
Owner

Choose a reason for hiding this comment

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

This method is not static. We should use its own methods within the body.

if ( !player )
return false;
Comment on lines +919 to +920
Copy link
Owner

Choose a reason for hiding this comment

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

Is it even possible?

Also, please parenthesis for this if-body.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Might be related to my last comment where the current player might not be set at the start of the game. I'll remove this check if I update the code once I find a way to only draw the interface once at the beginning of the game.


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() );
}
}
Comment on lines +926 to +931
Copy link
Owner

Choose a reason for hiding this comment

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

Do we need to check this? UI Interface changes only during player's turn.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some parts of the UI is updated during the AI's turn.
Mainly the top part with the shield:
image

If I remove this code, the background of the shield will change color very rapidly based on the AI factions

break;
}
default:
assert( 0 );
}

return false;
}

bool Settings::isEditorAnimationEnabled() const
Expand Down
Loading
Loading