Jelajahi Sumber

Implemented disabling of buttons based on condition

Ivan Savenko 2 tahun lalu
induk
melakukan
97426a3f7c

+ 106 - 57
client/adventureMap/AdventureMapShortcuts.cpp

@@ -11,74 +11,73 @@
 #include "StdInc.h"
 #include "AdventureMapShortcuts.h"
 
+#include "../CGameInfo.h"
+#include "../CPlayerInterface.h"
+#include "../PlayerLocalState.h"
+#include "../gui/CGuiHandler.h"
+#include "../gui/Shortcut.h"
+#include "../lobby/CSavingScreen.h"
+#include "../mapView/mapHandler.h"
 #include "../windows/CKingdomInterface.h"
 #include "../windows/CSpellWindow.h"
 #include "../windows/CTradeWindow.h"
-#include "../lobby/CSavingScreen.h"
-
-#include "../gui/CGuiHandler.h"
-#include "../gui/Shortcut.h"
-#include "../CPlayerInterface.h"
-#include "../PlayerLocalState.h"
-#include "../CGameInfo.h"
+#include "../windows/settings/SettingsMainWindow.h"
 #include "CAdventureMapInterface.h"
 #include "CAdventureOptions.h"
-#include "../windows/settings/SettingsMainWindow.h"
 
 #include "../../CCallback.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/CPathfinder.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
-#include "../../lib/CPathfinder.h"
+#include "../../lib/mapping/CMap.h"
 
 AdventureMapShortcuts::AdventureMapShortcuts(CAdventureMapInterface & owner)
 	:owner(owner)
 {}
 
