Ver código fonte

Merge pull request #1540 from dydzio0614/settings-rework

Settings rework
Ivan Savenko 2 anos atrás
pai
commit
cc84bed0c0
45 arquivos alterados com 2033 adições e 474 exclusões
  1. BIN
      Mods/vcmi/Data/vcmiSettingsWindow.png
  2. BIN
      Mods/vcmi/Sprites/buttons/quadwide-normal-selected.png
  3. BIN
      Mods/vcmi/Sprites/buttons/quadwide-normal.png
  4. BIN
      Mods/vcmi/Sprites/buttons/quadwide-pressed-selected.png
  5. BIN
      Mods/vcmi/Sprites/buttons/quadwide-pressed.png
  6. 10 0
      Mods/vcmi/Sprites/buttons/quadwide.json
  7. BIN
      Mods/vcmi/Sprites/buttons/square-normal-selected.png
  8. BIN
      Mods/vcmi/Sprites/buttons/square-normal.png
  9. BIN
      Mods/vcmi/Sprites/buttons/square-pressed-selected.png
  10. BIN
      Mods/vcmi/Sprites/buttons/square-pressed.png
  11. 10 0
      Mods/vcmi/Sprites/buttons/square.json
  12. BIN
      Mods/vcmi/Sprites/buttons/triplewide-normal-selected.png
  13. BIN
      Mods/vcmi/Sprites/buttons/triplewide-normal.png
  14. BIN
      Mods/vcmi/Sprites/buttons/triplewide-pressed-selected.png
  15. BIN
      Mods/vcmi/Sprites/buttons/triplewide-pressed.png
  16. 10 0
      Mods/vcmi/Sprites/buttons/triplewide.json
  17. 47 2
      Mods/vcmi/config/vcmi/english.json
  18. 10 0
      client/CMakeLists.txt
  19. 3 2
      client/adventureMap/CAdvMapInt.cpp
  20. 1 1
      client/adventureMap/mapHandler.cpp
  21. 26 38
      client/battle/BattleInterface.cpp
  22. 4 5
      client/battle/BattleInterface.h
  23. 0 72
      client/battle/BattleInterfaceClasses.cpp
  24. 0 16
      client/battle/BattleInterfaceClasses.h
  25. 17 6
      client/battle/BattleWindow.cpp
  26. 7 3
      client/battle/BattleWindow.h
  27. 7 0
      client/gui/InterfaceObjectConfigurable.cpp
  28. 0 274
      client/windows/GUIClasses.cpp
  29. 0 52
      client/windows/GUIClasses.h
  30. 96 0
      client/windows/settings/AdventureOptionsTab.cpp
  31. 19 0
      client/windows/settings/AdventureOptionsTab.h
  32. 169 0
      client/windows/settings/BattleOptionsTab.cpp
  33. 36 0
      client/windows/settings/BattleOptionsTab.h
  34. 229 0
      client/windows/settings/GeneralOptionsTab.cpp
  35. 34 0
      client/windows/settings/GeneralOptionsTab.h
  36. 47 0
      client/windows/settings/OtherOptionsTab.cpp
  37. 20 0
      client/windows/settings/OtherOptionsTab.h
  38. 150 0
      client/windows/settings/SettingsMainWindow.cpp
  39. 44 0
      client/windows/settings/SettingsMainWindow.h
  40. 47 3
      config/schemas/settings.json
  41. 267 0
      config/widgets/settings/adventureOptionsTab.json
  42. 358 0
      config/widgets/settings/battleOptionsTab.json
  43. 158 0
      config/widgets/settings/generalOptionsTab.json
  44. 42 0
      config/widgets/settings/otherOptionsTab.json
  45. 165 0
      config/widgets/settings/settingsMainContainer.json

BIN
Mods/vcmi/Data/vcmiSettingsWindow.png


BIN
Mods/vcmi/Sprites/buttons/quadwide-normal-selected.png


BIN
Mods/vcmi/Sprites/buttons/quadwide-normal.png


BIN
Mods/vcmi/Sprites/buttons/quadwide-pressed-selected.png


BIN
Mods/vcmi/Sprites/buttons/quadwide-pressed.png


+ 10 - 0
Mods/vcmi/Sprites/buttons/quadwide.json

@@ -0,0 +1,10 @@
+{
+	"basepath" : "buttons/",
+	"images" :
+	[
+		{ "frame" : 0, "file" : "quadwide-normal.png"},
+		{ "frame" : 1, "file" : "quadwide-pressed-selected.png"},
+		{ "frame" : 2, "file" : "quadwide-pressed.png"},
+		{ "frame" : 3, "file" : "quadwide-normal-selected.png"},
+	]
+}

BIN
Mods/vcmi/Sprites/buttons/square-normal-selected.png


BIN
Mods/vcmi/Sprites/buttons/square-normal.png


BIN
Mods/vcmi/Sprites/buttons/square-pressed-selected.png


BIN
Mods/vcmi/Sprites/buttons/square-pressed.png


+ 10 - 0
Mods/vcmi/Sprites/buttons/square.json

@@ -0,0 +1,10 @@
+{
+	"basepath" : "buttons/",
+	"images" :
+	[
+		{ "frame" : 0, "file" : "square-normal.png"},
+		{ "frame" : 1, "file" : "square-pressed-selected.png"},
+		{ "frame" : 2, "file" : "square-pressed.png"},
+		{ "frame" : 3, "file" : "square-normal-selected.png"}
+	]
+}

BIN
Mods/vcmi/Sprites/buttons/triplewide-normal-selected.png


BIN
Mods/vcmi/Sprites/buttons/triplewide-normal.png


BIN
Mods/vcmi/Sprites/buttons/triplewide-pressed-selected.png


BIN
Mods/vcmi/Sprites/buttons/triplewide-pressed.png


+ 10 - 0
Mods/vcmi/Sprites/buttons/triplewide.json

@@ -0,0 +1,10 @@
+{
+	"basepath" : "buttons/",
+	"images" :
+	[
+		{ "frame" : 0, "file" : "triplewide-normal.png"},
+		{ "frame" : 1, "file" : "triplewide-pressed-selected.png"},
+		{ "frame" : 2, "file" : "triplewide-pressed.png"},
+		{ "frame" : 3, "file" : "triplewide-normal-selected.png"}
+	]
+}

+ 47 - 2
Mods/vcmi/config/vcmi/english.json

@@ -23,15 +23,60 @@
 
 	"vcmi.server.errors.existingProcess"     : "Another vcmiserver process is running, please terminate it first",
 	"vcmi.server.errors.modsIncompatibility" : "Required mods to load game:",
-	"vcmi.server.confirmReconnect"          : "Connect to the last session?",
+	"vcmi.server.confirmReconnect"           : "Connect to the last session?",
+
+	"vcmi.settingsMainWindow.generalTab.hover" : "General",
+	"vcmi.settingsMainWindow.generalTab.help"     : "Switches to System Options tab - these settings are related to general game client behavior",
+	"vcmi.settingsMainWindow.battleTab.hover" : "Battle",
+	"vcmi.settingsMainWindow.battleTab.help"     : "Switches to Battle Options tab - these settings allow configuring battle interface and related things",
+	"vcmi.settingsMainWindow.adventureTab.hover" : "Adventure Map",
+	"vcmi.settingsMainWindow.adventureTab.help"  : "Switches to Adventure Map Options tab - adventure map is part of the game where you can move your heroes",
+	"vcmi.settingsMainWindow.otherTab.hover"     : "Other Settings",
+	"vcmi.settingsMainWindow.otherTab.help"      : "Switches to Other Settings tab - these options do not fit into other categories for various reasons",
 
 	"vcmi.systemOptions.fullscreenButton.hover" : "Fullscreen",
-	"vcmi.systemOptions.fullscreenButton.help"  : "{Fullscreen}\n\n If selected, VCMI will run in fullscreen mode, othervice VCMI will run in window",
+	"vcmi.systemOptions.fullscreenButton.help"  : "{Fullscreen}\n\n If selected, VCMI will run in fullscreen mode, otherwise VCMI will run in window",
 	"vcmi.systemOptions.resolutionButton.hover" : "Resolution",
 	"vcmi.systemOptions.resolutionButton.help"  : "{Select resolution}\n\n Change in-game screen resolution. Game restart required to apply new resolution.",
 	"vcmi.systemOptions.resolutionMenu.hover"   : "Select resolution",
 	"vcmi.systemOptions.resolutionMenu.help"    : "Change in-game screen resolution.",
 	"vcmi.systemOptions.fullscreenFailed"       : "{Fullscreen}\n\n Failed to switch to fullscreen mode! Current resolution is not supported by display!",
+	"vcmi.systemOptions.framerateButton.hover"  : "Show FPS",
+	"vcmi.systemOptions.framerateButton.help"   : "{Show FPS}\n\n Toggle on/off showing Frames Per Second counter in corner of game window.",
+
+	"vcmi.adventureOptions.numericQuantities.hover" : "Numeric creatures quantities",
+	"vcmi.adventureOptions.numericQuantities.help" : "{Numeric creatures quantities}\n\n Shows inaccurate enemy creatures quantities in numeric A-B format.",
+	"vcmi.adventureOptions.forceMovementInfo.hover" : "Always show move info in status bar",
+	"vcmi.adventureOptions.forceMovementInfo.help" : "{Always show move info in status bar}\n\n Replaces default status bar info with movement points data without need to hold ALT button.",
+	"vcmi.adventureOptions.showGrid.hover" : "Show grid",
+	"vcmi.adventureOptions.showGrid.help" : "{Show grid}\n\n Shows grid overlay, showing borders between adventure map tiles.",
+	"vcmi.adventureOptions.mapScrollSpeed4.hover": "4",
+	"vcmi.adventureOptions.mapScrollSpeed4.help": "Set map scrolling speed to very fast",
+	"vcmi.adventureOptions.mapScrollSpeed5.hover": "5",
+	"vcmi.adventureOptions.mapScrollSpeed5.help": "Set map scrolling speed to extremely fast",
+
+	"vcmi.battleOptions.showQueue.hover": "Show queue",
+	"vcmi.battleOptions.showQueue.help": "{Show queue}\n\n Show queue that displays movement order of creature stacks.",
+	"vcmi.battleOptions.queueSizeLabel.hover": "Queue size (takes effect on next battle)",
+	"vcmi.battleOptions.queueSizeAutoButton.hover": "AUTO",
+	"vcmi.battleOptions.queueSizeAutoButton.help": "Sets queue size depending on game resolution (small if width < 700 pixels, big otherwise)",
+	"vcmi.battleOptions.queueSizeSmallButton.hover": "SMALL",
+	"vcmi.battleOptions.queueSizeSmallButton.help": "Sets queue size to small",
+	"vcmi.battleOptions.queueSizeBigButton.hover": "BIG",
+	"vcmi.battleOptions.queueSizeBigButton.help": "Sets queue size to big (not supported if game resolution width < 700 pixels)",
+	"vcmi.battleOptions.animationsSpeed4.hover": "4",
+	"vcmi.battleOptions.animationsSpeed4.help": "Sets animation speed to very fast",
+	"vcmi.battleOptions.animationsSpeed5.hover": "5",
+	"vcmi.battleOptions.animationsSpeed5.help": "Sets animation speed to super fast",
+	"vcmi.battleOptions.animationsSpeed6.hover": "6",
+	"vcmi.battleOptions.animationsSpeed6.help": "Sets animation speed to extremely fast",
+	"vcmi.battleOptions.skipBattleIntroMusic.hover": "Skip intro music",
+	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Skip intro music}\n\n Skip short music that plays at beginning of each battle before action starts. Can also be skipped by pressing ESC key.",
+
+	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "Show available creatures in town summary",
+	"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Show available creatures in town summary}\n\n Shows creatures available to purchase instead of their growth in town summary (bottom-left corner).",
+	"vcmi.otherOptions.compactTownCreatureInfo.hover": "Compact creature info in town summary",
+	"vcmi.otherOptions.compactTownCreatureInfo.help": "{Compact creature info in town summary}\n\n Smaller town creatures information in town summary.",
 
 	"vcmi.townHall.missingBase"             : "Base building %s must be built first",
 	"vcmi.townHall.noCreaturesToRecruit"    : "There are no creatures to recruit!",

+ 10 - 0
client/CMakeLists.txt

