浏览代码

Merge pull request #2163 from IvanSavenko/adventure_map_zoom

Adventure map zoom
Ivan Savenko 2 年之前
父节点
当前提交
956ff22e64

+ 5 - 0
client/adventureMap/AdventureMapInterface.cpp

@@ -803,6 +803,11 @@ void AdventureMapInterface::hotkeySwitchMapLevel()
 	widget->getMapView()->onMapLevelSwitched();
 	widget->getMapView()->onMapLevelSwitched();
 }
 }
 
 
+void AdventureMapInterface::hotkeyZoom(int delta)
+{
+	widget->getMapView()->onMapZoomLevelChanged(delta);
+}
+
 void AdventureMapInterface::onScreenResize()
 void AdventureMapInterface::onScreenResize()
 {
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;

+ 1 - 0
client/adventureMap/AdventureMapInterface.h

@@ -109,6 +109,7 @@ public:
 	void hotkeyEndingTurn();
 	void hotkeyEndingTurn();
 	void hotkeyNextTown();
 	void hotkeyNextTown();
 	void hotkeySwitchMapLevel();
 	void hotkeySwitchMapLevel();
+	void hotkeyZoom(int delta);
 
 
 	/// Called by PlayerInterface when specified player is ready to start his turn
 	/// Called by PlayerInterface when specified player is ready to start his turn
 	void onHotseatWaitStarted(PlayerColor playerID);
 	void onHotseatWaitStarted(PlayerColor playerID);

+ 8 - 0
client/adventureMap/AdventureMapShortcuts.cpp

@@ -81,6 +81,9 @@ std::vector<AdventureMapShortcutState> AdventureMapShortcuts::getShortcuts()
 		{ EShortcut::ADVENTURE_VISIT_OBJECT,     optionHeroSelected(),   [this]() { this->visitObject(); } },
 		{ EShortcut::ADVENTURE_VISIT_OBJECT,     optionHeroSelected(),   [this]() { this->visitObject(); } },
 		{ EShortcut::ADVENTURE_VIEW_SELECTED,    optionInMapView(),      [this]() { this->openObject(); } },
 		{ EShortcut::ADVENTURE_VIEW_SELECTED,    optionInMapView(),      [this]() { this->openObject(); } },
 		{ EShortcut::GAME_OPEN_MARKETPLACE,      optionInMapView(),      [this]() { this->showMarketplace(); } },
 		{ EShortcut::GAME_OPEN_MARKETPLACE,      optionInMapView(),      [this]() { this->showMarketplace(); } },
+		{ EShortcut::ADVENTURE_ZOOM_IN,          optionSidePanelActive(),[this]() { this->zoom(+1); } },
+		{ EShortcut::ADVENTURE_ZOOM_OUT,         optionSidePanelActive(),[this]() { this->zoom(-1); } },
+		{ EShortcut::ADVENTURE_ZOOM_RESET,       optionSidePanelActive(),[this]() { this->zoom( 0); } },
 		{ EShortcut::ADVENTURE_NEXT_TOWN,        optionInMapView(),      [this]() { this->nextTown(); } },
 		{ EShortcut::ADVENTURE_NEXT_TOWN,        optionInMapView(),      [this]() { this->nextTown(); } },
 		{ EShortcut::ADVENTURE_NEXT_OBJECT,      optionInMapView(),      [this]() { this->nextObject(); } },
 		{ EShortcut::ADVENTURE_NEXT_OBJECT,      optionInMapView(),      [this]() { this->nextObject(); } },
 		{ EShortcut::ADVENTURE_MOVE_HERO_SW,     optionHeroSelected(),   [this]() { this->moveHeroDirectional({-1, +1}); } },
 		{ EShortcut::ADVENTURE_MOVE_HERO_SW,     optionHeroSelected(),   [this]() { this->moveHeroDirectional({-1, +1}); } },
@@ -337,6 +340,11 @@ void AdventureMapShortcuts::nextTown()
 	owner.hotkeyNextTown();
 	owner.hotkeyNextTown();
 }
 }
 
 