-std::map<EShortcut, std::function<void()>> AdventureMapShortcuts::getFunctors()
-{
-	std::map<EShortcut, std::function<void()>> result = {
-		{ EShortcut::ADVENTURE_KINGDOM_OVERVIEW, [this]() { this->showOverview(); } },
-		{ EShortcut::NONE, [this]() { this->worldViewBack(); } },
-		{ EShortcut::NONE, [this]() { this->worldViewScale1x(); } },
-		{ EShortcut::NONE, [this]() { this->worldViewScale2x(); } },
-		{ EShortcut::NONE, [this]() { this->worldViewScale4x(); } },
-		{ EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL, [this]() { this->switchMapLevel(); } },
-		{ EShortcut::ADVENTURE_QUEST_LOG, [this]() { this->showQuestlog(); } },
-		{ EShortcut::ADVENTURE_TOGGLE_SLEEP, [this]() { this->toggleSleepWake(); } },
-		{ EShortcut::ADVENTURE_SET_HERO_ASLEEP, [this]() { this->setHeroSleeping(); } },
-		{ EShortcut::ADVENTURE_SET_HERO_AWAKE, [this]() { this->setHeroAwake(); } },
-		{ EShortcut::ADVENTURE_MOVE_HERO, [this]() { this->moveHeroAlongPath(); } },
-		{ EShortcut::ADVENTURE_CAST_SPELL, [this]() { this->showSpellbook(); } },
-		{ EShortcut::ADVENTURE_GAME_OPTIONS, [this]() { this->adventureOptions(); } },
-		{ EShortcut::GLOBAL_OPTIONS, [this]() { this->systemOptions(); } },
-		{ EShortcut::ADVENTURE_NEXT_HERO, [this]() { this->nextHero(); } },
-		{ EShortcut::GAME_END_TURN, [this]() { this->endTurn(); } },
-		{ EShortcut::ADVENTURE_THIEVES_GUILD, [this]() { this->showThievesGuild(); } },
-		{ EShortcut::ADVENTURE_VIEW_SCENARIO, [this]() { this->showScenarioInfo(); } },
-		{ EShortcut::GAME_SAVE_GAME, [this]() { this->saveGame(); } },
-		{ EShortcut::GAME_LOAD_GAME, [this]() { this->loadGame(); } },
-		{ EShortcut::ADVENTURE_DIG_GRAIL, [this]() { this->digGrail(); } },
-		{ EShortcut::ADVENTURE_VIEW_PUZZLE, [this]() { this->viewPuzzleMap(); } },
-		{ EShortcut::ADVENTURE_VIEW_WORLD, [this]() { this->viewWorldMap(); } },
-		{ EShortcut::GAME_RESTART_GAME, [this]() { this->restartGame(); } },
-		{ EShortcut::ADVENTURE_VISIT_OBJECT, [this]() { this->visitObject(); } },
-		{ EShortcut::ADVENTURE_VIEW_SELECTED, [this]() { this->openObject(); } },
-		{ EShortcut::GLOBAL_CANCEL, [this]() { this->abortSpellcasting(); } },
-		{ EShortcut::GAME_OPEN_MARKETPLACE, [this]() { this->showMarketplace(); } },
-		{ EShortcut::ADVENTURE_NEXT_TOWN, [this]() { this->nextTown(); } },
-//		{ EShortcut::ADVENTURE_NEXT_OBJECT, [this]() { this->nextObject(); } },
-		{ EShortcut::ADVENTURE_MOVE_HERO_SW, [this]() { this->moveHeroDirectional({-1, +1}); } },
-		{ EShortcut::ADVENTURE_MOVE_HERO_SS, [this]() { this->moveHeroDirectional({ 0, +1}); } },
-		{ EShortcut::ADVENTURE_MOVE_HERO_SE, [this]() { this->moveHeroDirectional({+1, +1}); } },
-		{ EShortcut::ADVENTURE_MOVE_HERO_WW, [this]() { this->moveHeroDirectional({-1,  0}); } },
-		{ EShortcut::ADVENTURE_MOVE_HERO_EE, [this]() { this->moveHeroDirectional({+1,  0}); } },
-		{ EShortcut::ADVENTURE_MOVE_HERO_NW, [this]() { this->moveHeroDirectional({-1, -1}); } },
-		{ EShortcut::ADVENTURE_MOVE_HERO_NN, [this]() { this->moveHeroDirectional({ 0, -1}); } },
-		{ EShortcut::ADVENTURE_MOVE_HERO_NE, [this]() { this->moveHeroDirectional({+1, -1}); } },
+std::vector<AdventureMapShortcutState> AdventureMapShortcuts::getShortcuts()
+{
+	std::vector<AdventureMapShortcutState> result = {
+		{ EShortcut::ADVENTURE_KINGDOM_OVERVIEW, optionDefault(),        [this]() { this->showOverview(); } },
+		{ EShortcut::ADVENTURE_EXIT_WORLD_VIEW,  optionDefault(),        [this]() { this->worldViewBack(); } },
+		{ EShortcut::ADVENTURE_VIEW_WORLD_X1,    optionDefault(),        [this]() { this->worldViewScale1x(); } },
+		{ EShortcut::ADVENTURE_VIEW_WORLD_X2,    optionDefault(),        [this]() { this->worldViewScale2x(); } },
+		{ EShortcut::ADVENTURE_VIEW_WORLD_X4,    optionDefault(),        [this]() { this->worldViewScale4x(); } },
+		{ EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL, optionHasUnderground(), [this]() { this->switchMapLevel(); } },
+		{ EShortcut::ADVENTURE_QUEST_LOG,        optionHasQuests(),      [this]() { this->showQuestlog(); } },
+		{ EShortcut::ADVENTURE_TOGGLE_SLEEP,     optionHeroSelected(),   [this]() { this->toggleSleepWake(); } },
+		{ EShortcut::ADVENTURE_SET_HERO_ASLEEP,  optionHeroSleeping(),   [this]() { this->setHeroSleeping(); } },
+		{ EShortcut::ADVENTURE_SET_HERO_AWAKE,   optionHeroSleeping(),   [this]() { this->setHeroAwake(); } },
+		{ EShortcut::ADVENTURE_MOVE_HERO,        optionHeroCanMove(),    [this]() { this->moveHeroAlongPath(); } },
+		{ EShortcut::ADVENTURE_CAST_SPELL,       optionHeroSelected(),   [this]() { this->showSpellbook(); } },
+		{ EShortcut::ADVENTURE_GAME_OPTIONS,     optionDefault(),        [this]() { this->adventureOptions(); } },
+		{ EShortcut::GLOBAL_OPTIONS,             optionDefault(),        [this]() { this->systemOptions(); } },
+		{ EShortcut::ADVENTURE_NEXT_HERO,        optionHasNextHero(),    [this]() { this->nextHero(); } },
+		{ EShortcut::GAME_END_TURN,              optionDefault(),        [this]() { this->endTurn(); } },
+		{ EShortcut::ADVENTURE_THIEVES_GUILD,    optionDefault(),        [this]() { this->showThievesGuild(); } },
+		{ EShortcut::ADVENTURE_VIEW_SCENARIO,    optionDefault(),        [this]() { this->showScenarioInfo(); } },
+		{ EShortcut::GAME_SAVE_GAME,             optionDefault(),        [this]() { this->saveGame(); } },
+		{ EShortcut::GAME_LOAD_GAME,             optionDefault(),        [this]() { this->loadGame(); } },
+		{ EShortcut::ADVENTURE_DIG_GRAIL,        optionHeroSelected(),   [this]() { this->digGrail(); } },
+		{ EShortcut::ADVENTURE_VIEW_PUZZLE,      optionDefault(),        [this]() { this->viewPuzzleMap(); } },
+		{ EShortcut::GAME_RESTART_GAME,          optionDefault(),        [this]() { this->restartGame(); } },
+		{ EShortcut::ADVENTURE_VISIT_OBJECT,     optionHeroSelected(),   [this]() { this->visitObject(); } },
+		{ EShortcut::ADVENTURE_VIEW_SELECTED,    optionDefault(),        [this]() { this->openObject(); } },
+		{ EShortcut::GLOBAL_CANCEL,              optionSpellcasting(),   [this]() { this->abortSpellcasting(); } },
+		{ EShortcut::GAME_OPEN_MARKETPLACE,      optionDefault(),        [this]() { this->showMarketplace(); } },
+		{ EShortcut::ADVENTURE_NEXT_TOWN,        optionDefault(),        [this]() { this->nextTown(); } },
+		{ EShortcut::ADVENTURE_NEXT_OBJECT,      optionDefault(),        [this]() { this->nextObject(); } },
+		{ EShortcut::ADVENTURE_MOVE_HERO_SW,     optionHeroSelected(),   [this]() { this->moveHeroDirectional({-1, +1}); } },
+		{ EShortcut::ADVENTURE_MOVE_HERO_SS,     optionHeroSelected(),   [this]() { this->moveHeroDirectional({ 0, +1}); } },
+		{ EShortcut::ADVENTURE_MOVE_HERO_SE,     optionHeroSelected(),   [this]() { this->moveHeroDirectional({+1, +1}); } },
+		{ EShortcut::ADVENTURE_MOVE_HERO_WW,     optionHeroSelected(),   [this]() { this->moveHeroDirectional({-1,  0}); } },
+		{ EShortcut::ADVENTURE_MOVE_HERO_EE,     optionHeroSelected(),   [this]() { this->moveHeroDirectional({+1,  0}); } },
+		{ EShortcut::ADVENTURE_MOVE_HERO_NW,     optionHeroSelected(),   [this]() { this->moveHeroDirectional({-1, -1}); } },
+		{ EShortcut::ADVENTURE_MOVE_HERO_NN,     optionHeroSelected(),   [this]() { this->moveHeroDirectional({ 0, -1}); } },
+		{ EShortcut::ADVENTURE_MOVE_HERO_NE,     optionHeroSelected(),   [this]() { this->moveHeroDirectional({+1, -1}); } }
 	};
-
 	return result;
 }
 
@@ -284,11 +283,6 @@ void AdventureMapShortcuts::viewPuzzleMap()
 	LOCPLINT->showPuzzleMap();
 }
 
-void AdventureMapShortcuts::viewWorldMap()
-{
-	LOCPLINT->viewWorldMap();
-}
-
 void AdventureMapShortcuts::restartGame()
 {
 	LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.adventureMap.confirmRestartGame"),
@@ -342,10 +336,65 @@ void AdventureMapShortcuts::showMarketplace()
 
 void AdventureMapShortcuts::nextTown()
 {
-	owner.hotkeyEndingTurn();
+	//TODO
+}
+
+void AdventureMapShortcuts::nextObject()
+{
+	//TODO
 }
 
 void AdventureMapShortcuts::moveHeroDirectional(const Point & direction)
 {
 	owner.hotkeyMoveHeroDirectional(direction);
 }
+
+bool AdventureMapShortcuts::optionHasQuests()
+{
+	return CGI->mh->getMap()->quests.empty();
+}
+
+bool AdventureMapShortcuts::optionHasUnderground()
+{
+	return LOCPLINT->cb->getMapSize().z > 0;
+}
+
+bool AdventureMapShortcuts::optionMapLevelSurface()
+{
+	return false; //TODO
+}
+
+bool AdventureMapShortcuts::optionHeroSleeping()
+{
+	const CGHeroInstance *hero = LOCPLINT->localState->getCurrentHero();
+	return hero && LOCPLINT->localState->isHeroSleeping(hero);
+}
+
+bool AdventureMapShortcuts::optionHeroSelected()
+{
+	return LOCPLINT->localState->getCurrentHero() != nullptr;
+}
+
+bool AdventureMapShortcuts::optionHeroCanMove()
+{
+	const auto * hero = LOCPLINT->localState->getCurrentHero();
+	return hero && hero->movement != 0 && LOCPLINT->localState->hasPath(hero);
+}
+
+bool AdventureMapShortcuts::optionHasNextHero()
+{
+	const auto * hero = LOCPLINT->localState->getCurrentHero();
+	const auto * nextSuitableHero = LOCPLINT->localState->getNextWanderingHero(hero);
+
+	return nextSuitableHero != nullptr;
+}
+
+bool AdventureMapShortcuts::optionSpellcasting()
+{
+	return true; //TODO
+}
+
+bool AdventureMapShortcuts::optionDefault()
+{
+	return true; //TODO
+}

+ 18 - 2
client/adventureMap/AdventureMapShortcuts.h

@@ -17,6 +17,13 @@ VCMI_LIB_NAMESPACE_END
 enum class EShortcut;
 class CAdventureMapInterface;
 
+struct AdventureMapShortcutState
+{
+	EShortcut shortcut;
+	bool isEnabled;
+	std::function<void()> callback;
+};
+
 /// Class that contains list of functions for shortcuts available from adventure map
 class AdventureMapShortcuts
 {
@@ -44,7 +51,6 @@ class AdventureMapShortcuts
 	void loadGame();
 	void digGrail();
 	void viewPuzzleMap();
-	void viewWorldMap();
 	void restartGame();
 	void visitObject();
 	void openObject();
@@ -57,5 +63,15 @@ class AdventureMapShortcuts
 public:
 	explicit AdventureMapShortcuts(CAdventureMapInterface & owner);
 
-	std::map<EShortcut, std::function<void()>> getFunctors();
+	std::vector<AdventureMapShortcutState> getShortcuts();
+
+	bool optionHasQuests();
+	bool optionHasUnderground();
+	bool optionMapLevelSurface();
+	bool optionHeroSleeping();
+	bool optionHeroSelected();
+	bool optionHeroCanMove();
+	bool optionHasNextHero();
+	bool optionSpellcasting();
+	bool optionDefault();
 };

+ 4 - 17
client/adventureMap/CAdventureMapInterface.cpp

@@ -55,15 +55,12 @@ CAdventureMapInterface::CAdventureMapInterface():
 	widget = std::make_shared<CAdventureMapWidget>(shortcuts);
 	widget->setState(EGameState::MAKING_TURN);
 	widget->getMapView()->onViewMapActivated();
-
-	widget->setOptionHasQuests(!CGI->mh->getMap()->quests.empty());
-	widget->setOptionHasUnderground(CGI->mh->getMap()->twoLevel);
 }
 
 void CAdventureMapInterface::onMapViewMoved(const Rect & visibleArea, int mapLevel)
 {
-	widget->setOptionUndergroundLevel(mapLevel > 0);
 	widget->getMinimap()->onMapViewMoved(visibleArea, mapLevel);
+	widget->updateActiveState();
 }
 
 void CAdventureMapInterface::onAudioResumed()
@@ -76,17 +73,6 @@ void CAdventureMapInterface::onAudioPaused()
 	mapAudio->onAudioPaused();
 }
 
