1
0
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:
Ivan Savenko
2023-05-20 13:35:53 +03:00
committed by GitHub
16 changed files with 109 additions and 8 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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();

View File

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

View File

@@ -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,

View File

@@ -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 },

View File

@@ -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();

View File

@@ -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();
};

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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();
}

View File

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

View File

@@ -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)

View File

@@ -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();

View File

@@ -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());
}

View File

@@ -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;