+void AdventureMapShortcuts::zoom( int distance)
+{
+	owner.hotkeyZoom(distance);
+}
+
 void AdventureMapShortcuts::nextObject()
 void AdventureMapShortcuts::nextObject()
 {
 {
 	const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
 	const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();

+ 1 - 0
client/adventureMap/AdventureMapShortcuts.h

@@ -61,6 +61,7 @@ class AdventureMapShortcuts
 	void showMarketplace();
 	void showMarketplace();
 	void nextTown();
 	void nextTown();
 	void nextObject();
 	void nextObject();
+	void zoom( int distance);
 	void moveHeroDirectional(const Point & direction);
 	void moveHeroDirectional(const Point & direction);
 
 
 public:
 public:

+ 3 - 0
client/gui/Shortcut.h

@@ -105,6 +105,9 @@ enum class EShortcut
 	ADVENTURE_CAST_SPELL,
 	ADVENTURE_CAST_SPELL,
 	ADVENTURE_THIEVES_GUILD,
 	ADVENTURE_THIEVES_GUILD,
 	ADVENTURE_EXIT_WORLD_VIEW,
 	ADVENTURE_EXIT_WORLD_VIEW,
+	ADVENTURE_ZOOM_IN,
+	ADVENTURE_ZOOM_OUT,
+	ADVENTURE_ZOOM_RESET,
 
 
 	// Move hero one tile in specified direction. Bound to cursors & numpad buttons
 	// Move hero one tile in specified direction. Bound to cursors & numpad buttons
 	ADVENTURE_MOVE_HERO_SW,
 	ADVENTURE_MOVE_HERO_SW,

+ 6 - 0
client/gui/ShortcutHandler.cpp

@@ -116,6 +116,9 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(SDL_Keycode key) const
 		{SDLK_q,         EShortcut::ADVENTURE_QUEST_LOG       },
 		{SDLK_q,         EShortcut::ADVENTURE_QUEST_LOG       },
 		{SDLK_c,         EShortcut::ADVENTURE_CAST_SPELL      },
 		{SDLK_c,         EShortcut::ADVENTURE_CAST_SPELL      },
 		{SDLK_g,         EShortcut::ADVENTURE_THIEVES_GUILD   },
 		{SDLK_g,         EShortcut::ADVENTURE_THIEVES_GUILD   },
+		{SDLK_KP_PLUS,   EShortcut::ADVENTURE_ZOOM_IN         },
+		{SDLK_KP_MINUS,  EShortcut::ADVENTURE_ZOOM_OUT        },
+		{SDLK_BACKSPACE, EShortcut::ADVENTURE_ZOOM_RESET      },
 		{SDLK_q,         EShortcut::BATTLE_TOGGLE_QUEUE       },
 		{SDLK_q,         EShortcut::BATTLE_TOGGLE_QUEUE       },
 		{SDLK_c,         EShortcut::BATTLE_USE_CREATURE_SPELL },
 		{SDLK_c,         EShortcut::BATTLE_USE_CREATURE_SPELL },
 		{SDLK_s,         EShortcut::BATTLE_SURRENDER          },
 		{SDLK_s,         EShortcut::BATTLE_SURRENDER          },
@@ -253,6 +256,9 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
 		{"adventureCastSpell",       EShortcut::ADVENTURE_CAST_SPELL      },
 		{"adventureCastSpell",       EShortcut::ADVENTURE_CAST_SPELL      },
 		{"adventureThievesGuild",    EShortcut::ADVENTURE_THIEVES_GUILD   },
 		{"adventureThievesGuild",    EShortcut::ADVENTURE_THIEVES_GUILD   },
 		{"adventureExitWorldView",   EShortcut::ADVENTURE_EXIT_WORLD_VIEW },
 		{"adventureExitWorldView",   EShortcut::ADVENTURE_EXIT_WORLD_VIEW },
+		{"adventureZoomIn",          EShortcut::ADVENTURE_ZOOM_IN         },
+		{"adventureZoomOut",         EShortcut::ADVENTURE_ZOOM_OUT        },
+		{"adventureZoomReset",       EShortcut::ADVENTURE_ZOOM_RESET      },
 		{"battleToggleQueue",        EShortcut::BATTLE_TOGGLE_QUEUE       },
 		{"battleToggleQueue",        EShortcut::BATTLE_TOGGLE_QUEUE       },
 		{"battleUseCreatureSpell",   EShortcut::BATTLE_USE_CREATURE_SPELL },
 		{"battleUseCreatureSpell",   EShortcut::BATTLE_USE_CREATURE_SPELL },
 		{"battleSurrender",          EShortcut::BATTLE_SURRENDER          },
 		{"battleSurrender",          EShortcut::BATTLE_SURRENDER          },

+ 5 - 0
client/mapView/MapView.cpp

@@ -152,6 +152,11 @@ void MapView::onViewWorldActivated(uint32_t tileSize)
 	controller->setTileSize(Point(tileSize, tileSize));
 	controller->setTileSize(Point(tileSize, tileSize));
 }
 }
 
 