-void CAdventureMapInterface::updateButtons()
-{
-	const auto * hero = LOCPLINT->localState->getCurrentHero();
-	const auto * nextSuitableHero = LOCPLINT->localState->getNextWanderingHero(hero);
-
-	widget->setOptionHeroSelected(hero != nullptr);
-	widget->setOptionHeroCanMove(hero && LOCPLINT->localState->hasPath(hero) && hero->movement != 0);
-	widget->setOptionHasNextHero(nextSuitableHero != nullptr);
-	widget->setOptionHeroSleeping(hero && LOCPLINT->localState->isHeroSleeping(hero));
-}
-
 void CAdventureMapInterface::onHeroMovementStarted(const CGHeroInstance * hero)
 {
 	widget->getInfoBar()->popAll();
@@ -100,7 +86,7 @@ void CAdventureMapInterface::onHeroChanged(const CGHeroInstance *h)
 	if (h && h == LOCPLINT->localState->getCurrentHero() && !widget->getInfoBar()->showingComponents())
 		widget->getInfoBar()->showSelection();
 
-	updateButtons();
+	widget->updateActiveState();
 }
 
 void CAdventureMapInterface::onTownChanged(const CGTownInstance * town)
@@ -297,7 +283,8 @@ void CAdventureMapInterface::onSelectionChanged(const CArmedInstance *sel)
 		LOCPLINT->localState->verifyPath(hero);
 		onHeroChanged(hero);
 	}