@@ -91,6 +91,11 @@ set(client_SRCS
 	windows/GUIClasses.cpp
 	windows/InfoWindows.cpp
 	windows/QuickRecruitmentWindow.cpp
+	windows/settings/GeneralOptionsTab.cpp
+	windows/settings/OtherOptionsTab.cpp
+	windows/settings/SettingsMainWindow.cpp
+	windows/settings/BattleOptionsTab.cpp
+	windows/settings/AdventureOptionsTab.cpp
 
 	CGameInfo.cpp
 	CMT.cpp
@@ -203,6 +208,11 @@ set(client_HEADERS
 	windows/GUIClasses.h
 	windows/InfoWindows.h
 	windows/QuickRecruitmentWindow.h
+	windows/settings/GeneralOptionsTab.h
+	windows/settings/OtherOptionsTab.h
+	windows/settings/SettingsMainWindow.h
+	windows/settings/BattleOptionsTab.h
+	windows/settings/AdventureOptionsTab.h
 
 	CGameInfo.h
 	CMT.h

+ 3 - 2
client/adventureMap/CAdvMapInt.cpp

@@ -35,6 +35,7 @@
 #include "../gui/CGuiHandler.h"
 #include "../widgets/TextControls.h"
 #include "../widgets/Buttons.h"
+#include "../windows/settings/SettingsMainWindow.h"
 #include "../CMT.h"
 
 #include "../../CCallback.h"
@@ -346,7 +347,7 @@ void CAdvMapInt::fadventureOPtions()
 
 void CAdvMapInt::fsystemOptions()
 {
-	GH.pushIntT<CSystemOptionsWindow>();
+	GH.pushIntT<SettingsMainWindow>();
 }
 
 void CAdvMapInt::fnextHero()
@@ -1322,7 +1323,7 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
 		const CGPathNode * pathNode = LOCPLINT->cb->getPathsInfo(hero)->getPathInfo(mapPos);
 		assert(pathNode);
 
-		if(GH.isKeyboardAltDown() && pathNode->reachable()) //overwrite status bar text with movement info
+		if((GH.isKeyboardAltDown() || settings["gameTweaks"]["forceMovementInfo"].Bool()) && pathNode->reachable()) //overwrite status bar text with movement info
 		{
 			showMoveDetailsInStatusbar(*hero, *pathNode);
 		}

+ 1 - 1
client/adventureMap/mapHandler.cpp

@@ -920,7 +920,7 @@ void CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingInfo * info)
 	drawOverlayEx(targetSurf);
 
 	// drawDebugGrid()
-	if (settings["session"]["showGrid"].Bool())
+	if (settings["gameTweaks"]["showGrid"].Bool())
 	{
 		for (realPos.x = initPos.x, pos.x = topTile.x; pos.x < topTile.x + tileCount.x; pos.x++, realPos.x += tileSize)
 		{

+ 26 - 38
client/battle/BattleInterface.cpp

@@ -97,7 +97,22 @@ BattleInterface::BattleInterface(const CCreatureSet *army1, const CCreatureSet *
 
 	CCS->musich->stopMusic();
 	setAnimationCondition(EAnimationEvents::OPENING, true);
-	battleIntroSoundChannel = CCS->soundh->playSoundFromSet(CCS->soundh->battleIntroSounds);
+
+	GH.pushInt(windowObject);
+	windowObject->blockUI(true);
+	windowObject->updateQueue();
+
+	playIntroSoundAndUnlockInterface();
+}
+
+void BattleInterface::playIntroSoundAndUnlockInterface()
+{
+	if(settings["gameTweaks"]["skipBattleIntroMusic"].Bool())
+	{
+		onIntroSoundPlayed();
+		return;
+	}
+
 	auto onIntroPlayed = [this]()
 	{
 		if(LOCPLINT->battleInt)
@@ -107,10 +122,7 @@ BattleInterface::BattleInterface(const CCreatureSet *army1, const CCreatureSet *
 		}
 	};
 
-	GH.pushInt(windowObject);
-	windowObject->blockUI(true);
-	windowObject->updateQueue();
-
+	battleIntroSoundChannel = CCS->soundh->playSoundFromSet(CCS->soundh->battleIntroSounds);
 	if (battleIntroSoundChannel != -1)
 		CCS->soundh->setCallback(battleIntroSoundChannel, onIntroPlayed);
 	else
@@ -145,30 +157,12 @@ BattleInterface::~BattleInterface()
 	setAnimationCondition(EAnimationEvents::ACTION, false);
 }
 
-void BattleInterface::setPrintCellBorders(bool set)
-{
-	Settings cellBorders = settings.write["battle"]["cellBorders"];
-	cellBorders->Bool() = set;
-
-	fieldController->redrawBackgroundWithHexes();
-	GH.totalRedraw();
-}
-
-void BattleInterface::setPrintStackRange(bool set)
+void BattleInterface::redrawBattlefield()
 {
-	Settings stackRange = settings.write["battle"]["stackRange"];
-	stackRange->Bool() = set;
-
 	fieldController->redrawBackgroundWithHexes();
 	GH.totalRedraw();
 }
 
-void BattleInterface::setPrintMouseShadow(bool set)
-{
-	Settings shadow = settings.write["battle"]["mouseShadow"];
-	shadow->Bool() = set;
-}
-
 void BattleInterface::stackReset(const CStack * stack)
 {
 	stacksController->stackReset(stack);
@@ -516,20 +510,6 @@ void BattleInterface::displaySpellHit(const CSpell * spell, BattleHex destinatio
 		displaySpellAnimationQueue(spell, spell->animationInfo.hit, destinationTile, true);
 }
 
-void BattleInterface::setAnimSpeed(int set)
-{
-	Settings speed = settings.write["battle"]["speedFactor"];
-	speed->Float() = float(set);
-}
-
-int BattleInterface::getAnimSpeed() const
-{
-	if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-battle-speed"].isNull())
-		return static_cast<int>(std::round(settings["session"]["spectate-battle-speed"].Float()));
-
-	return static_cast<int>(std::round(settings["battle"]["speedFactor"].Float()));
-}
-
 CPlayerInterface *BattleInterface::getCurrentPlayerInterface() const
 {
 	return curInt.get();
@@ -766,3 +746,11 @@ void BattleInterface::executeOnAnimationCondition( EAnimationEvents event, bool
 {
 	awaitingEvents.push_back({action, event, state});
 }
+
+void BattleInterface::setBattleQueueVisibility(bool visible)
+{
+	if(visible)
+		windowObject->showQueue();
+	else
+		windowObject->hideQueue();
+}

+ 4 - 5
client/battle/BattleInterface.h

@@ -112,6 +112,7 @@ class BattleInterface
 	/// defender interface, not null if attacker is human in our vcmiclient
 	std::shared_ptr<CPlayerInterface> defenderInt;
 
+	void playIntroSoundAndUnlockInterface();
 	void onIntroSoundPlayed();
 public:
 	/// copy of initial armies (for result window)
@@ -169,16 +170,14 @@ public:
 
 	void appendBattleLog(const std::string & newEntry);
 
-	void setPrintCellBorders(bool set); //if true, cell borders will be printed
-	void setPrintStackRange(bool set); //if true,range of active stack will be printed
-	void setPrintMouseShadow(bool set); //if true, hex under mouse will be shaded
-	void setAnimSpeed(int set); //speed of animation; range 1..100
-	int getAnimSpeed() const; //speed of animation; range 1..100
+	void redrawBattlefield(); //refresh GUI after changing stack range / grid settings
 	CPlayerInterface *getCurrentPlayerInterface() const;
 
 	void tacticNextStack(const CStack *current);
 	void tacticPhaseEnd();
 
+	void setBattleQueueVisibility(bool visible);
+
 	/// sets condition to targeted state and executes any awaiting actions
 	void setAnimationCondition( EAnimationEvents event, bool state);
 

+ 0 - 72
client/battle/BattleInterfaceClasses.cpp

@@ -396,78 +396,6 @@ HeroInfoWindow::HeroInfoWindow(const InfoAboutHero & hero, Point * position)
 	labels.push_back(std::make_shared<CLabel>(39, 186, EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(currentSpellPoints) + "/" + std::to_string(maxSpellPoints)));
 }
 
-BattleOptionsWindow::BattleOptionsWindow(BattleInterface & owner):
-	CWindowObject(PLAYER_COLORED, "comopbck.bmp")
-{
-	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
-
-	auto viewGrid = std::make_shared<CToggleButton>(Point(25, 56), "sysopchk.def", CGI->generaltexth->zelp[427], [&](bool on){owner.setPrintCellBorders(on);} );
-	viewGrid->setSelected(settings["battle"]["cellBorders"].Bool());
-	toggles.push_back(viewGrid);
-
-	auto movementShadow = std::make_shared<CToggleButton>(Point(25, 89), "sysopchk.def", CGI->generaltexth->zelp[428], [&](bool on){owner.setPrintStackRange(on);});
-	movementShadow->setSelected(settings["battle"]["stackRange"].Bool());
-	toggles.push_back(movementShadow);
-
-	auto mouseShadow = std::make_shared<CToggleButton>(Point(25, 122), "sysopchk.def", CGI->generaltexth->zelp[429], [&](bool on){owner.setPrintMouseShadow(on);});
-	mouseShadow->setSelected(settings["battle"]["mouseShadow"].Bool());
-	toggles.push_back(mouseShadow);
-
-	animSpeeds = std::make_shared<CToggleGroup>([&](int value){ owner.setAnimSpeed(value);});
-
-	std::shared_ptr<CToggleButton> toggle;
-	toggle = std::make_shared<CToggleButton>(Point( 28, 225), "sysopb9.def", CGI->generaltexth->zelp[422]);
-	animSpeeds->addToggle(1, toggle);
-
-	toggle = std::make_shared<CToggleButton>(Point( 92, 225), "sysob10.def", CGI->generaltexth->zelp[423]);
-	animSpeeds->addToggle(2, toggle);
-
-	toggle = std::make_shared<CToggleButton>(Point(156, 225), "sysob11.def", CGI->generaltexth->zelp[424]);
-	animSpeeds->addToggle(3, toggle);
-
-	animSpeeds->setSelected(owner.getAnimSpeed());
-
-	setToDefault = std::make_shared<CButton>(Point(246, 359), "codefaul.def", CGI->generaltexth->zelp[393], [&](){ bDefaultf(); });
-	setToDefault->setImageOrder(1, 0, 2, 3);
-	exit = std::make_shared<CButton>(Point(357, 359), "soretrn.def", CGI->generaltexth->zelp[392], [&](){ bExitf();}, SDLK_RETURN);
-	exit->setImageOrder(1, 0, 2, 3);
-
-	//creating labels
-	labels.push_back(std::make_shared<CLabel>(242,  32, FONT_BIG,    ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[392]));//window title
-	labels.push_back(std::make_shared<CLabel>(122, 214, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[393]));//animation speed
-	labels.push_back(std::make_shared<CLabel>(122, 293, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[394]));//music volume
-	labels.push_back(std::make_shared<CLabel>(122, 359, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[395]));//effects' volume
-	labels.push_back(std::make_shared<CLabel>(353,  66, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[396]));//auto - combat options
-	labels.push_back(std::make_shared<CLabel>(353, 265, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[397]));//creature info
-
-	//auto - combat options
-	labels.push_back(std::make_shared<CLabel>(283,  86, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[398]));//creatures
-	labels.push_back(std::make_shared<CLabel>(283, 116, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[399]));//spells
-	labels.push_back(std::make_shared<CLabel>(283, 146, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[400]));//catapult
-	labels.push_back(std::make_shared<CLabel>(283, 176, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[151]));//ballista
-	labels.push_back(std::make_shared<CLabel>(283, 206, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[401]));//first aid tent
-
-	//creature info
-	labels.push_back(std::make_shared<CLabel>(283, 285, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[402]));//all stats
-	labels.push_back(std::make_shared<CLabel>(283, 315, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[403]));//spells only
-
-	//general options
-	labels.push_back(std::make_shared<CLabel>(61,  57, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[404]));
-	labels.push_back(std::make_shared<CLabel>(61,  90, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[405]));
-	labels.push_back(std::make_shared<CLabel>(61, 123, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[406]));
-	labels.push_back(std::make_shared<CLabel>(61, 156, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[407]));
-}
-
-void BattleOptionsWindow::bDefaultf()
-{
-	//TODO: implement
-}
-
-void BattleOptionsWindow::bExitf()
-{
-	close();
-}
-
 BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface & _owner)
 	: owner(_owner)
 {

+ 0 - 16
client/battle/BattleInterfaceClasses.h

@@ -137,22 +137,6 @@ public:
 	HeroInfoWindow(const InfoAboutHero & hero, Point * position);
 };
 
-/// Class which manages the battle options window
-class BattleOptionsWindow : public CWindowObject
-{
-private:
-	std::shared_ptr<CButton> setToDefault;
-	std::shared_ptr<CButton> exit;
-	std::shared_ptr<CToggleGroup> animSpeeds;
-	std::vector<std::shared_ptr<CLabel>> labels;
-	std::vector<std::shared_ptr<CToggleButton>> toggles;
-public:
-	BattleOptionsWindow(BattleInterface & owner);
-
-	void bDefaultf(); //default button callback
-	void bExitf(); //exit button callback
-};
-
 /// Class which is responsible for showing the battle result window
 class BattleResultWindow : public WindowBase
 {

+ 17 - 6
client/battle/BattleWindow.cpp

@@ -35,6 +35,7 @@
 #include "../../lib/CStack.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/filesystem/ResourceID.h"
+#include "../windows/settings/SettingsMainWindow.h"
 
 BattleWindow::BattleWindow(BattleInterface & owner):
 	owner(owner)
@@ -110,8 +111,19 @@ std::shared_ptr<BattleConsole> BattleWindow::buildBattleConsole(const JsonNode &
 	return std::make_shared<BattleConsole>(background, rect.topLeft(), offset, rect.dimensions() );
 }
 
+void BattleWindow::toggleQueueVisibility()
+{
+	if(settings["battle"]["showQueue"].Bool())
+		hideQueue();
+	else
+		showQueue();
+}
+
 void BattleWindow::hideQueue()
 {
+	if(settings["battle"]["showQueue"].Bool() == false)
+		return;
+
 	Settings showQueue = settings.write["battle"]["showQueue"];
 	showQueue->Bool() = false;
 
@@ -129,6 +141,9 @@ void BattleWindow::hideQueue()
 
 void BattleWindow::showQueue()
 {
+	if(settings["battle"]["showQueue"].Bool() == true)
+		return;
+
 	Settings showQueue = settings.write["battle"]["showQueue"];
 	showQueue->Bool() = true;
 
@@ -166,11 +181,7 @@ void BattleWindow::keyPressed(const SDL_Keycode & key)
 {
 	if(key == SDLK_q)
 	{
-		if(settings["battle"]["showQueue"].Bool()) //hide queue
-			hideQueue();
-		else
-			showQueue();
-
+		toggleQueueVisibility();
 	}
 	else if(key == SDLK_f)
 	{
@@ -234,7 +245,7 @@ void BattleWindow::bOptionsf()
 
 	CCS->curh->set(Cursor::Map::POINTER);
 
-	GH.pushIntT<BattleOptionsWindow>(owner);
+	GH.pushIntT<SettingsMainWindow>(&owner);
 }
 
 void BattleWindow::bSurrenderf()

+ 7 - 3
client/battle/BattleWindow.h

@@ -55,9 +55,9 @@ class BattleWindow : public InterfaceObjectConfigurable
 	PossiblePlayerBattleAction defaultAction;
 	void showAlternativeActionIcon(PossiblePlayerBattleAction);
 
-	/// Toggle StackQueue visibility
-	void hideQueue();
-	void showQueue();
+	/// flip battle queue visibility to opposite
+	void toggleQueueVisibility();
+
 
 	std::shared_ptr<BattleConsole> buildBattleConsole(const JsonNode &) const;
 
@@ -68,6 +68,10 @@ public:
 	/// Closes window once battle finished
 	void close();
 
+	/// Toggle StackQueue visibility
+	void hideQueue();
+	void showQueue();
+
 	/// block all UI elements when player is not allowed to act, e.g. during enemy turn
 	void blockUI(bool on);
 

+ 7 - 0
client/gui/InterfaceObjectConfigurable.cpp

@@ -281,6 +281,13 @@ std::shared_ptr<CToggleButton> InterfaceObjectConfigurable::buildToggleButton(co
 	auto image = config["image"].String();
 	auto help = readHintText(config["help"]);
 	auto button = std::make_shared<CToggleButton>(position, image, help);
+	if(!config["items"].isNull())
+	{
+		for(const auto & item : config["items"].Vector())
+		{
+			button->addOverlay(buildWidget(item));
+		}
+	}
 	if(!config["selected"].isNull())
 		button->setSelected(config["selected"].Bool());
 	if(!config["imageOrder"].isNull())

+ 0 - 274
client/windows/GUIClasses.cpp

@@ -425,280 +425,6 @@ CLevelWindow::~CLevelWindow()
 	LOCPLINT->showingDialog->setn(false);
 }
 
-static void setIntSetting(std::string group, std::string field, int value)
-{
-	Settings entry = settings.write[group][field];
-	entry->Float() = value;
-}
-
-static void setBoolSetting(std::string group, std::string field, bool value)
-{
-	Settings fullscreen = settings.write[group][field];
-	fullscreen->Bool() = value;
-}
-
-static std::string resolutionToString(int w, int h)
-{
-	return std::to_string(w) + 'x' + std::to_string(h);
-}
-
-CSystemOptionsWindow::CSystemOptionsWindow()
-	: CWindowObject(PLAYER_COLORED, "SysOpBck"),
-	onFullscreenChanged(settings.listen["video"]["fullscreen"])
-{
-	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
-	title = std::make_shared<CLabel>(242, 32, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[568]);
-
-	//left window section
-	leftGroup = std::make_shared<CLabelGroup>(FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW);
-	leftGroup->add(122,  64, CGI->generaltexth->allTexts[569]);
-	leftGroup->add(122, 130, CGI->generaltexth->allTexts[570]);
-	leftGroup->add(122, 196, CGI->generaltexth->allTexts[571]);
-	leftGroup->add(122, 262, CGI->generaltexth->translate("vcmi.systemOptions.resolutionButton.hover"));
-	leftGroup->add(122, 347, CGI->generaltexth->allTexts[394]);
-	leftGroup->add(122, 412, CGI->generaltexth->allTexts[395]);
-
-	//right section
-	rightGroup = std::make_shared<CLabelGroup>(FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE);
-	rightGroup->add(282, 57,  CGI->generaltexth->allTexts[572]);
-	rightGroup->add(282, 89,  CGI->generaltexth->allTexts[573]);
-	rightGroup->add(282, 121, CGI->generaltexth->allTexts[574]);
-	rightGroup->add(282, 153, CGI->generaltexth->allTexts[577]);
-	rightGroup->add(282, 217, CGI->generaltexth->translate("vcmi.systemOptions.fullscreenButton.hover"));
-
-	//setting up buttons
-	load = std::make_shared<CButton>(Point(246,  298), "SOLOAD.DEF", CGI->generaltexth->zelp[321], [&](){ bloadf(); }, SDLK_l);
-	load->setImageOrder(1, 0, 2, 3);
-
-	save = std::make_shared<CButton>(Point(357, 298), "SOSAVE.DEF", CGI->generaltexth->zelp[322], [&](){ bsavef(); }, SDLK_s);
-	save->setImageOrder(1, 0, 2, 3);
-
-	restart = std::make_shared<CButton>(Point(246, 357), "SORSTRT", CGI->generaltexth->zelp[323], [&](){ brestartf(); }, SDLK_r);
-	restart->setImageOrder(1, 0, 2, 3);
-
-	if(CSH->isGuest())
-	{
-		load->block(true);
-		save->block(true);
-		restart->block(true);
-	}
-
-	mainMenu = std::make_shared<CButton>(Point(357, 357), "SOMAIN.DEF", CGI->generaltexth->zelp[320], [&](){ bmainmenuf(); }, SDLK_m);
-	mainMenu->setImageOrder(1, 0, 2, 3);
-
-	quitGame = std::make_shared<CButton>(Point(246, 415), "soquit.def", CGI->generaltexth->zelp[324], [&](){ bquitf(); }, SDLK_q);
-	quitGame->setImageOrder(1, 0, 2, 3);
-
-	backToMap = std::make_shared<CButton>( Point(357, 415), "soretrn.def", CGI->generaltexth->zelp[325], [&](){ breturnf(); }, SDLK_RETURN);
-	backToMap->setImageOrder(1, 0, 2, 3);
-	backToMap->assignedKeys.insert(SDLK_ESCAPE);
-
-	heroMoveSpeed = std::make_shared<CToggleGroup>(0);
-	enemyMoveSpeed = std::make_shared<CToggleGroup>(0);
-	mapScrollSpeed = std::make_shared<CToggleGroup>(0);
-
-	heroMoveSpeed->addToggle(1, std::make_shared<CToggleButton>(Point( 28, 77), "sysopb1.def", CGI->generaltexth->zelp[349]));
-	heroMoveSpeed->addToggle(2, std::make_shared<CToggleButton>(Point( 76, 77), "sysopb2.def", CGI->generaltexth->zelp[350]));
-	heroMoveSpeed->addToggle(4, std::make_shared<CToggleButton>(Point(124, 77), "sysopb3.def", CGI->generaltexth->zelp[351]));
-	heroMoveSpeed->addToggle(8, std::make_shared<CToggleButton>(Point(172, 77), "sysopb4.def", CGI->generaltexth->zelp[352]));
-	heroMoveSpeed->setSelected((int)settings["adventure"]["heroSpeed"].Float());
-	heroMoveSpeed->addCallback(std::bind(&setIntSetting, "adventure", "heroSpeed", _1));
-
-	enemyMoveSpeed->addToggle(2, std::make_shared<CToggleButton>(Point( 28, 144), "sysopb5.def", CGI->generaltexth->zelp[353]));
-	enemyMoveSpeed->addToggle(4, std::make_shared<CToggleButton>(Point( 76, 144), "sysopb6.def", CGI->generaltexth->zelp[354]));
-	enemyMoveSpeed->addToggle(8, std::make_shared<CToggleButton>(Point(124, 144), "sysopb7.def", CGI->generaltexth->zelp[355]));
-	enemyMoveSpeed->addToggle(0, std::make_shared<CToggleButton>(Point(172, 144), "sysopb8.def", CGI->generaltexth->zelp[356]));
-	enemyMoveSpeed->setSelected((int)settings["adventure"]["enemySpeed"].Float());
-	enemyMoveSpeed->addCallback(std::bind(&setIntSetting, "adventure", "enemySpeed", _1));
-
-	mapScrollSpeed->addToggle(1, std::make_shared<CToggleButton>(Point( 28, 210), "sysopb9.def", CGI->generaltexth->zelp[357]));
-	mapScrollSpeed->addToggle(2, std::make_shared<CToggleButton>(Point( 92, 210), "sysob10.def", CGI->generaltexth->zelp[358]));
-	mapScrollSpeed->addToggle(4, std::make_shared<CToggleButton>(Point(156, 210), "sysob11.def", CGI->generaltexth->zelp[359]));
-	mapScrollSpeed->setSelected((int)settings["adventure"]["scrollSpeed"].Float());
-	mapScrollSpeed->addCallback(std::bind(&setIntSetting, "adventure", "scrollSpeed", _1));
-
-	musicVolume = std::make_shared<CVolumeSlider>(Point(29, 359), "syslb.def", CCS->musich->getVolume(), CVolumeSlider::MUSIC);
-	musicVolume->addCallback(std::bind(&setIntSetting, "general", "music", _1));
-
-	effectsVolume = std::make_shared<CVolumeSlider>(Point(29, 425), "syslb.def", CCS->soundh->getVolume(), CVolumeSlider::SOUND);
-	effectsVolume->addCallback(std::bind(&setIntSetting, "general", "sound", _1));
-
-	showReminder = std::make_shared<CToggleButton>(Point(246, 87), "sysopchk.def", CGI->generaltexth->zelp[361], [&](bool value)
-	{
-		setBoolSetting("adventure", "heroReminder", value);
-	});
-	showReminder->setSelected(settings["adventure"]["heroReminder"].Bool());
-
-	quickCombat = std::make_shared<CToggleButton>(Point(246, 87+32), "sysopchk.def", CGI->generaltexth->zelp[362], [&](bool value)
-	{
-		setBoolSetting("adventure", "quickCombat", value);
-	});
-	quickCombat->setSelected(settings["adventure"]["quickCombat"].Bool());
-
-	spellbookAnim = std::make_shared<CToggleButton>(Point(246, 87+64), "sysopchk.def", CGI->generaltexth->zelp[364], [&](bool value)
-	{
-		setBoolSetting("video", "spellbookAnimation", value);
-	});
-	spellbookAnim->setSelected(settings["video"]["spellbookAnimation"].Bool());
-
-	fullscreen = std::make_shared<CToggleButton>(Point(246, 215), "sysopchk.def", CButton::tooltipLocalized("vcmi.systemOptions.fullscreenButton"), [&](bool value)
-	{
-		setFullscreenMode(value);
-	});
-	fullscreen->setSelected(settings["video"]["fullscreen"].Bool());
-
-	onFullscreenChanged([&](const JsonNode &newState){ fullscreen->setSelected(newState.Bool());});
-
-	gameResButton = std::make_shared<CButton>(Point(28, 275),"buttons/resolution", CButton::tooltipLocalized("vcmi.systemOptions.resolutionButton"), std::bind(&CSystemOptionsWindow::selectGameRes, this), SDLK_g);
-
-	const auto & screenRes = settings["video"]["screenRes"];
-	gameResLabel = std::make_shared<CLabel>(170, 292, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, resolutionToString(screenRes["width"].Integer(), screenRes["height"].Integer()));
-}
-
-void CSystemOptionsWindow::setFullscreenMode( bool on)
-{
-	fillSelectableResolutions();
-
-	const auto & screenRes = settings["video"]["screenRes"];
-	const Point desiredResolution(screenRes["width"].Integer(), screenRes["height"].Integer());
-	const Point currentResolution(screen->w, screen->h);
-
-	if (!isResolutionSupported(currentResolution, on))
-	{
-		fullscreen->setSelected(!on);
-		LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.systemOptions.fullscreenFailed"));
-		return;
-	}
-
-	setBoolSetting("video", "fullscreen", on);
-
-	if (!isResolutionSupported(desiredResolution, on))
-	{
-		// user changed his desired resolution and switched to fullscreen
-		// however resolution he selected before is not available in fullscreen
-		// so reset it back to currect resolution which is confirmed to be supported earlier
-		Settings gameRes = settings.write["video"]["screenRes"];
-		gameRes["width"].Float() = currentResolution.x;
-		gameRes["height"].Float() = currentResolution.y;
-
-		gameResLabel->setText(resolutionToString(currentResolution.x, currentResolution.y));
-	}
-}
-
-void CSystemOptionsWindow::fillSelectableResolutions()
-{
-	selectableResolutions.clear();
-
-	for(const auto & it : conf.guiOptions)
-	{
-		const Point dimensions(it.first.first, it.first.second);
-
-		if(isResolutionSupported(dimensions))
-			selectableResolutions.push_back(dimensions);
-	}
-
-	boost::range::sort(selectableResolutions, [](const auto & left, const auto & right)
-	{
-		return left.x * left.y < right.x * right.y;
-	});
-}
-
-bool CSystemOptionsWindow::isResolutionSupported(const Point & resolution)
-{
-	return isResolutionSupported( resolution, settings["video"]["fullscreen"].Bool());
-}
-
-bool CSystemOptionsWindow::isResolutionSupported(const Point & resolution, bool fullscreen)
-{
-	if (!fullscreen)
-		return true;
-
-	auto supportedList = CSDL_Ext::getSupportedResolutions();
-
-	return CSDL_Ext::isResolutionSupported(supportedList, resolution);
-}
-
-void CSystemOptionsWindow::selectGameRes()
-{
-	fillSelectableResolutions();
-
-	std::vector<std::string> items;
-	size_t currentResolutionIndex = 0;
-	size_t i = 0;
-	for(const auto & it : selectableResolutions)
-	{
-		auto resolutionStr = resolutionToString(it.x, it.y);
-		if(gameResLabel->getText() == resolutionStr)
-			currentResolutionIndex = i;
-
-		items.push_back(std::move(resolutionStr));
-		++i;
-	}
-
-	GH.pushIntT<CObjectListWindow>(items, nullptr,
-								   CGI->generaltexth->translate("vcmi.systemOptions.resolutionMenu.hover"),
-								   CGI->generaltexth->translate("vcmi.systemOptions.resolutionMenu.help"),
-								   std::bind(&CSystemOptionsWindow::setGameRes, this, _1),
-								   currentResolutionIndex);
-}
-
-void CSystemOptionsWindow::setGameRes(int index)
-{
-	assert(index >= 0 && index < selectableResolutions.size());
-
-	if ( index < 0 || index >= selectableResolutions.size() )
-		return;
-
-	Point resolution = selectableResolutions[index];
-
-	Settings gameRes = settings.write["video"]["screenRes"];
-	gameRes["width"].Float() = resolution.x;
-	gameRes["height"].Float() = resolution.y;
-
-	std::string resText;
-	resText += std::to_string(resolution.x);
-	resText += "x";
-	resText += std::to_string(resolution.y);
-	gameResLabel->setText(resText);
-}
-
-void CSystemOptionsWindow::bquitf()
-{
-	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], [this](){ closeAndPushEvent(EUserEvent::FORCE_QUIT); }, 0);
-}
-
-void CSystemOptionsWindow::breturnf()
-{
-	close();
-}
-
-void CSystemOptionsWindow::bmainmenuf()
-{
-	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], [this](){ closeAndPushEvent(EUserEvent::RETURN_TO_MAIN_MENU); }, 0);
-}
-
-void CSystemOptionsWindow::bloadf()
-{
-	close();
-	LOCPLINT->proposeLoadingGame();
-}
-
-void CSystemOptionsWindow::bsavef()
-{
-	close();
-	GH.pushIntT<CSavingScreen>();
-}
-
-void CSystemOptionsWindow::brestartf()
-{
-	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [this](){ closeAndPushEvent(EUserEvent::RESTART_GAME); }, 0);
-}
-
-void CSystemOptionsWindow::closeAndPushEvent(EUserEvent code)
-{
-	close();
-	GH.pushUserEvent(code);
-}
 
 CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj)
 	: CStatusbarWindow(PLAYER_COLORED, "TPTAVERN"),

+ 0 - 52
client/windows/GUIClasses.h

@@ -198,58 +198,6 @@ public:
 	void keyPressed(const SDL_Keycode & key) override;
 };
 
-class CSystemOptionsWindow : public CWindowObject
-{
-private:
-	std::shared_ptr<CLabel> title;
-	std::shared_ptr<CLabelGroup> leftGroup;
-	std::shared_ptr<CLabelGroup> rightGroup;
-	std::shared_ptr<CButton> load;
-	std::shared_ptr<CButton> save;
-	std::shared_ptr<CButton> restart;
-	std::shared_ptr<CButton> mainMenu;
-	std::shared_ptr<CButton> quitGame;
-	std::shared_ptr<CButton> backToMap; //load and restart are not used yet
-	std::shared_ptr<CToggleGroup> heroMoveSpeed;
-	std::shared_ptr<CToggleGroup> enemyMoveSpeed;
-	std::shared_ptr<CToggleGroup> mapScrollSpeed;
-	std::shared_ptr<CVolumeSlider> musicVolume;
-	std::shared_ptr<CVolumeSlider> effectsVolume;
-
-	std::shared_ptr<CToggleButton> showReminder;
-	std::shared_ptr<CToggleButton> quickCombat;
-	std::shared_ptr<CToggleButton> spellbookAnim;
-	std::shared_ptr<CToggleButton> fullscreen;
-
-	std::shared_ptr<CButton> gameResButton;
-	std::shared_ptr<CLabel> gameResLabel;
-
-	SettingsListener onFullscreenChanged;
-
-	std::vector<Point> supportedResolutions;
-	std::vector<Point> selectableResolutions;
-
-	//functions bound to buttons
-	void bloadf(); //load game
-	void bsavef(); //save game
-	void bquitf(); //quit game
-	void breturnf(); //return to game
-	void brestartf(); //restart game
-	void bmainmenuf(); //return to main menu
-
-	void setFullscreenMode( bool on);
-	void fillSelectableResolutions();
-	bool isResolutionSupported(const Point & resolution);
-	bool isResolutionSupported(const Point & resolution, bool fullscreen);
-
-	void selectGameRes();
-	void setGameRes(int index);
-	void closeAndPushEvent(EUserEvent code);
-
-public:
-	CSystemOptionsWindow();
-};
-
 class CTavernWindow : public CStatusbarWindow
 {
 public:

+ 96 - 0
client/windows/settings/AdventureOptionsTab.cpp

@@ -0,0 +1,96 @@
+/*
+ * AdventureOptionsTab.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+
+#include "AdventureOptionsTab.h"
+
+#include "../../../lib/filesystem/ResourceID.h"
+#include "../../gui/CGuiHandler.h"
+#include "../../widgets/Buttons.h"
+#include "../../widgets/TextControls.h"
+#include "../../widgets/Images.h"
+#include "CConfigHandler.h"
+
+static void setBoolSetting(std::string group, std::string field, bool value)
+{
+	Settings fullscreen = settings.write[group][field];
+	fullscreen->Bool() = value;
+}
+
+static void setIntSetting(std::string group, std::string field, int value)
+{
+	Settings entry = settings.write[group][field];
+	entry->Float() = value;
+}
+
+AdventureOptionsTab::AdventureOptionsTab()
+		: InterfaceObjectConfigurable()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
+	const JsonNode config(ResourceID("config/widgets/settings/adventureOptionsTab.json"));
+	addCallback("playerHeroSpeedChanged", [](int value)
+	{
+		return setIntSetting("adventure", "heroSpeed", value);
+	});
+	addCallback("enemyHeroSpeedChanged", [](int value)
+	{
+		return setIntSetting("adventure", "enemySpeed", value);
+	});
+	addCallback("mapScrollSpeedChanged", [](int value)
+	{
+		return setIntSetting("adventure", "scrollSpeed", value);
+	});
+	addCallback("heroReminderChanged", [](bool value)
+	{
+		return setBoolSetting("adventure", "heroReminder", value);
+	});
+	addCallback("quickCombatChanged", [](bool value)
+	{
+		return setBoolSetting("adventure", "quickCombat", value);
+	});
+	//settings that do not belong to base game:
+	addCallback("numericQuantitiesChanged", [](bool value)
+	{
+		return setBoolSetting("gameTweaks", "numericCreaturesQuantities", value);
+	});
+	addCallback("forceMovementInfoChanged", [](bool value)
+	{
+		return setBoolSetting("gameTweaks", "forceMovementInfo", value);
+	});
+	addCallback("showGridChanged", [](bool value)
+	{
+		return setBoolSetting("gameTweaks", "showGrid", value);
+	});
+	build(config);
+
+	std::shared_ptr<CToggleGroup> playerHeroSpeedToggle = widget<CToggleGroup>("heroMovementSpeedPicker");
+	playerHeroSpeedToggle->setSelected(static_cast<int>(settings["adventure"]["heroSpeed"].Float()));
+
+	std::shared_ptr<CToggleGroup> enemyHeroSpeedToggle = widget<CToggleGroup>("enemyMovementSpeedPicker");
+	enemyHeroSpeedToggle->setSelected(static_cast<int>(settings["adventure"]["enemySpeed"].Float()));
+
+	std::shared_ptr<CToggleGroup> mapScrollSpeedToggle = widget<CToggleGroup>("mapScrollSpeedPicker");
+	mapScrollSpeedToggle->setSelected(static_cast<int>(settings["adventure"]["scrollSpeed"].Float()));
+
+	std::shared_ptr<CToggleButton> heroReminderCheckbox = widget<CToggleButton>("heroReminderCheckbox");
+	heroReminderCheckbox->setSelected(settings["adventure"]["heroReminder"].Bool());
+
+	std::shared_ptr<CToggleButton> quickCombatCheckbox = widget<CToggleButton>("quickCombatCheckbox");
+	quickCombatCheckbox->setSelected(settings["adventure"]["quickCombat"].Bool());
+
+	std::shared_ptr<CToggleButton> numericQuantitiesCheckbox = widget<CToggleButton>("numericQuantitiesCheckbox");
+	numericQuantitiesCheckbox->setSelected(settings["gameTweaks"]["numericCreaturesQuantities"].Bool());
+
+	std::shared_ptr<CToggleButton> forceMovementInfoCheckbox = widget<CToggleButton>("forceMovementInfoCheckbox");
+	forceMovementInfoCheckbox->setSelected(settings["gameTweaks"]["forceMovementInfo"].Bool());
+
+	std::shared_ptr<CToggleButton> showGridCheckbox = widget<CToggleButton>("showGridCheckbox");
+	showGridCheckbox->setSelected(settings["gameTweaks"]["showGrid"].Bool());
+}

+ 19 - 0
client/windows/settings/AdventureOptionsTab.h

@@ -0,0 +1,19 @@
+/*
+ * AdventureOptionsTab.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../../gui/InterfaceObjectConfigurable.h"
+
+class AdventureOptionsTab : public InterfaceObjectConfigurable
+{
+public:
+	AdventureOptionsTab();
+};
+

+ 169 - 0
client/windows/settings/BattleOptionsTab.cpp

@@ -0,0 +1,169 @@
+/*
+ * BattleOptionsTab.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+
+#include "BattleOptionsTab.h"
+#include "CConfigHandler.h"
+
+#include "../../battle/BattleInterface.h"
+#include "../../gui/CGuiHandler.h"
+#include "../../../lib/filesystem/ResourceID.h"
+#include "../../../lib/CGeneralTextHandler.h"
+#include "../../widgets/Buttons.h"
+#include "../../widgets/TextControls.h"
+
+BattleOptionsTab::BattleOptionsTab(BattleInterface * owner):
+		InterfaceObjectConfigurable()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
+
+	const JsonNode config(ResourceID("config/widgets/settings/battleOptionsTab.json"));
+	addCallback("viewGridChanged", [this, owner](bool value)
+	{
+		viewGridChangedCallback(value, owner);
+	});
+	addCallback("movementShadowChanged", [this, owner](bool value)
+	{
+		movementShadowChangedCallback(value, owner);
+	});
+	addCallback("mouseShadowChanged", [this](bool value)
+	{
+		mouseShadowChangedCallback(value);
+	});
+	addCallback("animationSpeedChanged", [this](int value)
+	{
+		animationSpeedChangedCallback(value);
+	});
+	addCallback("showQueueChanged", [this, owner](bool value)
+	{
+		showQueueChangedCallback(value, owner);
+	});
+	addCallback("queueSizeChanged", [this](int value)
+	{
+		queueSizeChangedCallback(value);
+	});
+	addCallback("skipBattleIntroMusicChanged", [this](bool value)
+	{
+		skipBattleIntroMusicChangedCallback(value);
+	});
+	build(config);
+
+	std::shared_ptr<CToggleGroup> animationSpeedToggle = widget<CToggleGroup>("animationSpeedPicker");
+	animationSpeedToggle->setSelected(getAnimSpeed());
+
+	std::shared_ptr<CToggleGroup> queueSizeToggle = widget<CToggleGroup>("queueSizePicker");
+	queueSizeToggle->setSelected(getQueueSizeId());
+
+	std::shared_ptr<CToggleButton> viewGridCheckbox = widget<CToggleButton>("viewGridCheckbox");
+	viewGridCheckbox->setSelected(settings["battle"]["cellBorders"].Bool());
+
+	std::shared_ptr<CToggleButton> movementShadowCheckbox = widget<CToggleButton>("movementShadowCheckbox");
+	movementShadowCheckbox->setSelected(settings["battle"]["stackRange"].Bool());
+
+	std::shared_ptr<CToggleButton> mouseShadowCheckbox = widget<CToggleButton>("mouseShadowCheckbox");
+	mouseShadowCheckbox->setSelected(settings["battle"]["mouseShadow"].Bool());
+
+	std::shared_ptr<CToggleButton> showQueueCheckbox = widget<CToggleButton>("showQueueCheckbox");
+	showQueueCheckbox->setSelected(settings["battle"]["showQueue"].Bool());
+
+	std::shared_ptr<CToggleButton> skipBattleIntroMusicCheckbox = widget<CToggleButton>("skipBattleIntroMusicCheckbox");
+	skipBattleIntroMusicCheckbox->setSelected(settings["gameTweaks"]["skipBattleIntroMusic"].Bool());
+}
+
+int BattleOptionsTab::getAnimSpeed() const
+{
+	if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-battle-speed"].isNull())
+		return static_cast<int>(std::round(settings["session"]["spectate-battle-speed"].Float()));
+
+	return static_cast<int>(std::round(settings["battle"]["speedFactor"].Float()));
+}
+
+int BattleOptionsTab::getQueueSizeId() const
+{
+	std::string text = settings["battle"]["queueSize"].String();
+	if(text == "auto")
+		return 0;
+	else if(text == "small")
+		return 1;
+	else if(text == "big")
+		return 2;
+
+	return 0;
+}
+
+std::string BattleOptionsTab::getQueueSizeStringFromId(int value) const
+{
+	switch(value)
+	{
+		case 0:
+			return "auto";
+		case 1:
+			return "small";
+		case 2:
+			return "big";
+		default:
+			return "auto";
+	}
+}
+
+void BattleOptionsTab::viewGridChangedCallback(bool value, BattleInterface * parentBattleInterface)
+{
+	Settings cellBorders = settings.write["battle"]["cellBorders"];
+	cellBorders->Bool() = value;
+	if(parentBattleInterface)
+		parentBattleInterface->redrawBattlefield();
+}
+
+void BattleOptionsTab::movementShadowChangedCallback(bool value, BattleInterface * parentBattleInterface)
+{
+	Settings stackRange = settings.write["battle"]["stackRange"];
+	stackRange->Bool() = value;
+	if(parentBattleInterface)
+		parentBattleInterface->redrawBattlefield();
+}
+
+void BattleOptionsTab::mouseShadowChangedCallback(bool value)
+{
+	Settings shadow = settings.write["battle"]["mouseShadow"];
+	shadow->Bool() = value;
+}
+
+void BattleOptionsTab::animationSpeedChangedCallback(int value)
+{
+	Settings speed = settings.write["battle"]["speedFactor"];
+	speed->Float() = static_cast<float>(value);
+}
+
+void BattleOptionsTab::showQueueChangedCallback(bool value, BattleInterface * parentBattleInterface)
+{
+	if(!parentBattleInterface)
+	{
+		Settings showQueue = settings.write["battle"]["showQueue"];
+		showQueue->Bool() = value;
+	}
+	else
+	{
+		parentBattleInterface->setBattleQueueVisibility(value);
+	}
+}
+
+void BattleOptionsTab::queueSizeChangedCallback(int value)
+{
+	std::string stringifiedValue = getQueueSizeStringFromId(value);
+	Settings size = settings.write["battle"]["queueSize"];
+	size->String() = stringifiedValue;
+}
+
+void BattleOptionsTab::skipBattleIntroMusicChangedCallback(bool value)
+{
+	Settings musicSkipSettingValue = settings.write["gameTweaks"]["skipBattleIntroMusic"];
+	musicSkipSettingValue->Bool() = value;
+}
+

+ 36 - 0
client/windows/settings/BattleOptionsTab.h

@@ -0,0 +1,36 @@
+/*
+ * BattleOptionsTab.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../../gui/InterfaceObjectConfigurable.h"
+
+class BattleInterface;
+
+class BattleOptionsTab : public InterfaceObjectConfigurable
+{
+private:
+	std::shared_ptr<CToggleGroup> animSpeeds;
+	std::vector<std::shared_ptr<CToggleButton>> toggles;
+
+	int getAnimSpeed() const;
+	int getQueueSizeId() const;
+	std::string getQueueSizeStringFromId(int value) const;
+	void viewGridChangedCallback(bool value, BattleInterface * parentBattleInterface = nullptr);
+	void movementShadowChangedCallback(bool value, BattleInterface * parentBattleInterface = nullptr);
+	void mouseShadowChangedCallback(bool value);
+	void animationSpeedChangedCallback(int value);
+	void showQueueChangedCallback(bool value, BattleInterface * parentBattleInterface = nullptr);
+	void queueSizeChangedCallback(int value);
+	void skipBattleIntroMusicChangedCallback(bool value);
+public:
+	BattleOptionsTab(BattleInterface * owner = nullptr);
+};
+
+

+ 229 - 0
client/windows/settings/GeneralOptionsTab.cpp

@@ -0,0 +1,229 @@
+/*
+ * GeneralOptionsTab.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+
+#include "GeneralOptionsTab.h"
+
+#include "../../../lib/CGeneralTextHandler.h"
+#include "../../../lib/filesystem/ResourceID.h"
+#include "../../gui/CGuiHandler.h"
+#include "../../widgets/Buttons.h"
+#include "../../widgets/TextControls.h"
+#include "../../widgets/Images.h"
+#include "CGameInfo.h"
+#include "CMusicHandler.h"
+#include "CPlayerInterface.h"
+#include "windows/GUIClasses.h"
+#include "CServerHandler.h"
+#include "renderSDL/SDL_Extensions.h"
+
+
+static void setIntSetting(std::string group, std::string field, int value)
+{
+	Settings entry = settings.write[group][field];
+	entry->Float() = value;
+}
+
+static void setBoolSetting(std::string group, std::string field, bool value)
+{
+	Settings fullscreen = settings.write[group][field];
+	fullscreen->Bool() = value;
+}
+
+static std::string resolutionToString(int w, int h)
+{
+	return std::to_string(w) + 'x' + std::to_string(h);
+}
+
+GeneralOptionsTab::GeneralOptionsTab()
+		: InterfaceObjectConfigurable(),
+		  onFullscreenChanged(settings.listen["video"]["fullscreen"])
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
+
+	const JsonNode config(ResourceID("config/widgets/settings/generalOptionsTab.json"));
+	addCallback("spellbookAnimationChanged", [](bool value)
+	{
+		return setBoolSetting("video", "spellbookAnimation", value);
+	});
+	addCallback("setMusic", [this](int value)
+	{
+		setIntSetting("general", "music", value);
+		widget<CSlider>("musicSlider")->redraw();
+	});
+	addCallback("setVolume", [this](int value)
+	{
+		setIntSetting("general", "sound", value);
+		widget<CSlider>("soundVolumeSlider")->redraw();
+	});
+	//settings that do not belong to base game:
+	addCallback("fullscreenChanged", [this](bool value)
+	{
+		setFullscreenMode(value);
+	});
+	addCallback("setGameResolution", [this](int dummyValue)
+	{
+		selectGameResolution();
+	});
+	addCallback("framerateChanged", [](bool value)
+	{
+		setBoolSetting("general", "showfps", value);
+	});
+	//moved from "other" tab that is disabled for now to avoid excessible tabs with barely any content
+	addCallback("availableCreaturesAsDwellingLabelChanged", [](bool value)
+	{
+		setBoolSetting("gameTweaks", "availableCreaturesAsDwellingLabel", value);
+	});
+	addCallback("compactTownCreatureInfoChanged", [](bool value)
+	{
+		return setBoolSetting("gameTweaks", "compactTownCreatureInfo", value);
+	});
+	build(config);
+
+	std::shared_ptr<CLabel> resolutionLabel = widget<CLabel>("resolutionLabel");
+	const auto & currentResolution = settings["video"]["screenRes"];
+	resolutionLabel->setText(resolutionToString(currentResolution["width"].Integer(), currentResolution["height"].Integer()));
+
+
+	std::shared_ptr<CToggleButton> spellbookAnimationCheckbox = widget<CToggleButton>("spellbookAnimationCheckbox");
+	spellbookAnimationCheckbox->setSelected(settings["video"]["spellbookAnimation"].Bool());
+
+	std::shared_ptr<CToggleButton> fullscreenCheckbox = widget<CToggleButton>("fullscreenCheckbox");
+	fullscreenCheckbox->setSelected(settings["video"]["fullscreen"].Bool());
+	onFullscreenChanged([&](const JsonNode &newState) //used when pressing F4 etc. to change fullscreen checkbox state
+	{
+		widget<CToggleButton>("fullscreenCheckbox")->setSelected(newState.Bool());
+	});
+
+	std::shared_ptr<CToggleButton> framerateCheckbox = widget<CToggleButton>("framerateCheckbox");
+	framerateCheckbox->setSelected(settings["general"]["showfps"].Bool());
+
+	std::shared_ptr<CSlider> musicSlider = widget<CSlider>("musicSlider");
+	musicSlider->moveTo(CCS->musich->getVolume());
+
+	std::shared_ptr<CSlider> volumeSlider = widget<CSlider>("soundVolumeSlider");
+	volumeSlider->moveTo(CCS->soundh->getVolume());
+
+	std::shared_ptr<CToggleButton> availableCreaturesAsDwellingLabelCheckbox = widget<CToggleButton>("availableCreaturesAsDwellingLabelCheckbox");
+	availableCreaturesAsDwellingLabelCheckbox->setSelected(settings["gameTweaks"]["availableCreaturesAsDwellingLabel"].Bool());
+
+	std::shared_ptr<CToggleButton> compactTownCreatureInfo = widget<CToggleButton>("compactTownCreatureInfoCheckbox");
+	compactTownCreatureInfo->setSelected(settings["gameTweaks"]["compactTownCreatureInfo"].Bool());
+}
+
+
+bool GeneralOptionsTab::isResolutionSupported(const Point & resolution)
+{
+	return isResolutionSupported( resolution, settings["video"]["fullscreen"].Bool());
+}
+
+bool GeneralOptionsTab::isResolutionSupported(const Point & resolution, bool fullscreen)
+{
+	if (!fullscreen)
+		return true;
+
+	auto supportedList = CSDL_Ext::getSupportedResolutions();
+
+	return CSDL_Ext::isResolutionSupported(supportedList, resolution);
+}
+
+void GeneralOptionsTab::selectGameResolution()
+{
+	fillSelectableResolutions();
+
+	std::vector<std::string> items;
+	size_t currentResolutionIndex = 0;
+	size_t i = 0;
+	for(const auto & it : selectableResolutions)
+	{
+		auto resolutionStr = resolutionToString(it.x, it.y);
+		if(widget<CLabel>("resolutionLabel")->getText() == resolutionStr)
+			currentResolutionIndex = i;
+
+		items.push_back(std::move(resolutionStr));
+		++i;
+	}
+	GH.pushIntT<CObjectListWindow>(items, nullptr,
+								   CGI->generaltexth->translate("vcmi.systemOptions.resolutionMenu.hover"),
+								   CGI->generaltexth->translate("vcmi.systemOptions.resolutionMenu.help"),
+								   [this](int index)
+								   {
+									   setGameResolution(index);
+								   },
+								   currentResolutionIndex);
+}
+
+void GeneralOptionsTab::setGameResolution(int index)
+{
+	assert(index >= 0 && index < selectableResolutions.size());
+
+	if ( index < 0 || index >= selectableResolutions.size() )
+		return;
+
+	Point resolution = selectableResolutions[index];
+
+	Settings gameRes = settings.write["video"]["screenRes"];
+	gameRes["width"].Float() = resolution.x;
+	gameRes["height"].Float() = resolution.y;
+
+	std::string resText;
+	resText += std::to_string(resolution.x);
+	resText += "x";
+	resText += std::to_string(resolution.y);
+	widget<CLabel>("resolutionLabel")->setText(resText);
+}
+
+void GeneralOptionsTab::setFullscreenMode(bool on)
+{
+	fillSelectableResolutions();
+
+	const auto & screenRes = settings["video"]["screenRes"];
+	const Point desiredResolution(screenRes["width"].Integer(), screenRes["height"].Integer());
+	const Point currentResolution = GH.screenDimensions();
+
+	if (!isResolutionSupported(currentResolution, on))
+	{
+		widget<CToggleButton>("fullscreenCheckbox")->setSelected(!on);
+		LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.systemOptions.fullscreenFailed"));
+		return;
+	}
+
+	setBoolSetting("video", "fullscreen", on);
+
+	if (!isResolutionSupported(desiredResolution, on))
+	{
+		// user changed his desired resolution and switched to fullscreen
+		// however resolution he selected before is not available in fullscreen
+		// so reset it back to currect resolution which is confirmed to be supported earlier
+		Settings gameRes = settings.write["video"]["screenRes"];
+		gameRes["width"].Float() = currentResolution.x;
+		gameRes["height"].Float() = currentResolution.y;
+
+		widget<CLabel>("resolutionLabel")->setText(resolutionToString(currentResolution.x, currentResolution.y));
+	}
+}
+
+void GeneralOptionsTab::fillSelectableResolutions()
+{
+	selectableResolutions.clear();
+
+	for(const auto & it : conf.guiOptions)
+	{
+		const Point dimensions(it.first.first, it.first.second);
+
+		if(isResolutionSupported(dimensions))
+			selectableResolutions.push_back(dimensions);
+	}
+
+	boost::range::sort(selectableResolutions, [](const auto & left, const auto & right)
+	{
+		return left.x * left.y < right.x * right.y;
+	});
+}

+ 34 - 0
client/windows/settings/GeneralOptionsTab.h

@@ -0,0 +1,34 @@
+/*
+ * GeneralOptionsTab.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../../../lib/CConfigHandler.h"
+#include "../../gui/InterfaceObjectConfigurable.h"
+
+
+class GeneralOptionsTab : public InterfaceObjectConfigurable
+{
+private:
+	SettingsListener onFullscreenChanged;
+
+	std::vector<Point> supportedResolutions;
+	std::vector<Point> selectableResolutions;
+
+	void setFullscreenMode( bool on);
+	void fillSelectableResolutions();
+	bool isResolutionSupported(const Point & resolution);
+	bool isResolutionSupported(const Point & resolution, bool fullscreen);
+
+	void selectGameResolution();
+	void setGameResolution(int index);
+
+public:
+	GeneralOptionsTab();
+};

+ 47 - 0
client/windows/settings/OtherOptionsTab.cpp

@@ -0,0 +1,47 @@
+/*
+ * VcmiSettingsWindow.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+
+#include "OtherOptionsTab.h"
+
+#include "../../../lib/filesystem/ResourceID.h"
+#include "../../gui/CGuiHandler.h"
+#include "../../widgets/Buttons.h"
+#include "CConfigHandler.h"
+
+static void setBoolSetting(std::string group, std::string field, bool value)
+{
+	Settings fullscreen = settings.write[group][field];
+	fullscreen->Bool() = value;
+}
+
+OtherOptionsTab::OtherOptionsTab() : InterfaceObjectConfigurable()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
+
+	const JsonNode config(ResourceID("config/widgets/settings/otherOptionsTab.json"));
+	addCallback("availableCreaturesAsDwellingLabelChanged", [](bool value)
+	{
+		return setBoolSetting("gameTweaks", "availableCreaturesAsDwellingLabel", value);
+	});
+	addCallback("compactTownCreatureInfoChanged", [](bool value)
+	{
+		return setBoolSetting("gameTweaks", "compactTownCreatureInfo", value);
+	});
+	build(config);
+
+	std::shared_ptr<CToggleButton> availableCreaturesAsDwellingLabelCheckbox = widget<CToggleButton>("availableCreaturesAsDwellingLabelCheckbox");
+	availableCreaturesAsDwellingLabelCheckbox->setSelected(settings["gameTweaks"]["availableCreaturesAsDwellingLabel"].Bool());
+
+	std::shared_ptr<CToggleButton> compactTownCreatureInfo = widget<CToggleButton>("compactTownCreatureInfoCheckbox");
+	compactTownCreatureInfo->setSelected(settings["gameTweaks"]["compactTownCreatureInfo"].Bool());
+}
+
+

+ 20 - 0
client/windows/settings/OtherOptionsTab.h

@@ -0,0 +1,20 @@
+/*
+ * VcmiSettingsWindow.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../../gui/InterfaceObjectConfigurable.h"
+
+class OtherOptionsTab : public InterfaceObjectConfigurable
+{
+private:
+
+public:
+	OtherOptionsTab();
+};

+ 150 - 0
client/windows/settings/SettingsMainWindow.cpp

@@ -0,0 +1,150 @@
+/*
+ * SettingsMainContainer.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+
+#include "SettingsMainWindow.h"
+
+#include "GeneralOptionsTab.h"
+#include "AdventureOptionsTab.h"
+#include "BattleOptionsTab.h"
+#include "OtherOptionsTab.h"
+
+#include "filesystem/ResourceID.h"
+#include "CGeneralTextHandler.h"
+#include "gui/CGuiHandler.h"
+#include "lobby/CSavingScreen.h"
+#include "widgets/Buttons.h"
+#include "widgets/Images.h"
+#include "widgets/ObjectLists.h"
+#include "CGameInfo.h"
+#include "CPlayerInterface.h"
+#include "CServerHandler.h"
+
+
+SettingsMainWindow::SettingsMainWindow(BattleInterface * parentBattleUi) : InterfaceObjectConfigurable()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
+
+	const JsonNode config(ResourceID("config/widgets/settings/settingsMainContainer.json"));
+	addCallback("activateSettingsTab", [this](int tabId) { openTab(tabId); });
+	addCallback("loadGame", [this](int) { loadGameButtonCallback(); });
+	addCallback("saveGame", [this](int) { saveGameButtonCallback(); });
+	addCallback("restartGame", [this](int) { restartGameButtonCallback(); });
+	addCallback("quitGame", [this](int) { quitGameButtonCallback(); });
+	addCallback("returnToMainMenu", [this](int) { mainMenuButtonCallback(); });
+	addCallback("closeWindow", [this](int) { backButtonCallback(); });
+	build(config);
+
+	std::shared_ptr<CPicture> background = widget<CPicture>("background");
+	pos.w = background->pos.w;
+	pos.h = background->pos.h;
+	pos = center();
+
+	std::shared_ptr<CButton> loadButton = widget<CButton>("loadButton");
+	assert(loadButton);
+
+	std::shared_ptr<CButton> saveButton = widget<CButton>("saveButton");
+	assert(saveButton);
+
+	std::shared_ptr<CButton> restartButton = widget<CButton>("restartButton");
+	assert(restartButton);
+
+	if(CSH->isGuest())
+	{
+		loadButton->block(true);
+		saveButton->block(true);
+		restartButton->block(true);
+	}
+
+	int defaultTabIndex = 0;
+	if(parentBattleUi != nullptr)
+		defaultTabIndex = 2;
+	else if(settings["general"]["lastSettingsTab"].isNumber())
+		defaultTabIndex = settings["general"]["lastSettingsTab"].Integer();
+
+	parentBattleInterface = parentBattleUi;
+	tabContentArea = std::make_shared<CTabbedInt>(std::bind(&SettingsMainWindow::createTab, this, _1), Point(0, 0), defaultTabIndex);
+
+	std::shared_ptr<CToggleGroup> mainTabs = widget<CToggleGroup>("settingsTabs");
+	mainTabs->setSelected(defaultTabIndex);
+}
+
+std::shared_ptr<CIntObject> SettingsMainWindow::createTab(size_t index)
+{
+	switch(index)
+	{
+		case 0:
+			return std::make_shared<GeneralOptionsTab>();
+		case 1:
+			return std::make_shared<AdventureOptionsTab>();
+		case 2:
+			return std::make_shared<BattleOptionsTab>(parentBattleInterface);
+		case 3:
+			return std::make_shared<OtherOptionsTab>();
+		default:
+			logGlobal->error("Wrong settings tab ID!");
+			return std::make_shared<GeneralOptionsTab>();
+	}
+}
+
+void SettingsMainWindow::openTab(size_t index)
+{
+	tabContentArea->setActive(index);
+	CIntObject::redraw();
+
+	Settings lastUsedTab = settings.write["general"]["lastSettingsTab"];
+	lastUsedTab->Integer() = index;
+}
+
+void SettingsMainWindow::close()
+{
+	if(GH.topInt().get() != this)
+		logGlobal->error("Only top interface must be closed");
+	GH.popInts(1);
+}
+
+void SettingsMainWindow::quitGameButtonCallback()
+{
+	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], [this](){ closeAndPushEvent(EUserEvent::FORCE_QUIT); }, 0);
+}
+
+void SettingsMainWindow::backButtonCallback()
+{
+	close();
+}
+
+void SettingsMainWindow::mainMenuButtonCallback()
+{
+	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], [this](){ closeAndPushEvent(EUserEvent::RETURN_TO_MAIN_MENU); }, 0);
+}
+
+void SettingsMainWindow::loadGameButtonCallback()
+{
+	close();
+	LOCPLINT->proposeLoadingGame();
+}
+
+void SettingsMainWindow::saveGameButtonCallback()
+{
+	close();
+	GH.pushIntT<CSavingScreen>();
+}
+
+void SettingsMainWindow::restartGameButtonCallback()
+{
+	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [this](){ closeAndPushEvent(EUserEvent::RESTART_GAME); }, 0);
+}
+
+void SettingsMainWindow::closeAndPushEvent(EUserEvent code)
+{
+	close();
+	GH.pushUserEvent(code);
+}

+ 44 - 0
client/windows/settings/SettingsMainWindow.h

@@ -0,0 +1,44 @@
+/*
+ * SettingsMainContainer.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include "gui/InterfaceObjectConfigurable.h"
+
+class BattleInterface;
+class CTabbedInt;
+enum class EUserEvent;
+
+/// <summary>
+///Original H3 settings were replaced with this window in Github PR #1540. New options added are intended to be clientside settings.
+///By design, most new options that alter gameplay UI from now on should be added to "gameTweaks" config key.
+/// </summary>
+class SettingsMainWindow : public InterfaceObjectConfigurable
+{
+private:
+	BattleInterface * parentBattleInterface;
+	std::shared_ptr<CTabbedInt> tabContentArea;
+
+	std::shared_ptr<CIntObject> createTab(size_t index);
+	void openTab(size_t index);
+
+	void close(); //TODO: copypaste of WindowBase::close(), consider changing Windowbase to IWindowbase with default close() implementation and changing WindowBase inheritance to CIntObject + IWindowBase
+	void closeAndPushEvent(EUserEvent code);
+
+	void loadGameButtonCallback();
+	void saveGameButtonCallback();
+	void quitGameButtonCallback();
+	void backButtonCallback();
+	void restartGameButtonCallback();
+	void mainMenuButtonCallback();
+public:
+	SettingsMainWindow(BattleInterface * parentBattleInterface = nullptr);
+};
+

+ 47 - 3
config/schemas/settings.json

@@ -3,7 +3,7 @@
 {
 	"type" : "object",
 	"$schema": "http://json-schema.org/draft-04/schema",
-	"required" : [ "general", "video", "adventure", "pathfinder", "battle", "server", "logging", "launcher" ],
+	"required" : [ "general", "video", "adventure", "pathfinder", "battle", "server", "logging", "launcher", "gameTweaks" ],
 	"definitions" : {
 		"logLevelEnum" : {
 			"type" : "string",
@@ -30,7 +30,8 @@
 				"notifications",
 				"extraDump",
 				"userRelativePointer",
-				"relativePointerSpeedMultiplier"
+				"relativePointerSpeedMultiplier",
+				"lastSettingsTab"
 			],
 			"properties" : {
 				"playerName" : {
@@ -74,6 +75,10 @@
 					"type":"string",
 					"default" : "NEWGAME"
 				},
+				"lastSettingsTab" : {
+					"type" : "number",
+					"default" : 0
+				},
 				"lastCampaign" : {
 					"type":"string",
 					"default" : ""
@@ -421,7 +426,7 @@
 					],
 					"items" : {
 						"type" : "string"
-					},
+					}
 				},
 				"enableInstalledMods" : {
 					"type" : "boolean",
@@ -464,6 +469,45 @@
 					"default" : 2000
 				}
 			}
+		},
+		"gameTweaks" : {
+			"type": "object",
+			"default": {},
+			"additionalProperties": false,
+			"required": [
+				"showGrid",
+				"forceMovementInfo",
+				"numericCreaturesQuantities",
+				"availableCreaturesAsDwellingLabel",
+				"compactTownCreatureInfo",
+				"skipBattleIntroMusic"
+			],
+			"properties": {
+				"showGrid": {
+					"type": "boolean",
+					"default": false
+				},
+				"forceMovementInfo": {
+					"type": "boolean",
+					"default": false
+				},
+				"numericCreaturesQuantities": {
+					"type": "boolean",
+					"default": false
+				},
+				"availableCreaturesAsDwellingLabel" : {
+					"type" : "boolean",
+					"default" : false
+				},
+				"compactTownCreatureInfo" : {
+					"type" : "boolean",
+					"default" : false
+				},
+				"skipBattleIntroMusic" : {
+					"type" : "boolean",
+					"default" : false
+				}
+			}
 		}
 	}
 }

+ 267 - 0
config/widgets/settings/adventureOptionsTab.json

@@ -0,0 +1,267 @@
+{
+	"items":
+	[
+		{
+			"name": "topSettingsLabels",
+			"type": "labelGroup",
+			"font": "medium",
+			"alignment": "center",
+			"color": "yellow",
+			"items":
+			[
+				{
+					"position": {"x": 122, "y": 52},
+					"text": "core.genrltxt.569"
+				},
+				{
+					"position": {"x": 122, "y": 118},
+					"text": "core.genrltxt.570"
+				},
+				{
+					"position": {"x": 122, "y": 184},
+					"text": "core.genrltxt.571"
+				}
+			]
+		},
+
+		{
+			"name": "heroMovementSpeedPicker",
+			"type": "toggleGroup",
+			"position": {"x": 28, "y": 65},
+			"items":
+			[
+				{
+					"index": 1,
+					"type": "toggleButton",
+					"image": "sysopb1",
+					"help": "core.help.349",
+					"position": {"x": 0, "y": 0}
+				},
+
+				{
+					"index": 2,
+					"type": "toggleButton",
+					"image": "sysopb2",
+					"help": "core.help.350",
+					"position": {"x": 48, "y": 0}
+				},
+
+				{
+					"index": 4,
+					"type": "toggleButton",
+					"image": "sysopb3",
+					"help": "core.help.351",
+					"position": {"x": 96, "y": 0}
+				},
+
+				{
+					"index": 16,
+					"type": "toggleButton",
+					"image": "sysopb4",
+					"help": "core.help.352",
+					"position": {"x": 144, "y": 0}
+				}
+				//barely any difference compared to index 16 for now
+//				{
+//					"index": 9999,
+//					"type": "toggleButton",
+//					"image": "sysopb8",
+//					"help": "core.help.356",
+//					"position": {"x": 192, "y": 0}
+//				}
+			],
+			"callback": "playerHeroSpeedChanged"
+		},
+
+		{
+			"name": "enemyMovementSpeedPicker",
+			"type": "toggleGroup",
+			"position": {"x": 28, "y": 132},
+			"items":
+			[
+				{
+					"index": 2,
+					"type": "toggleButton",
+					"image": "sysopb5",
+					"help": "core.help.353",
+					"position": {"x": 0, "y": 0}
+				},
+
+				{
+					"index": 4,
+					"type": "toggleButton",
+					"image": "sysopb6",
+					"help": "core.help.354",
+					"position": {"x": 48, "y": 0}
+				},
+
+				{
+					"index": 8,
+					"type": "toggleButton",
+					"image": "sysopb7",
+					"help": "core.help.355",
+					"position": {"x": 96, "y": 0}
+				},
+
+				{
+					"index": 0,
+					"type": "toggleButton",
+					"image": "sysopb8",
+					"help": "core.help.356",
+					"position": {"x": 144, "y": 0}
+				}
+			],
+			"callback": "enemyHeroSpeedChanged"
+		},
+
+		{
+			"name": "mapScrollSpeedPicker",
+			"type": "toggleGroup",
+			"position": {"x": 28, "y": 198},
+			"items":
+			[
+				{
+					"index": 1,
+					"type": "toggleButton",
+					"image": "sysopb9",
+					"help": "core.help.357",
+					"position": {"x": 0, "y": 0}
+				},
+
+				{
+					"index": 2,
+					"type": "toggleButton",
+					"image": "sysob10",
+					"help": "core.help.358",
+					"position": {"x": 64, "y": 0}
+				},
+
+				{
+					"index": 4,
+					"type": "toggleButton",
+					"image": "sysob11",
+					"help": "core.help.359",
+					"position": {"x": 128, "y": 0}
+				}
+
+				//not really functional yet...
+//				{
+//					"index": 6,
+//					"type": "toggleButton",
+//					"image": "buttons/square",
+//					"help": "vcmi.adventureOptions.mapScrollSpeed4",
+//					"position": {"x": 192, "y": 0},
+//					"items":
+//					[
+//						{
+//							"name": "mapScrollSpeed4",
+//							"type": "label",
+//							"font": "big",
+//							"alignment": "center",
+//							"color": "yellow",
+//							"text": "vcmi.adventureOptions.mapScrollSpeed4.hover"
+//						}
+//					]
+//				},
+//				{
+//					"index": 8,
+//					"type": "toggleButton",
+//					"image": "buttons/square",
+//					"help": "vcmi.adventureOptions.mapScrollSpeed5",
+//					"position": {"x": 224, "y": 0},
+//					"items":
+//					[
+//						{
+//							"name": "mapScrollSpeed5",
+//							"type": "label",
+//							"font": "big",
+//							"alignment": "center",
+//							"color": "yellow",
+//							"text": "vcmi.adventureOptions.mapScrollSpeed5.hover"
+//						}
+//					]
+//				}
+			],
+			"callback": "mapScrollSpeedChanged"
+		},
+
+		{
+			"name": "bottomCheckboxesLabels",
+			"type": "labelGroup",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"items":
+			[
+				{
+					"position": {"x": 64, "y": 278},
+					"text": "core.genrltxt.572"
+				},
+				{
+					"position": {"x": 64, "y": 314},
+					"text": "core.genrltxt.573"
+				},
+				{
+					"position": {"x": 64, "y": 350},
+					"text": "core.genrltxt.574"
+				},
+				{
+					"position": {"x": 64, "y": 386},
+					"text": "vcmi.adventureOptions.numericQuantities.hover"
+				},
+				{
+					"position": {"x": 64, "y": 422},
+					"text": "vcmi.adventureOptions.forceMovementInfo.hover"
+				},
+				{
+					"position": {"x": 64, "y": 458},
+					"text": "vcmi.adventureOptions.showGrid.hover"
+				}
+			]
+		},
+
+		{
+			"name": "heroReminderCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "core.help.361",
+			"position": {"x": 28, "y": 312},
+			"callback": "heroReminderChanged"
+		},
+
+		{
+			"name": "quickCombatCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "core.help.362",
+			"position": {"x": 28, "y": 348},
+			"callback": "quickCombatChanged"
+		},
+
+		{
+			"name": "numericQuantitiesCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "vcmi.adventureOptions.numericQuantities",
+			"position": {"x": 28, "y": 384},
+			"callback": "numericQuantitiesChanged"
+		},
+
+		{
+			"name": "forceMovementInfoCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "vcmi.adventureOptions.forceMovementInfo",
+			"position": {"x": 28, "y": 420},
+			"callback": "forceMovementInfoChanged"
+		},
+		{
+			"name": "showGridCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "vcmi.adventureOptions.showGrid",
+			"position": {"x": 28, "y": 456},
+			"callback": "showGridChanged"
+		}
+	]
+}

+ 358 - 0
config/widgets/settings/battleOptionsTab.json

@@ -0,0 +1,358 @@
+{
+	"items":
+	[
+		{
+			"name": "animationSpeedLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "center",
+			"color": "yellow",
+			"text": "core.genrltxt.393",
+			"position": {"x": 122, "y": 224}
+		},
+		{
+			"name": "autoCombatOptionsLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "center",
+			"color": "yellow",
+			"text": "core.genrltxt.396",
+			"position": {"x": 353, "y": 56}
+		},
+		{
+			"name": "creatureInfoLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "center",
+			"color": "yellow",
+			"text": "core.genrltxt.397",
+			"position": {"x": 98, "y": 295}
+		},
+
+		{
+			"name": "creaturesAutoCombatLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "core.genrltxt.398",
+			"position": {"x": 283, "y": 76}
+		},
+		{
+			"name": "spellsAutoCombatLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "core.genrltxt.399",
+			"position": {"x": 283, "y": 106}
+		},
+		{
+			"name": "catapultAutoCombatLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "core.genrltxt.400",
+			"position": {"x": 283, "y": 136}
+		},
+		{
+			"name": "ballistaAutoCombatLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "core.genrltxt.151",
+			"position": {"x": 283, "y": 166}
+		},
+		{
+			"name": "aidTentAutoCombatLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "core.genrltxt.401",
+			"position": {"x": 283, "y": 196}
+		},
+
+		{
+			"name": "creatureInfoAllStatsLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "core.genrltxt.402",
+			"position": {"x": 61, "y": 315}
+		},
+		{
+			"name": "creatureInfoOnlySpellsLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "core.genrltxt.403",
+			"position": {"x": 61, "y": 345}
+		},
+
+		{
+			"name": "viewGridLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "core.genrltxt.404",
+			"position": {"x": 61, "y": 57}
+		},
+		{
+			"name": "showMovementRangeLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "core.genrltxt.405",
+			"position": {"x": 61, "y": 90}
+		},
+		{
+			"name": "mouseShadowLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "core.genrltxt.406",
+			"position": {"x": 61, "y": 123}
+		},
+		{
+			"name": "battleCasualtiesLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "core.genrltxt.407",
+			"position": {"x": 61, "y": 156}
+		},
+
+		{
+			"name": "viewGridCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "core.help.427",
+			"position": {"x": 25, "y": 56},
+			"callback": "viewGridChanged"
+		},
+		{
+			"name": "movementShadowCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "core.help.428",
+			"position": {"x": 25, "y": 89},
+			"callback": "movementShadowChanged"
+		},
+		{
+			"name": "mouseShadowCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "core.help.429",
+			"position": {"x": 25, "y": 122},
+			"callback": "mouseShadowChanged"
+		},
+
+		{
+			"name": "animationSpeedPicker",
+			"type": "toggleGroup",
+			"position": {"x": 28, "y": 233},
+			"items":
+			[
+				{
+					"index": 1,
+					"type": "toggleButton",
+					"image": "sysopb9",
+					"help": "core.help.422",
+					"position": {"x": 0, "y": 0}
+				},
+
+				{
+					"index": 2,
+					"type": "toggleButton",
+					"image": "sysob10",
+					"help": "core.help.423",
+					"position": {"x": 64, "y": 0}
+				},
+
+				{
+					"index": 3,
+					"type": "toggleButton",
+					"image": "sysob11",
+					"help": "core.help.424",
+					"position": {"x": 128, "y": 0}
+				},
+				{
+					"index": 6,
+					"type": "toggleButton",
+					"image": "buttons/square",
+					"help": "vcmi.battleOptions.animationsSpeed4",
+					"position": {"x": 192, "y": 0},
+					"items":
+					[
+						{
+							"name": "battleAnimationsSpeed4",
+							"type": "label",
+							"font": "big",
+							"alignment": "center",
+							"color": "yellow",
+							"text": "vcmi.battleOptions.animationsSpeed4.hover"
+						}
+					]
+				},
+				{
+					"index": 12,
+					"type": "toggleButton",
+					"image": "buttons/square",
+					"help": "vcmi.battleOptions.animationsSpeed5",
+					"position": {"x": 229, "y": 0},
+					"items":
+					[
+						{
+							"name": "battleAnimationsSpeed5",
+							"type": "label",
+							"font": "big",
+							"alignment": "center",
+							"color": "yellow",
+							"text": "vcmi.battleOptions.animationsSpeed5.hover"
+						}
+					]
+				},
+				{
+					"index": 24,
+					"type": "toggleButton",
+					"image": "buttons/square",
+					"help": "vcmi.battleOptions.animationsSpeed6",
+					"position": {"x": 266, "y": 0},
+					"items":
+					[
+						{
+							"name": "battleAnimationsSpeed6",
+							"type": "label",
+							"font": "big",
+							"alignment": "center",
+							"color": "yellow",
+							"text": "vcmi.battleOptions.animationsSpeed6.hover"
+						}
+					]
+				}
+			],
+			"callback": "animationSpeedChanged"
+		},
+
+		{
+			"name": "showQueueLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "vcmi.battleOptions.showQueue.hover",
+			"position": {"x": 61, "y": 393}
+		},
+
+		{
+			"name": "showQueueCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "vcmi.battleOptions.showQueue",
+			"position": {"x": 25, "y": 391},
+			"callback": "showQueueChanged"
+		},
+
+		{
+			"name": "queueSizeLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "yellow",
+			"text": "vcmi.battleOptions.queueSizeLabel.hover",
+			"position": {"x": 60, "y": 429}
+		},
+
+		{
+			"name": "queueSizePicker",
+			"type": "toggleGroup",
+			"position": {"x": 28, "y": 450},
+			"items":
+			[
+				{
+					"index": 0, //auto
+					"type": "toggleButton",
+					"image": "buttons/triplewide",
+					"help": "vcmi.battleOptions.queueSizeAutoButton",
+					"position": {"x": 0, "y": 0},
+					"items":
+					[
+						{
+							"name": "queueSizeAutoText",
+							"type": "label",
+							"font": "big",
+							"alignment": "center",
+							"color": "yellow",
+							"text": "vcmi.battleOptions.queueSizeAutoButton.hover"
+						}
+					]
+				},
+				{
+					"index": 1, //small
+					"type": "toggleButton",
+					"image": "buttons/triplewide",
+					"help": "vcmi.battleOptions.queueSizeSmallButton",
+					"position": {"x": 113, "y": 0},
+					"items":
+					[
+						{
+							"name": "queueSizeSmallText",
+							"type": "label",
+							"font": "big",
+							"alignment": "center",
+							"color": "yellow",
+							"text": "vcmi.battleOptions.queueSizeSmallButton.hover"
+						}
+					]
+				},
+				{
+					"index": 2, //large
+					"type": "toggleButton",
+					"image": "buttons/triplewide",
+					"help": "vcmi.battleOptions.queueSizeBigButton",
+					"position": {"x": 226, "y": 0},
+					"items":
+					[
+						{
+							"name": "queueSizeBigText",
+							"type": "label",
+							"font": "big",
+							"alignment": "center",
+							"color": "yellow",
+							"text": "vcmi.battleOptions.queueSizeBigButton.hover"
+						}
+					]
+				}
+			],
+			"callback": "queueSizeChanged"
+		},
+
+		{
+			"name": "skipBattleIntroMusicLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "vcmi.battleOptions.skipBattleIntroMusic.hover",
+			"position": {"x": 61, "y": 502}
+		},
+
+		{
+			"name": "skipBattleIntroMusicCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "vcmi.battleOptions.skipBattleIntroMusic",
+			"position": {"x": 25, "y": 500},
+			"callback": "skipBattleIntroMusicChanged"
+		}
+	]
+}

+ 158 - 0
config/widgets/settings/generalOptionsTab.json

@@ -0,0 +1,158 @@
+{
+	"items":
+	[
+		{
+			"name": "topSettingsLabels",
+			"type": "labelGroup",
+			"font": "medium",
+			"alignment": "center",
+			"color": "yellow",
+			"items":
+			[
+				{
+					"position": {"x": 122, "y": 50},
+					"text": "vcmi.systemOptions.resolutionButton.hover"
+				},
+				{
+					"position": {"x": 122, "y": 135},
+					"text": "core.genrltxt.394"
+				},
+				{
+					"position": {"x": 122, "y": 200},
+					"text": "core.genrltxt.395"
+				}
+			]
+		},
+
+		{
+			"name": "resolutionButton",
+			"type": "button",
+			"position": {"x": 28, "y": 63},
+			"image": "buttons/resolution",
+			"help": "vcmi.systemOptions.resolutionButton",
+			"callback": "setGameResolution",
+			"hotkey": "g"
+		},
+
+		{
+			"name": "resolutionLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "center",
+			"color": "yellow",
+			"position": {"x": 170, "y": 80}
+		},
+
+		{
+			"name": "musicSlider",
+			"type": "slider",
+			"position": {"x": 29, "y": 147},
+			"size": 190,
+			"style": "brown",
+			"orientation": "horizontal",
+			"itemsVisible": 0,
+			"itemsTotal": 100,
+			"callback": "setMusic"
+		},
+
+		{
+			"name": "soundVolumeSlider",
+			"type": "slider",
+			"position": {"x": 29, "y": 213},
+			"size": 190,
+			"style": "brown",
+			"orientation": "horizontal",
+			"itemsVisible": 0,
+			"itemsTotal": 100,
+			"callback": "setVolume"
+		},
+
+		{
+			"name": "bottomCheckboxesLabels",
+			"type": "labelGroup",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"items":
+			[
+				{
+					"position": {"x": 64, "y": 278},
+					"text": "core.genrltxt.577"
+				},
+				{
+					"position": {"x": 64, "y": 314},
+					"text": "vcmi.systemOptions.fullscreenButton.hover"
+				},
+				{
+					"position": {"x": 64, "y": 350},
+					"text": "vcmi.systemOptions.framerateButton.hover"
+				}
+			]
+		},
+
+		{
+			"name": "spellbookAnimationCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "core.help.364",
+			"position": {"x": 28, "y": 276},
+			"callback": "spellbookAnimationChanged"
+		},
+
+		{
+			"name": "fullscreenCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "vcmi.systemOptions.fullscreenButton",
+			"position": {"x": 28, "y": 312},
+			"callback": "fullscreenChanged"
+		},
+
+		{
+			"name": "framerateCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "vcmi.systemOptions.framerateButton",
+			"position": {"x": 28, "y": 348},
+			"callback": "framerateChanged"
+		},
+
+		{
+			"name": "availableCreaturesAsDwellingLabelText",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover",
+			"position": {"x": 64, "y": 386}
+		},
+
+		{
+			"name": "availableCreaturesAsDwellingLabelCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "vcmi.otherOptions.availableCreaturesAsDwellingLabel",
+			"position": {"x": 28, "y": 384},
+			"callback": "availableCreaturesAsDwellingLabelChanged"
+		},
+
+		{
+			"name": "compactTownCreatureInfoLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "vcmi.otherOptions.compactTownCreatureInfo.hover",
+			"position": {"x": 64, "y": 422}
+		},
+
+		{
+			"name": "compactTownCreatureInfoCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "vcmi.otherOptions.compactTownCreatureInfo",
+			"position": {"x": 28, "y": 420},
+			"callback": "compactTownCreatureInfoChanged"
+		}
+	]
+}

+ 42 - 0
config/widgets/settings/otherOptionsTab.json

@@ -0,0 +1,42 @@
+{
+	"items":
+	[
+		{
+			"name": "availableCreaturesAsDwellingLabelText",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover",
+			"position": {"x": 61, "y": 27}
+		},
+
+		{
+			"name": "availableCreaturesAsDwellingLabelCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "vcmi.otherOptions.availableCreaturesAsDwellingLabel",
+			"position": {"x": 25, "y": 26},
+			"callback": "availableCreaturesAsDwellingLabelChanged"
+		},
+
+		{
+			"name": "compactTownCreatureInfoLabel",
+			"type": "label",
+			"font": "medium",
+			"alignment": "left",
+			"color": "white",
+			"text": "vcmi.otherOptions.compactTownCreatureInfo.hover",
+			"position": {"x": 61, "y": 60}
+		},
+
+		{
+			"name": "compactTownCreatureInfoCheckbox",
+			"type": "toggleButton",
+			"image": "sysopchk.def",
+			"help": "vcmi.otherOptions.compactTownCreatureInfo",
+			"position": {"x": 25, "y": 59},
+			"callback": "compactTownCreatureInfoChanged"
+		}
+	]
+}

+ 165 - 0
config/widgets/settings/settingsMainContainer.json

@@ -0,0 +1,165 @@
+{
+	"items":
+	[
+		{
+			"name": "background",
+			"type": "picture",
+			"image": "vcmiSettingsWindow",
+			"position": {"x": 0, "y": 0}
+		},
+
+		{
+			"name": "settingsTabs",
+			"type": "toggleGroup",
+			"position": {
+				"x": 0,
+				"y": 0
+			},
+			"items":
+			[
+				{
+					"index": 0,
+					"type": "toggleButton",
+					"position": {"x": 0, "y": 0},
+					"image": "buttons/quadwide",
+					"help": "vcmi.settingsMainWindow.generalTab",
+					"items":
+					[
+						{
+							"name": "generalTabButtonTitle",
+							"type": "label",
+							"font": "big",
+							"alignment": "center",
+							"color": "yellow",
+							"text": "vcmi.settingsMainWindow.generalTab.hover"
+						}
+					]
+				},
+
+				{
+					"index": 1,
+					"type": "toggleButton",
+					"position": {"x": 150, "y": 0},
+					"image": "buttons/quadwide",
+					"help": "vcmi.settingsMainWindow.adventureTab",
+					"items":
+					[
+						{
+							"name": "generalTabButtonTitle",
+							"type": "label",
+							"font": "big",
+							"alignment": "center",
+							"color": "yellow",
+							"text": "vcmi.settingsMainWindow.adventureTab.hover"
+						}
+					]
+				},
+
+				{
+					"index": 2,
+					"type": "toggleButton",
+					"position": {"x": 300, "y": 0},
+					"image": "buttons/quadwide",
+					"help": "vcmi.settingsMainWindow.battleTab",
+					"items":
+					[
+						{
+							"name": "adventureTabButtonTitle",
+							"type": "label",
+							"font": "big",
+							"alignment": "center",
+							"color": "yellow",
+							"text": "vcmi.settingsMainWindow.battleTab.hover"
+						}
+					]
+				}
+				//disabled, due to being redundant for now
+//				{
+//					"index": 3,
+//					"type": "toggleButton",
+//					"position": {"x": 450, "y": 0},
+//					"image": "buttons/quadwide",
+//					"help": "vcmi.settingsMainWindow.otherTab",
+//					"items":
+//					[
+//						{
+//							"name": "otherTabButtonTitle",
+//							"type": "label",
+//							"font": "big",
+//							"alignment": "center",
+//							"color": "yellow",
+//							"text": "vcmi.settingsMainWindow.otherTab.hover"
+//						}
+//					]
+//				}
+			],
+			"callback": "activateSettingsTab"
+		},
+
+		{
+			"name": "loadButton",
+			"type": "button",
+			"position": {"x": 386, "y": 383},
+			"image": "SOLOAD.DEF",
+			"imageOrder": [1, 0, 2, 3],
+			"help": "core.help.321",
+			"callback": "loadGame",
+			"hotkey": "l"
+		},
+
+		{
+			"name": "saveButton",
+			"type": "button",
+			"position": {"x": 497, "y": 383},
+			"image": "SOSAVE.DEF",
+			"imageOrder": [1, 0, 2, 3],
+			"help": "core.help.322",
+			"callback": "saveGame",
+			"hotkey": "s"
+		},
+
+		{
+			"name": "restartButton",
+			"type": "button",
+			"position": {"x": 386, "y": 442},
+			"image": "SORSTRT.DEF",
+			"imageOrder": [1, 0, 2, 3],
+			"help": "core.help.323",
+			"callback": "restartGame",
+			"hotkey": "r"
+		},
+
+		{
+			"name": "mainMenuButton",
+			"type": "button",
+			"position": {"x": 497, "y": 442},
+			"image": "SOMAIN.DEF",
+			"imageOrder": [1, 0, 2, 3],
+			"help": "core.help.320",
+			"callback": "returnToMainMenu",
+			"hotkey": "m"
+		},
+
+		{
+			"name": "quitButton",
+			"type": "button",
+			"position": {"x": 386, "y": 500},
+			"image": "soquit.def",
+			"imageOrder": [1, 0, 2, 3],
+			"help": "core.help.324",
+			"callback": "quitGame",
+			"hotkey": "q"
+		},
+
+		{
+			"name": "closeSettingsButton",
+			"type": "button",
+			"position": {"x": 497, "y": 500},
+			"image": "soretrn.def",
+			"imageOrder": [1, 0, 2, 3],
+			"help": "core.help.325",
+			"callback": "closeWindow",
+			"hotkey": ["esc", "backspace"]
+		}
+	]
+}