+void MapView::onMapZoomLevelChanged(int stepsChange)
+{
+	controller->modifyTileSize(stepsChange);
+}
+
 void MapView::onViewMapActivated()
 void MapView::onViewMapActivated()
 {
 {
 	controller->activateAdventureContext();
 	controller->activateAdventureContext();

+ 3 - 0
client/mapView/MapView.h

@@ -79,6 +79,9 @@ public:
 	/// Switches view to downscaled View World
 	/// Switches view to downscaled View World
 	void onViewWorldActivated(uint32_t tileSize);
 	void onViewWorldActivated(uint32_t tileSize);
 
 
+	/// Changes zoom level / tile size of current view by specified factor
+	void onMapZoomLevelChanged(int stepsChange);
+
 	/// Switches view from View World mode back to standard view
 	/// Switches view from View World mode back to standard view
 	void onViewMapActivated();
 	void onViewMapActivated();
 };
 };

+ 8 - 1
client/mapView/MapViewActions.cpp

@@ -29,7 +29,7 @@ MapViewActions::MapViewActions(MapView & owner, const std::shared_ptr<MapViewMod
 	pos.w = model->getPixelsVisibleDimensions().x;
 	pos.w = model->getPixelsVisibleDimensions().x;
 	pos.h = model->getPixelsVisibleDimensions().y;
 	pos.h = model->getPixelsVisibleDimensions().y;
 
 
-	addUsedEvents(LCLICK | RCLICK | MCLICK | HOVER | MOVE);
+	addUsedEvents(LCLICK | RCLICK | MCLICK | HOVER | MOVE | WHEEL);
 }
 }
 
 
 bool MapViewActions::swipeEnabled() const
 bool MapViewActions::swipeEnabled() const
@@ -92,6 +92,13 @@ void MapViewActions::mouseMoved(const Point & cursorPosition)
 	handleSwipeMove(cursorPosition);
 	handleSwipeMove(cursorPosition);
 }
 }
 
 
+void MapViewActions::wheelScrolled(bool down, bool in)
+{
+	if (!in)
+		return;
+	adventureInt->hotkeyZoom(down ? -1 : +1);
+}
+
 void MapViewActions::handleSwipeMove(const Point & cursorPosition)
 void MapViewActions::handleSwipeMove(const Point & cursorPosition)
 {
 {
 	// unless swipe is enabled, swipe move only works with middle mouse button
 	// unless swipe is enabled, swipe move only works with middle mouse button

+ 1 - 0
client/mapView/MapViewActions.h

@@ -42,4 +42,5 @@ public:
 	void clickMiddle(tribool down, bool previousState) override;
 	void clickMiddle(tribool down, bool previousState) override;
 	void hover(bool on) override;
 	void hover(bool on) override;
 	void mouseMoved(const Point & cursorPosition) override;
 	void mouseMoved(const Point & cursorPosition) override;
+	void wheelScrolled(bool down, bool in) override;
 };
 };

+ 4 - 2
client/mapView/MapViewCache.cpp