-	updateButtons();
+
+	widget->updateActiveState();
 	widget->getHeroList()->redraw();
 	widget->getTownList()->redraw();
 }

+ 0 - 3
client/adventureMap/CAdventureMapInterface.h

@@ -66,9 +66,6 @@ private:
 
 	const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const; //checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
 
-	// update locked state of buttons
-	void updateButtons();
-
 	void handleMapScrollingUpdate();
 
 	void showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode);

+ 39 - 36
client/adventureMap/CAdventureMapWidget.cpp

@@ -51,8 +51,8 @@ CAdventureMapWidget::CAdventureMapWidget( std::shared_ptr<AdventureMapShortcuts>
 	REGISTER_BUILDER("adventureResourceDateBar", &CAdventureMapWidget::buildResourceDateBar );
 	REGISTER_BUILDER("adventureStatusBar",       &CAdventureMapWidget::buildStatusBar       );
 
-	for (const auto & entry : shortcuts->getFunctors())
-		addShortcut(entry.first, entry.second);
+	for (const auto & entry : shortcuts->getShortcuts())
+		addShortcut(entry.shortcut, entry.callback);
 
 	const JsonNode config(ResourceID("config/widgets/adventureMap.json"));
 
@@ -190,12 +190,17 @@ std::shared_ptr<CIntObject> CAdventureMapWidget::buildMapContainer(const JsonNod
 	else
 		result = std::make_shared<CAdventureMapContainerWidget>();
 
+	result->disableCondition = input["hideWhen"].String();
+
 	result->moveBy(position.topLeft());
 	subwidgetSizes.push_back(position);
 	for(const auto & entry : input["items"].Vector())
 	{
-		result->ownedChildren.push_back(buildWidget(entry));
-		result->addChild(result->ownedChildren.back().get(), false);
+		auto widget = buildWidget(entry);
+
+		addWidget(entry["name"].String(), widget);
+		result->ownedChildren.push_back(widget);
+		result->addChild(widget.get(), false);
 	}
 	subwidgetSizes.pop_back();
 
@@ -378,56 +383,54 @@ EGameState CAdventureMapWidget::getState()
 	return state;
 }
 
-void CAdventureMapWidget::setOptionHasQuests(bool on)
-{
-
-}
-
-void CAdventureMapWidget::setOptionHasUnderground(bool on)
+CAdventureMapIcon::CAdventureMapIcon(const Point & position, std::shared_ptr<CAnimation> animation, size_t index, size_t iconsPerPlayer)
+	: index(index)
+	, iconsPerPlayer(iconsPerPlayer)
 {
-
+	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
+	pos += position;
+	image = std::make_shared<CAnimImage>(animation, index);
 }
 
-void CAdventureMapWidget::setOptionUndergroundLevel(bool on)
+void CAdventureMapIcon::setPlayer(const PlayerColor & player)
 {
-
+	image->setFrame(index + player.getNum() * iconsPerPlayer);
 }
 
-void CAdventureMapWidget::setOptionHeroSleeping(bool on)
+void CAdventureMapOverlayWidget::show(SDL_Surface * to)
 {
-
+	CIntObject::showAll(to);
 }
 
-void CAdventureMapWidget::setOptionHeroSelected(bool on)
+void CAdventureMapWidget::updateActiveStateChildden(CIntObject * widget)
 {
+	for(auto & entry : widget->children)
+	{
+		auto container = dynamic_cast<CAdventureMapContainerWidget *>(entry);
 
-}
+		if (container)
+		{
+			if (container->disableCondition == "heroAwake")
+				container->setEnabled(!shortcuts->optionHeroSleeping());
 
-void CAdventureMapWidget::setOptionHeroCanMove(bool on)
-{
+			if (container->disableCondition == "heroSleeping")
+				container->setEnabled(shortcuts->optionHeroSleeping());
 
-}
+			if (container->disableCondition == "mapLayerSurface")
+				container->setEnabled(shortcuts->optionMapLevelSurface());
 
-void CAdventureMapWidget::setOptionHasNextHero(bool on)
-{
+			if (container->disableCondition == "mapLayerUnderground")
+				container->setEnabled(!shortcuts->optionMapLevelSurface());
 
-}
+		}
 
-CAdventureMapIcon::CAdventureMapIcon(const Point & position, std::shared_ptr<CAnimation> animation, size_t index, size_t iconsPerPlayer)
-	: index(index)
-	, iconsPerPlayer(iconsPerPlayer)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
-	pos += position;
-	image = std::make_shared<CAnimImage>(animation, index);
+	}
 }
 
