mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +02:00
Merge pull request #2163 from IvanSavenko/adventure_map_zoom
Adventure map zoom
This commit is contained in:
@@ -803,6 +803,11 @@ void AdventureMapInterface::hotkeySwitchMapLevel()
|
||||
widget->getMapView()->onMapLevelSwitched();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::hotkeyZoom(int delta)
|
||||
{
|
||||
widget->getMapView()->onMapZoomLevelChanged(delta);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onScreenResize()
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
@@ -109,6 +109,7 @@ public:
|
||||
void hotkeyEndingTurn();
|
||||
void hotkeyNextTown();
|
||||
void hotkeySwitchMapLevel();
|
||||
void hotkeyZoom(int delta);
|
||||
|
||||
/// Called by PlayerInterface when specified player is ready to start his turn
|
||||
void onHotseatWaitStarted(PlayerColor playerID);
|
||||
|
@@ -81,6 +81,9 @@ std::vector<AdventureMapShortcutState> AdventureMapShortcuts::getShortcuts()
|
||||
{ EShortcut::ADVENTURE_VISIT_OBJECT, optionHeroSelected(), [this]() { this->visitObject(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_SELECTED, optionInMapView(), [this]() { this->openObject(); } },
|
||||
{ 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_OBJECT, optionInMapView(), [this]() { this->nextObject(); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_SW, optionHeroSelected(), [this]() { this->moveHeroDirectional({-1, +1}); } },
|
||||
@@ -337,6 +340,11 @@ void AdventureMapShortcuts::nextTown()
|
||||
owner.hotkeyNextTown();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::zoom( int distance)
|
||||
{
|
||||
owner.hotkeyZoom(distance);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::nextObject()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
|
@@ -61,6 +61,7 @@ class AdventureMapShortcuts
|
||||
void showMarketplace();
|
||||
void nextTown();
|
||||
void nextObject();
|
||||
void zoom( int distance);
|
||||
void moveHeroDirectional(const Point & direction);
|
||||
|
||||
public:
|
||||
|
@@ -105,6 +105,9 @@ enum class EShortcut
|
||||
ADVENTURE_CAST_SPELL,
|
||||
ADVENTURE_THIEVES_GUILD,
|
||||
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
|
||||
ADVENTURE_MOVE_HERO_SW,
|
||||
|
@@ -116,6 +116,9 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(SDL_Keycode key) const
|
||||
{SDLK_q, EShortcut::ADVENTURE_QUEST_LOG },
|
||||
{SDLK_c, EShortcut::ADVENTURE_CAST_SPELL },
|
||||
{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_c, EShortcut::BATTLE_USE_CREATURE_SPELL },
|
||||
{SDLK_s, EShortcut::BATTLE_SURRENDER },
|
||||
@@ -253,6 +256,9 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
||||
{"adventureCastSpell", EShortcut::ADVENTURE_CAST_SPELL },
|
||||
{"adventureThievesGuild", EShortcut::ADVENTURE_THIEVES_GUILD },
|
||||
{"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 },
|
||||
{"battleUseCreatureSpell", EShortcut::BATTLE_USE_CREATURE_SPELL },
|
||||
{"battleSurrender", EShortcut::BATTLE_SURRENDER },
|
||||
|
@@ -152,6 +152,11 @@ void MapView::onViewWorldActivated(uint32_t tileSize)
|
||||
controller->setTileSize(Point(tileSize, tileSize));
|
||||
}
|
||||
|
||||
void MapView::onMapZoomLevelChanged(int stepsChange)
|
||||
{
|
||||
controller->modifyTileSize(stepsChange);
|
||||
}
|
||||
|
||||
void MapView::onViewMapActivated()
|
||||
{
|
||||
controller->activateAdventureContext();
|
||||
|
@@ -79,6 +79,9 @@ public:
|
||||
/// Switches view to downscaled View World
|
||||
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
|
||||
void onViewMapActivated();
|
||||
};
|
||||
|
@@ -29,7 +29,7 @@ MapViewActions::MapViewActions(MapView & owner, const std::shared_ptr<MapViewMod
|
||||
pos.w = model->getPixelsVisibleDimensions().x;
|
||||
pos.h = model->getPixelsVisibleDimensions().y;
|
||||
|
||||
addUsedEvents(LCLICK | RCLICK | MCLICK | HOVER | MOVE);
|
||||
addUsedEvents(LCLICK | RCLICK | MCLICK | HOVER | MOVE | WHEEL);
|
||||
}
|
||||
|
||||
bool MapViewActions::swipeEnabled() const
|
||||
@@ -92,6 +92,13 @@ void MapViewActions::mouseMoved(const Point & cursorPosition)
|
||||
handleSwipeMove(cursorPosition);
|
||||
}
|
||||
|
||||
void MapViewActions::wheelScrolled(bool down, bool in)
|
||||
{
|
||||
if (!in)
|
||||
return;
|
||||
adventureInt->hotkeyZoom(down ? -1 : +1);
|
||||
}
|
||||
|
||||
void MapViewActions::handleSwipeMove(const Point & cursorPosition)
|
||||
{
|
||||
// unless swipe is enabled, swipe move only works with middle mouse button
|
||||
|
@@ -42,4 +42,5 @@ public:
|
||||
void clickMiddle(tribool down, bool previousState) override;
|
||||
void hover(bool on) override;
|
||||
void mouseMoved(const Point & cursorPosition) override;
|
||||
void wheelScrolled(bool down, bool in) override;
|
||||
};
|
||||
|
@@ -108,8 +108,9 @@ void MapViewCache::updateTile(const std::shared_ptr<IMapRendererContext> & conte
|
||||
void MapViewCache::update(const std::shared_ptr<IMapRendererContext> & context)
|
||||
{
|
||||
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;
|
||||
newCache.resize(boost::extents[dimensions.w][dimensions.h]);
|
||||
@@ -117,7 +118,7 @@ void MapViewCache::update(const std::shared_ptr<IMapRendererContext> & context)
|
||||
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;
|
||||
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)
|
||||
updateTile(context, {x, y, model->getLevel()});
|
||||
|
||||
cachedSize = model->getSingleTileSize();
|
||||
cachedLevel = model->getLevel();
|
||||
}
|
||||
|
||||
|
@@ -41,6 +41,7 @@ class MapViewCache
|
||||
boost::multi_array<TileChecksum, 2> terrainChecksum;
|
||||
boost::multi_array<bool, 2> tilesUpToDate;
|
||||
|
||||
Point cachedSize;
|
||||
Point cachedPosition;
|
||||
int cachedLevel;
|
||||
|
||||
|
@@ -70,10 +70,49 @@ void MapViewController::setViewCenter(const Point & position, int level)
|
||||
|
||||
void MapViewController::setTileSize(const Point & tileSize)
|
||||
{
|
||||
Point oldSize = model->getSingleTileSize();
|
||||
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
|
||||
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)
|
||||
|
@@ -83,6 +83,7 @@ public:
|
||||
void setViewCenter(const int3 & position);
|
||||
void setViewCenter(const Point & position, int level);
|
||||
void setTileSize(const Point & tileSize);
|
||||
void modifyTileSize(int stepsChange);
|
||||
void tick(uint32_t timePassed);
|
||||
void afterRender();
|
||||
|
||||
|
@@ -33,6 +33,18 @@ void MapViewModel::setLevel(int 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
|
||||
{
|
||||
return tileSize;
|
||||
@@ -90,7 +102,7 @@ int3 MapViewModel::getTileAtPoint(const Point & position) const
|
||||
|
||||
Point MapViewModel::getCacheDimensionsPixels() const
|
||||
{
|
||||
return getTilesVisibleDimensions() * getSingleTileSize();
|
||||
return getTilesVisibleDimensions() * getSingleTileSizeUpperLimit();
|
||||
}
|
||||
|
||||
Rect MapViewModel::getCacheTileArea(const int3 & coordinates) const
|
||||
@@ -104,14 +116,14 @@ Rect MapViewModel::getCacheTileArea(const int3 & coordinates) const
|
||||
(getTilesVisibleDimensions().y + coordinates.y) % getTilesVisibleDimensions().y
|
||||
};
|
||||
|
||||
return Rect(tileIndex * tileSize, tileSize);
|
||||
return Rect(tileIndex * getSingleTileSize(), getSingleTileSize());
|
||||
}
|
||||
|
||||
Rect MapViewModel::getTargetTileArea(const int3 & coordinates) const
|
||||
{
|
||||
Point topLeftOffset = getMapViewCenter() - getPixelsVisibleDimensions() / 2;
|
||||
Point tilePosAbsolute = Point(coordinates) * tileSize;
|
||||
Point tilePosAbsolute = Point(coordinates) * getSingleTileSize();
|
||||
Point tilePosRelative = tilePosAbsolute - topLeftOffset;
|
||||
|
||||
return Rect(tilePosRelative, tileSize);
|
||||
return Rect(tilePosRelative, getSingleTileSize());
|
||||
}
|
||||
|
@@ -25,6 +25,12 @@ public:
|
||||
void setViewDimensions(const Point & newValue);
|
||||
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
|
||||
Point getSingleTileSize() const;
|
||||
|
||||
|
Reference in New Issue
Block a user