@@ -108,8 +108,9 @@ void MapViewCache::updateTile(const std::shared_ptr<IMapRendererContext> & conte
 void MapViewCache::update(const std::shared_ptr<IMapRendererContext> & context)
 void MapViewCache::update(const std::shared_ptr<IMapRendererContext> & context)
 {
 {
 	Rect dimensions = model->getTilesTotalRect();
 	Rect dimensions = model->getTilesTotalRect();
+	bool mapResized = cachedSize != model->getSingleTileSize();
 
 
-	if(dimensions.w != terrainChecksum.shape()[0] || dimensions.h != terrainChecksum.shape()[1])
+	if(mapResized || dimensions.w != terrainChecksum.shape()[0] || dimensions.h != terrainChecksum.shape()[1])
 	{
 	{
 		boost::multi_array<TileChecksum, 2> newCache;
 		boost::multi_array<TileChecksum, 2> newCache;
 		newCache.resize(boost::extents[dimensions.w][dimensions.h]);
 		newCache.resize(boost::extents[dimensions.w][dimensions.h]);
@@ -117,7 +118,7 @@ void MapViewCache::update(const std::shared_ptr<IMapRendererContext> & context)
 		terrainChecksum = newCache;
 		terrainChecksum = newCache;
 	}
 	}
 
 
-	if(dimensions.w != tilesUpToDate.shape()[0] || dimensions.h != tilesUpToDate.shape()[1])
+	if(mapResized || dimensions.w != tilesUpToDate.shape()[0] || dimensions.h != tilesUpToDate.shape()[1])
 	{
 	{
 		boost::multi_array<bool, 2> newCache;
 		boost::multi_array<bool, 2> newCache;
 		newCache.resize(boost::extents[dimensions.w][dimensions.h]);
 		newCache.resize(boost::extents[dimensions.w][dimensions.h]);
@@ -129,6 +130,7 @@ void MapViewCache::update(const std::shared_ptr<IMapRendererContext> & context)
 		for(int x = dimensions.left(); x < dimensions.right(); ++x)
 		for(int x = dimensions.left(); x < dimensions.right(); ++x)
 			updateTile(context, {x, y, model->getLevel()});
 			updateTile(context, {x, y, model->getLevel()});
 
 
+	cachedSize = model->getSingleTileSize();
 	cachedLevel = model->getLevel();
 	cachedLevel = model->getLevel();
 }
 }
 
 

+ 1 - 0
client/mapView/MapViewCache.h

@@ -41,6 +41,7 @@ class MapViewCache
 	boost::multi_array<TileChecksum, 2> terrainChecksum;
 	boost::multi_array<TileChecksum, 2> terrainChecksum;
 	boost::multi_array<bool, 2> tilesUpToDate;
 	boost::multi_array<bool, 2> tilesUpToDate;
 
 
+	Point cachedSize;
 	Point cachedPosition;
 	Point cachedPosition;
 	int cachedLevel;
 	int cachedLevel;
 
 

+ 40 - 1
client/mapView/MapViewController.cpp

@@ -70,10 +70,49 @@ void MapViewController::setViewCenter(const Point & position, int level)
 
 
 void MapViewController::setTileSize(const Point & tileSize)
 void MapViewController::setTileSize(const Point & tileSize)
 {
 {
+	Point oldSize = model->getSingleTileSize();
 	model->setTileSize(tileSize);
 	model->setTileSize(tileSize);
 
 
+	double scaleChangeX = 1.0 * tileSize.x / oldSize.x;
+	double scaleChangeY = 1.0 * tileSize.y / oldSize.y;
+
+	Point newViewCenter {
+		static_cast<int>(std::round(model->getMapViewCenter().x * scaleChangeX)),
+		static_cast<int>(std::round(model->getMapViewCenter().y * scaleChangeY))
+	};
+
 	// force update of view center since changing tile size may invalidated it
 	// force update of view center since changing tile size may invalidated it
-	setViewCenter(model->getMapViewCenter(), model->getLevel());
+	setViewCenter(newViewCenter, model->getLevel());
+}
+
+void MapViewController::modifyTileSize(int stepsChange)
+{
+	// we want to zoom in/out in fixed 10% steps, to allow player to return back to exactly 100% zoom just by scrolling
+	// so, zooming in for 5 steps will put game at 1.1^5 = 1.61 scale
+	// try to determine current zooming level and change it by requested number of steps
+	double currentZoomFactor = model->getSingleTileSize().x / 32.0;
+	double currentZoomSteps = std::round(std::log(currentZoomFactor) / std::log(1.1));
+	double newZoomSteps = stepsChange != 0 ? currentZoomSteps + stepsChange : stepsChange;
+	double newZoomFactor = std::pow(1.1, newZoomSteps);
+
+	Point currentZoom = model->getSingleTileSize();
+	Point desiredZoom = Point(32,32) * newZoomFactor;
+
+	if (desiredZoom == currentZoom && stepsChange < 0)
+		desiredZoom -= Point(1,1);
+
+	if (desiredZoom == currentZoom && stepsChange > 0)
+		desiredZoom += Point(1,1);
+
+	Point minimal = model->getSingleTileSizeLowerLimit();
+	Point maximal = model->getSingleTileSizeUpperLimit();
+	Point actualZoom = {
+		std::clamp(desiredZoom.x, minimal.x, maximal.x),
+		std::clamp(desiredZoom.y, minimal.y, maximal.y)
+	};
+
+	if (actualZoom != currentZoom)
+		setTileSize(actualZoom);
 }
 }
 
 
 MapViewController::MapViewController(std::shared_ptr<MapViewModel> model, std::shared_ptr<MapViewCache> view)
 MapViewController::MapViewController(std::shared_ptr<MapViewModel> model, std::shared_ptr<MapViewCache> view)

+ 1 - 0
client/mapView/MapViewController.h

@@ -83,6 +83,7 @@ public:
 	void setViewCenter(const int3 & position);
 	void setViewCenter(const int3 & position);
 	void setViewCenter(const Point & position, int level);
 	void setViewCenter(const Point & position, int level);
 	void setTileSize(const Point & tileSize);
 	void setTileSize(const Point & tileSize);
+	void modifyTileSize(int stepsChange);
 	void tick(uint32_t timePassed);
 	void tick(uint32_t timePassed);
 	void afterRender();
 	void afterRender();
 
 

+ 16 - 4
client/mapView/MapViewModel.cpp

@@ -33,6 +33,18 @@ void MapViewModel::setLevel(int newLevel)
 	mapLevel = newLevel;
 	mapLevel = newLevel;
 }
 }
 
 
+Point MapViewModel::getSingleTileSizeUpperLimit() const
+{
+	// arbitrary-seleted upscaling limit
+	return Point(256, 256);
+}
+
+Point MapViewModel::getSingleTileSizeLowerLimit() const
+{
+	// arbitrary-seleted downscaling limit
+	return Point(4, 4);
+}
+
 Point MapViewModel::getSingleTileSize() const
 Point MapViewModel::getSingleTileSize() const
 {
 {
 	return tileSize;
 	return tileSize;
@@ -90,7 +102,7 @@ int3 MapViewModel::getTileAtPoint(const Point & position) const
 
 
 Point MapViewModel::getCacheDimensionsPixels() const
 Point MapViewModel::getCacheDimensionsPixels() const
 {
 {
-	return getTilesVisibleDimensions() * getSingleTileSize();
+	return getTilesVisibleDimensions() * getSingleTileSizeUpperLimit();
 }
 }
 
 
 Rect MapViewModel::getCacheTileArea(const int3 & coordinates) const
 Rect MapViewModel::getCacheTileArea(const int3 & coordinates) const
@@ -104,14 +116,14 @@ Rect MapViewModel::getCacheTileArea(const int3 & coordinates) const
 		(getTilesVisibleDimensions().y + coordinates.y) % getTilesVisibleDimensions().y
 		(getTilesVisibleDimensions().y + coordinates.y) % getTilesVisibleDimensions().y
 	};
 	};
 
 
-	return Rect(tileIndex * tileSize, tileSize);
+	return Rect(tileIndex * getSingleTileSize(), getSingleTileSize());
 }
 }
 
 
 Rect MapViewModel::getTargetTileArea(const int3 & coordinates) const
 Rect MapViewModel::getTargetTileArea(const int3 & coordinates) const
 {
 {
 	Point topLeftOffset = getMapViewCenter() - getPixelsVisibleDimensions() / 2;
 	Point topLeftOffset = getMapViewCenter() - getPixelsVisibleDimensions() / 2;
-	Point tilePosAbsolute = Point(coordinates) * tileSize;
+	Point tilePosAbsolute = Point(coordinates) * getSingleTileSize();
 	Point tilePosRelative = tilePosAbsolute - topLeftOffset;
 	Point tilePosRelative = tilePosAbsolute - topLeftOffset;
 
 
-	return Rect(tilePosRelative, tileSize);
+	return Rect(tilePosRelative, getSingleTileSize());
 }
 }

+ 6 - 0
client/mapView/MapViewModel.h

@@ -25,6 +25,12 @@ public:
 	void setViewDimensions(const Point & newValue);
 	void setViewDimensions(const Point & newValue);
 	void setLevel(int newLevel);
 	void setLevel(int newLevel);
 
 
+	/// returns maximal possible size for a single tile
+	Point getSingleTileSizeUpperLimit() const;
+
+	/// returns minimal possible size for a single tile
+	Point getSingleTileSizeLowerLimit() const;
+
 	/// returns current size of map tile in pixels
 	/// returns current size of map tile in pixels
 	Point getSingleTileSize() const;
 	Point getSingleTileSize() const;