-void CAdventureMapIcon::setPlayer(const PlayerColor & player)
+void CAdventureMapWidget::updateActiveState()
 {
-	image->setFrame(index + player.getNum() * iconsPerPlayer);
-}
+	updateActiveStateChildden(this);
 
-void CAdventureMapOverlayWidget::show(SDL_Surface * to)
-{
-	CIntObject::showAll(to);
+	for (auto entry: shortcuts->getShortcuts())
+		setShortcutBlocked(entry.shortcut, !entry.isEnabled);
 }

+ 4 - 7
client/adventureMap/CAdventureMapWidget.h

@@ -72,6 +72,7 @@ class CAdventureMapWidget : public InterfaceObjectConfigurable
 	std::shared_ptr<CIntObject> buildStatusBar(const JsonNode & input);
 
 	void setPlayerChildren(CIntObject * widget, const PlayerColor & player);
+	void updateActiveStateChildden(CIntObject * widget);
 public:
 	explicit CAdventureMapWidget( std::shared_ptr<AdventureMapShortcuts> shortcuts );
 
@@ -85,13 +86,8 @@ public:
 	void setState(EGameState newState);
 	EGameState getState();
 
-	void setOptionHasQuests(bool on);
-	void setOptionHasUnderground(bool on);
-	void setOptionUndergroundLevel(bool on);
-	void setOptionHeroSleeping(bool on);
-	void setOptionHeroSelected(bool on);
-	void setOptionHeroCanMove(bool on);
-	void setOptionHasNextHero(bool on);
+	void updateActiveState();
+
 };
 
 /// Small helper class that provides ownership for shared_ptr's of child elements
@@ -99,6 +95,7 @@ class CAdventureMapContainerWidget : public CIntObject
 {
 	friend class CAdventureMapWidget;
 	std::vector<std::shared_ptr<CIntObject>> ownedChildren;
+	std::string disableCondition;
 };
 
 class CAdventureMapOverlayWidget : public CAdventureMapContainerWidget

+ 1 - 1
client/adventureMap/CAdventureOptions.cpp

@@ -28,7 +28,7 @@ CAdventureOptions::CAdventureOptions()
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 
-	viewWorld = std::make_shared<CButton>(Point(24, 23), "ADVVIEW.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_WORLD);
+	viewWorld = std::make_shared<CButton>(Point(24, 23), "ADVVIEW.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_WORLD_X2);
 	viewWorld->addCallback( [] { LOCPLINT->viewWorldMap(); });
 
 	exit = std::make_shared<CButton>(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), std::bind(&CAdventureOptions::close, this), EShortcut::GLOBAL_RETURN);

+ 8 - 0
client/gui/CIntObject.cpp

@@ -204,6 +204,14 @@ void CIntObject::enable()
 	recActions = 255;
 }
 
+void CIntObject::setEnabled(bool on)
+{
+	if (on)
+		enable();
+	else
+		disable();
+}
+
 void CIntObject::fitToScreen(int borderWidth, bool propagate)
 {
 	Point newPos = pos.topLeft();

+ 7 - 2
client/gui/CIntObject.h

@@ -140,8 +140,13 @@ public:
 	ui8 defActions; //which calls will be tried to be redirected to children
 	ui8 recActions; //which calls we allow to receive from parent
 
-	void disable(); //deactivates if needed, blocks all automatic activity, allows only disposal
-	void enable(); //activates if needed, all activity enabled (Warning: may not be symetric with disable if recActions was limited!)
+	/// deactivates if needed, blocks all automatic activity, allows only disposal
+	void disable();
+	/// activates if needed, all activity enabled (Warning: may not be symetric with disable if recActions was limited!)
+	void enable();
+	/// deactivates or activates UI element based on flag
+	void setEnabled(bool on);
+
 
 	// activate or deactivate object. Inactive object won't receive any input events (keyboard\mouse)
 	// usually used automatically by parent

+ 21 - 9
client/gui/InterfaceObjectConfigurable.cpp

@@ -82,15 +82,26 @@ void InterfaceObjectConfigurable::build(const JsonNode &config)
 		items = &config["items"];
 	}
 	
-	const std::string unnamedObjectPrefix = "__widget_";
 	for(const auto & item : items->Vector())
-	{
-		std::string name = item["name"].isNull()
-						? unnamedObjectPrefix + std::to_string(unnamedObjectId++)
-						: item["name"].String();
-		logGlobal->debug("Building widget with name %s", name);
-		widgets[name] = buildWidget(item);
-	}
+		addWidget(item["name"].String(), buildWidget(item));
+}
+
+void InterfaceObjectConfigurable::addWidget(const std::string & namePreferred, std::shared_ptr<CIntObject> widget)
+{
+	static const std::string unnamedObjectPrefix = "__widget_";
+
+	std::string nameActual;
+
+	if (widgets.count(namePreferred) == 0)
+		nameActual = namePreferred;
+	else
+		logGlobal->error("Duplicated widget name: '%s'", namePreferred);
+
+	if (nameActual.empty())
+		nameActual = unnamedObjectPrefix + std::to_string(unnamedObjectId++);
+
+	logGlobal->debug("Building widget with name %s", nameActual);
+	widgets[nameActual] = widget;
 }
 
 std::string InterfaceObjectConfigurable::readText(const JsonNode & config) const
@@ -382,7 +393,8 @@ std::shared_ptr<CSlider> InterfaceObjectConfigurable::buildSlider(const JsonNode
 	auto itemsTotal = config["itemsTotal"].Integer();
 	auto value = config["selected"].Integer();
 	bool horizontal = config["orientation"].String() == "horizontal";
-	auto const & result = std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style);
+	const auto & result =
+		std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style);
 
 	if (!config["scrollBounds"].isNull())
 	{

+ 2 - 0
client/gui/InterfaceObjectConfigurable.h

@@ -48,6 +48,8 @@ protected:
 	
 	//must be called after adding callbacks
 	void build(const JsonNode & config);
+
+	void addWidget(const std::string & name, std::shared_ptr<CIntObject> widget);
 	
 	void addCallback(const std::string & callbackName, std::function<void(int)> callback);
 	JsonNode variables;

+ 4 - 1
client/gui/Shortcut.h

@@ -95,12 +95,15 @@ enum class EShortcut
 	ADVENTURE_VIEW_SCENARIO,// View Scenario Information window
 	ADVENTURE_DIG_GRAIL,
 	ADVENTURE_VIEW_PUZZLE,
-	ADVENTURE_VIEW_WORLD,
+	ADVENTURE_VIEW_WORLD_X1,
+	ADVENTURE_VIEW_WORLD_X2,
+	ADVENTURE_VIEW_WORLD_X4,
 	ADVENTURE_TOGGLE_MAP_LEVEL,
 	ADVENTURE_KINGDOM_OVERVIEW,
 	ADVENTURE_QUEST_LOG,
 	ADVENTURE_CAST_SPELL,
 	ADVENTURE_THIEVES_GUILD,
+	ADVENTURE_EXIT_WORLD_VIEW,
 
 	// Move hero one tile in specified direction. Bound to cursors & numpad buttons
 	ADVENTURE_MOVE_HERO_SW,

+ 5 - 2
client/gui/ShortcutHandler.cpp

@@ -105,7 +105,7 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(SDL_Keycode key) const
 		{SDLK_i,         EShortcut::ADVENTURE_VIEW_SCENARIO   },
 		{SDLK_d,         EShortcut::ADVENTURE_DIG_GRAIL       },
 		{SDLK_p,         EShortcut::ADVENTURE_VIEW_PUZZLE     },
-		{SDLK_v,         EShortcut::ADVENTURE_VIEW_WORLD      },
+		{SDLK_v,         EShortcut::ADVENTURE_VIEW_WORLD_X1   },
 		{SDLK_u,         EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL},
 		{SDLK_k,         EShortcut::ADVENTURE_KINGDOM_OVERVIEW},
 		{SDLK_q,         EShortcut::ADVENTURE_QUEST_LOG       },
@@ -238,12 +238,15 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
 		{"adventureViewScenario",    EShortcut::ADVENTURE_VIEW_SCENARIO   },
 		{"adventureDigGrail",        EShortcut::ADVENTURE_DIG_GRAIL       },
 		{"adventureViewPuzzle",      EShortcut::ADVENTURE_VIEW_PUZZLE     },
-		{"adventureViewWorld",       EShortcut::ADVENTURE_VIEW_WORLD      },
+		{"adventureViewWorld1",      EShortcut::ADVENTURE_VIEW_WORLD_X1   },
+		{"adventureViewWorld2",      EShortcut::ADVENTURE_VIEW_WORLD_X2   },
+		{"adventureViewWorld4",      EShortcut::ADVENTURE_VIEW_WORLD_X4   },
 		{"adventureToggleMapLevel",  EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL},
 		{"adventureKingdomOverview", EShortcut::ADVENTURE_KINGDOM_OVERVIEW},
 		{"adventureQuestLog",        EShortcut::ADVENTURE_QUEST_LOG       },
 		{"adventureCastSpell",       EShortcut::ADVENTURE_CAST_SPELL      },
 		{"adventureThievesGuild",    EShortcut::ADVENTURE_THIEVES_GUILD   },
+		{"adventureExitWorldView",   EShortcut::ADVENTURE_EXIT_WORLD_VIEW },
 		{"battleToggleQueue",        EShortcut::BATTLE_TOGGLE_QUEUE       },
 		{"battleUseCreatureSpell",   EShortcut::BATTLE_USE_CREATURE_SPELL },
 		{"battleSurrender",          EShortcut::BATTLE_SURRENDER          },

+ 58 - 24
config/widgets/adventureMap.json

@@ -113,6 +113,7 @@
 		{
 			"type": "adventureMapContainer",
 			"name" : "buttonsContainer",
+			"hideWhen" : "worldViewMode",
 			"area": { "top": 196, "right" : 57, "width" : 64, "height" : 192 },
 			"items" : [
 				{
@@ -125,22 +126,36 @@
 					"area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 }
 				},
 				{
-					"type": "adventureMapButton",
-					"name": "buttonUnderground",
-					"image" : "IAM010.DEF",
-					"help" : "core.help.294",
-					"hotkey": "adventureToggleMapLevel",
-					"playerColored" : true,
+					"type": "adventureMapContainer",
+					"hideWhen" : "mapLayerUnderground",
 					"area": { "top" : 0, "left": 32, "width" : 32, "height" : 32 }
+					"items" : [
+						{
+							"type": "adventureMapButton",
+							"name": "buttonUnderground",
+							"image" : "IAM010.DEF",
+							"help" : "core.help.294",
+							"hotkey": "adventureToggleMapLevel",
+							"playerColored" : true,
+							"area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 }
+						}
+					],
 				},
 				{
-					"type": "adventureMapButton",
-					"name": "buttonSurface",
-					"image" : "IAM003.DEF",
-					"help" : "core.help.294",
-					"hotkey": "adventureToggleMapLevel",
-					"playerColored" : true,
+					"type": "adventureMapContainer",
+					"hideWhen" : "mapLayerSurface",
 					"area": { "top" : 0, "left": 32, "width" : 32, "height" : 32 }
+					"items" : [
+						{
+							"type": "adventureMapButton",
+							"name": "buttonSurface",
+							"image" : "IAM003.DEF",
+							"help" : "core.help.294",
+							"hotkey": "adventureToggleMapLevel",
+							"playerColored" : true,
+							"area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 }
+						}
+					],
 				},
 				{
 					"type": "adventureMapButton",
@@ -152,22 +167,36 @@
 					"area": { "top" : 32, "left": 0, "width" : 32, "height" : 32 }
 				},
 				{
-					"type": "adventureMapButton",
-					"name": "buttonSleep",
-					"image" : "IAM005.DEF",
-					"help" : "core.help.296",
-					"hotkey": "adventureSetHeroAsleep",
-					"playerColored" : true,
+					"type": "adventureMapContainer",
+					"hideWhen" : "heroAwake",
 					"area": { "top" : 32, "left": 32, "width" : 32, "height" : 32 }
+					"items" : [
+						{
+							"type": "adventureMapButton",
+							"name": "buttonSleep",
+							"image" : "IAM005.DEF",
+							"help" : "core.help.296",
+							"hotkey": "adventureSetHeroAsleep",
+							"playerColored" : true,
+							"area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 }
+						}
+					]
 				},
 				{
-					"type": "adventureMapButton",
-					"name": "buttonWake",
-					"image" : "IAM011.DEF",
-					"help" : "core.help.296",
-					"hotkey": "adventureSetHeroAwake",
-					"playerColored" : true,
+					"type": "adventureMapContainer",
+					"hideWhen" : "heroSleeping",
 					"area": { "top" : 32, "left": 32, "width" : 32, "height" : 32 }
+					"items" : [
+						{
+							"type": "adventureMapButton",
+							"name": "buttonWake",
+							"image" : "IAM011.DEF",
+							"help" : "core.help.296",
+							"hotkey": "adventureSetHeroAwake",
+							"playerColored" : true,
+							"area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 }
+						}
+					]
 				},
 				{
 					"type": "adventureMapButton",
@@ -229,6 +258,7 @@
 		{
 			"type": "adventureMapContainer",
 			"name" : "listContainerSmall",
+			"hideWhen" : "worldViewMode",
 			"area": { "top": 196, "right" : 0, "width" : 193, "height" : 196 },
 			"exists" : { "heightMax" : 664 },
 			"items" : [
@@ -304,6 +334,7 @@
 		{
 			"type": "adventureMapContainer",
 			"name" : "emptyAreaFillSmall",
+			"hideWhen" : "worldViewMode",
 			"area": { "top": 392, "right" : 3, "width" : 190, "bottom" : 211 },
 			"exists" : { "heightMax" : 664 },
 			"items" : [
@@ -320,6 +351,7 @@
 		{
 			"type": "adventureMapContainer",
 			"name" : "listContainerLarge",
+			"hideWhen" : "worldViewMode",
 			"area": { "top": 196, "right" : 0, "width" : 193, "height" : 260 },
 			"exists" : { "heightMin" : 664 },
 			"items" : [
@@ -389,6 +421,7 @@
 		{
 			"type": "adventureMapContainer",
 			"name" : "emptyAreaFillLarge",
+			"hideWhen" : "worldViewMode",
 			"area": { "top": 456, "right" : 3, "width" : 190, "bottom" : 211 },
 			"exists" : { "heightMin" : 664 },
 			"items" : [
@@ -434,6 +467,7 @@
 		{
 			"type": "adventureMapContainer",
 			"name" : "worldViewContainer",
+			"hideWhen" : "mapViewMode",
 			"area": { "top": 195, "right" : 4, "width" : 190, "height" : 381 },
 			"items" : [
 				{