From 387a7b421aa7194138a191afd76a8572f9b6ab83 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 26 Feb 2023 18:17:53 +0200 Subject: [PATCH] Refactoring of new code, multiple functionality changes: - Implemented separate Map View contexts for different map modes - Puzzle map now works - Enemy movement now works as in H3 - Removed no longer used code - Implemented missing embark/disembark sounds - Fixed view focusing on embarking/disembarking - Fixed focusing on movement near edge of terra incognita - Fixed sea movement sound --- client/CMakeLists.txt | 2 + client/CPlayerInterface.cpp | 148 +--- client/NetPacksClient.cpp | 38 +- client/mapRenderer/IMapRendererObserver.h | 14 +- client/mapRenderer/MapRendererContext.cpp | 639 ++++++++++-------- client/mapRenderer/MapRendererContext.h | 185 ++--- .../mapRenderer/MapRendererContextState.cpp | 93 +++ client/mapRenderer/MapRendererContextState.h | 62 ++ client/mapRenderer/MapView.cpp | 45 +- client/mapRenderer/MapView.h | 42 +- client/mapRenderer/MapViewCache.cpp | 27 + client/mapRenderer/MapViewCache.h | 4 + client/mapRenderer/MapViewController.cpp | 457 +++++++++---- client/mapRenderer/MapViewController.h | 53 +- client/mapRenderer/mapHandler.cpp | 85 +-- client/mapRenderer/mapHandler.h | 8 +- client/windows/CPuzzleWindow.cpp | 13 +- client/windows/CPuzzleWindow.h | 2 + config/terrains.json | 2 +- lib/NetPacks.h | 2 - mapeditor/maphandler.cpp | 1 - 21 files changed, 1152 insertions(+), 770 deletions(-) create mode 100644 client/mapRenderer/MapRendererContextState.cpp create mode 100644 client/mapRenderer/MapRendererContextState.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 0d4e57b63..b7d1f5580 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -48,6 +48,7 @@ set(client_SRCS mapRenderer/MapRenderer.cpp mapRenderer/MapRendererContext.cpp + mapRenderer/MapRendererContextState.cpp mapRenderer/MapView.cpp mapRenderer/MapViewActions.cpp mapRenderer/MapViewCache.cpp @@ -170,6 +171,7 @@ set(client_HEADERS mapRenderer/IMapRendererObserver.h mapRenderer/MapRenderer.h mapRenderer/MapRendererContext.h + mapRenderer/MapRendererContextState.h mapRenderer/MapView.h mapRenderer/MapViewActions.h mapRenderer/MapViewCache.h diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 3797a8058..7dee70b3a 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -328,6 +328,12 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose) if (!hero) return; + if (details.result == TryMoveHero::EMBARK || details.result == TryMoveHero::DISEMBARK) + { + if (hero->getRemovalSound()) + CCS->soundh->playSound(hero->getRemovalSound().get()); + } + adventureInt->minimap->updateTile(hero->convertToVisitablePos(details.start)); adventureInt->minimap->updateTile(hero->convertToVisitablePos(details.end)); @@ -1603,149 +1609,7 @@ int CPlayerInterface::getLastIndex( std::string namePrefix) return (--dates.end())->second; //return latest file number return 0; } -/* -void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroInstance * ho, const int3 &hp ) -{ - auto subArr = (CGI->mh->ttiles)[hp.z]; - int heroWidth = ho->appearance->getWidth(); - int heroHeight = ho->appearance->getHeight(); - - int tileMinX = std::min(details.start.x, details.end.x) - heroWidth; - int tileMaxX = std::max(details.start.x, details.end.x); - int tileMinY = std::min(details.start.y, details.end.y) - heroHeight; - int tileMaxY = std::max(details.start.y, details.end.y); - - // determine tiles on which hero will be visible during movement and add hero as visible object on these tiles where necessary - for ( int tileX = tileMinX; tileX <= tileMaxX; ++tileX) - { - for ( int tileY = tileMinY; tileY <= tileMaxY; ++tileY) - { - bool heroVisibleHere = false; - auto & tile = subArr[tileX][tileY]; - - for(const auto & obj : tile.objects) - { - if (obj.obj == ho) - { - heroVisibleHere = true; - break; - } - } - - if ( !heroVisibleHere) - { - tile.objects.push_back(TerrainTileObject(ho, {0,0,32,32})); - std::stable_sort(tile.objects.begin(), tile.objects.end(), objectBlitOrderSorter); - } - } - } -} - -void CPlayerInterface::movementPxStep( const TryMoveHero &details, int i, const int3 &hp, const CGHeroInstance * ho ) -{ - auto subArr = (CGI->mh->ttiles)[hp.z]; - - int heroWidth = ho->appearance->getWidth(); - int heroHeight = ho->appearance->getHeight(); - - int tileMinX = std::min(details.start.x, details.end.x) - heroWidth; - int tileMaxX = std::max(details.start.x, details.end.x); - int tileMinY = std::min(details.start.y, details.end.y) - heroHeight; - int tileMaxY = std::max(details.start.y, details.end.y); - - std::shared_ptr animation = graphics->getAnimation(ho); - - assert(animation); - assert(animation->size(0) != 0); - auto image = animation->getImage(0,0); - - int heroImageOldX = details.start.x * 32; - int heroImageOldY = details.start.y * 32; - - int heroImageNewX = details.end.x * 32; - int heroImageNewY = details.end.y * 32; - - int heroImageCurrX = heroImageOldX + i*(heroImageNewX - heroImageOldX)/32; - int heroImageCurrY = heroImageOldY + i*(heroImageNewY - heroImageOldY)/32; - - // recompute which part of hero sprite will be visible on each tile at this point of movement animation - for ( int tileX = tileMinX; tileX <= tileMaxX; ++tileX) - { - for ( int tileY = tileMinY; tileY <= tileMaxY; ++tileY) - { - auto & tile = subArr[tileX][tileY]; - for ( auto & obj : tile.objects) - { - if (obj.obj == ho) - { - int tilePosX = tileX * 32; - int tilePosY = tileY * 32; - - obj.rect.x = tilePosX - heroImageCurrX + image->width() - 32; - obj.rect.y = tilePosY - heroImageCurrY + image->height() - 32; - } - } - } - } - - //adventureInt->terrain->moveX = (32 - i) * (heroImageNewX - heroImageOldX) / 32; - //adventureInt->terrain->moveY = (32 - i) * (heroImageNewY - heroImageOldY) / 32; -} - -void CPlayerInterface::finishMovement( const TryMoveHero &details, const int3 &hp, const CGHeroInstance * ho ) -{ - auto subArr = (CGI->mh->ttiles)[hp.z]; - - int heroWidth = ho->appearance->getWidth(); - int heroHeight = ho->appearance->getHeight(); - - int tileMinX = std::min(details.start.x, details.end.x) - heroWidth; - int tileMaxX = std::max(details.start.x, details.end.x); - int tileMinY = std::min(details.start.y, details.end.y) - heroHeight; - int tileMaxY = std::max(details.start.y, details.end.y); - - // erase hero from all tiles on which he is currently visible - for ( int tileX = tileMinX; tileX <= tileMaxX; ++tileX) - { - for ( int tileY = tileMinY; tileY <= tileMaxY; ++tileY) - { - auto & tile = subArr[tileX][tileY]; - for (size_t i = 0; i < tile.objects.size(); ++i) - { - if ( tile.objects[i].obj == ho) - { - tile.objects.erase(tile.objects.begin() + i); - break; - } - } - } - } - - // re-add hero to all tiles on which he will still be visible after animation is over - for ( int tileX = details.end.x - heroWidth + 1; tileX <= details.end.x; ++tileX) - { - for ( int tileY = details.end.y - heroHeight + 1; tileY <= details.end.y; ++tileY) - { - auto & tile = subArr[tileX][tileY]; - tile.objects.push_back(TerrainTileObject(ho, {0,0,32,32})); - } - } - - // update object list on all tiles that were affected during previous operations - for ( int tileX = tileMinX; tileX <= tileMaxX; ++tileX) - { - for ( int tileY = tileMinY; tileY <= tileMaxY; ++tileY) - { - auto & tile = subArr[tileX][tileY]; - std::stable_sort(tile.objects.begin(), tile.objects.end(), objectBlitOrderSorter); - } - } - - //recompute hero sprite positioning using hero's final position - movementPxStep(details, 32, hp, ho); -} -*/ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult ) { EVENT_HANDLER_CALLED_BY_CLIENT; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index aad6d47e9..2342224c0 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -444,20 +444,20 @@ void ApplyFirstClientNetPackVisitor::visitTryMoveHero(TryMoveHero & pack) { CGHeroInstance *h = gs.getHero(pack.id); - //check if playerint will have the knowledge about movement - if not, directly update maphandler - for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++) + if(CGI->mh) { - auto ps = gs.getPlayerState(i->first); - if(ps && (gs.isVisible(h->convertToVisitablePos(pack.start), i->first) || gs.isVisible(h->convertToVisitablePos(pack.end), i->first))) + switch (pack.result) { - if(ps->human) - pack.humanKnows = true; + case TryMoveHero::EMBARK: + CGI->mh->onBeforeHeroEmbark(h, pack.start, pack.end); + break; + case TryMoveHero::TELEPORTATION: + CGI->mh->onBeforeHeroTeleported(h, pack.start, pack.end); + break; + case TryMoveHero::DISEMBARK: + CGI->mh->onBeforeHeroDisembark(h, pack.start, pack.end); + break; } - } - - if(CGI->mh && pack.result == TryMoveHero::EMBARK) - { - CGI->mh->onObjectFadeOut(h); CGI->mh->waitForOngoingAnimations(); } } @@ -471,23 +471,17 @@ void ApplyClientNetPackVisitor::visitTryMoveHero(TryMoveHero & pack) { switch(pack.result) { - case TryMoveHero::FAILED: - break; // no-op case TryMoveHero::SUCCESS: CGI->mh->onHeroMoved(h, pack.start, pack.end); break; - case TryMoveHero::TELEPORTATION: - CGI->mh->onHeroTeleported(h, pack.start, pack.end); - break; - case TryMoveHero::BLOCKING_VISIT: - CGI->mh->onHeroRotated(h, pack.start, pack.end); - break; case TryMoveHero::EMBARK: - // handled in ApplyFirst - //CGI->mh->onObjectFadeOut(h); + CGI->mh->onAfterHeroEmbark(h, pack.start, pack.end); + break; + case TryMoveHero::TELEPORTATION: + CGI->mh->onAfterHeroTeleported(h, pack.start, pack.end); break; case TryMoveHero::DISEMBARK: - CGI->mh->onObjectFadeIn(h); + CGI->mh->onAfterHeroDisembark(h, pack.start, pack.end); break; } } diff --git a/client/mapRenderer/IMapRendererObserver.h b/client/mapRenderer/IMapRendererObserver.h index f22502d72..a2e7f610f 100644 --- a/client/mapRenderer/IMapRendererObserver.h +++ b/client/mapRenderer/IMapRendererObserver.h @@ -37,12 +37,16 @@ public: /// Removes object from map instantly, with no animation virtual void onObjectInstantRemove(const CGObjectInstance * obj) {} - /// Perform hero teleportation animation with terrain fade animation - virtual void onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} - /// Perform hero movement animation, moving hero across terrain virtual void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} - /// Instantly rotates hero to face destination tile - virtual void onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} + /// Perform initialization of hero teleportation animation with terrain fade animation + virtual void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} + virtual void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} + + virtual void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}; + virtual void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}; + + virtual void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}; + virtual void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}; }; diff --git a/client/mapRenderer/MapRendererContext.cpp b/client/mapRenderer/MapRendererContext.cpp index fefea2fca..a0051559c 100644 --- a/client/mapRenderer/MapRendererContext.cpp +++ b/client/mapRenderer/MapRendererContext.cpp @@ -1,5 +1,5 @@ /* - * MapRendererContext.cpp, part of VCMI engine + * MapRendererContextState.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * @@ -11,305 +11,103 @@ #include "StdInc.h" #include "MapRendererContext.h" +#include "MapRendererContextState.h" #include "mapHandler.h" +#include "../../CCallback.h" #include "../CGameInfo.h" #include "../CPlayerInterface.h" #include "../adventureMap/CAdvMapInt.h" -#include "../../CCallback.h" +#include "../../lib/Point.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapping/CMap.h" +#include "../../lib/CPathfinder.h" -MapObjectsSorter::MapObjectsSorter(IMapRendererContext & context) - : context(context) +MapRendererBaseContext::MapRendererBaseContext(const MapRendererContextState & viewState) + : viewState(viewState) { } -bool MapObjectsSorter::operator()(const ObjectInstanceID & left, const ObjectInstanceID & right) const -{ - return (*this)(context.getObject(left), context.getObject(right)); -} - -bool MapObjectsSorter::operator()(const CGObjectInstance * left, const CGObjectInstance * right) const -{ - //FIXME: remove mh access - return CGI->mh->compareObjectBlitOrder(left, right); -} - -int3 MapRendererContext::getMapSize() const -{ - return LOCPLINT->cb->getMapSize(); -} - -bool MapRendererContext::isInMap(const int3 & coordinates) const -{ - return LOCPLINT->cb->isInTheMap(coordinates); -} - -const TerrainTile & MapRendererContext::getMapTile(const int3 & coordinates) const -{ - return CGI->mh->getMap()->getTile(coordinates); -} - -const CGObjectInstance * MapRendererContext::getObject(ObjectInstanceID objectID) const -{ - return CGI->mh->getMap()->objects.at(objectID.getNum()); -} - -bool MapRendererContext::isVisible(const int3 & coordinates) const -{ - if (settingsSessionSpectate || showAllTerrain) - return LOCPLINT->cb->isInTheMap(coordinates); - return LOCPLINT->cb->isVisible(coordinates); -} - -const CGPath * MapRendererContext::currentPath() const -{ - if (worldViewModeActive) - return nullptr; - - const auto * hero = adventureInt->curHero(); - - if(!hero) - return nullptr; - - if(!LOCPLINT->paths.hasPath(hero)) - return nullptr; - - return &LOCPLINT->paths.getPath(hero); -} - -size_t MapRendererContext::objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const -{ - assert(groupSize > 0); - if(groupSize == 0) - return 0; - - if (!settingsAdventureObjectAnimation) - return 0; - - if (worldViewModeActive) - return 0; - - // H3 timing for adventure map objects animation is 180 ms - // Terrain animations also use identical interval, however those are only present in HotA and/or HD Mod - size_t baseFrameTime = 180; - - // hero movement animation always plays at ~50ms / frame - // in-game setting only affect movement across screen - if(movementAnimation && movementAnimation->target == objectID) - baseFrameTime = 50; - - size_t frameCounter = animationTime / baseFrameTime; - size_t frameIndex = frameCounter % groupSize; - return frameIndex; -} - -size_t MapRendererContext::terrainImageIndex(size_t groupSize) const -{ - if (!settingsAdventureTerrainAnimation) - return 0; - - if (worldViewModeActive) - return 0; - - size_t baseFrameTime = 180; - size_t frameCounter = animationTime / baseFrameTime; - size_t frameIndex = frameCounter % groupSize; - return frameIndex; -} - -bool MapRendererContext::tileAnimated(const int3 & coordinates) const -{ - if (!isInMap(coordinates)) - return false; - - if(movementAnimation) - { - auto objects = getObjects(coordinates); - - if(vstd::contains(objects, movementAnimation->target)) - return true; - } - - if(fadeInAnimation) - { - auto objects = getObjects(coordinates); - - if(vstd::contains(objects, fadeInAnimation->target)) - return true; - } - - if(fadeOutAnimation) - { - auto objects = getObjects(coordinates); - - if(vstd::contains(objects, fadeOutAnimation->target)) - return true; - } - return false; -} - -bool MapRendererContext::filterGrayscale() const -{ - return false; -} - -bool MapRendererContext::showRoads() const -{ - return true; -} - -bool MapRendererContext::showRivers() const -{ - return true; -} - -bool MapRendererContext::showBorder() const -{ - return !worldViewModeActive; -} - -bool MapRendererContext::showOverlay() const -{ - return worldViewModeActive; -} - -bool MapRendererContext::showGrid() const -{ - return settingsSessionShowGrid; -} - -bool MapRendererContext::showVisitable() const -{ - return settingsSessionShowVisitable; -} - -bool MapRendererContext::showBlockable() const -{ - return settingsSessionShowBlockable; -} - -MapRendererContext::MapRendererContext() -{ - auto mapSize = getMapSize(); - - objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]); - - for(const auto & obj : CGI->mh->getMap()->objects) - addObject(obj); -} - -void MapRendererContext::addObject(const CGObjectInstance * obj) -{ - if(!obj) - return; - - for(int fx = 0; fx < obj->getWidth(); ++fx) - { - for(int fy = 0; fy < obj->getHeight(); ++fy) - { - int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z); - - if(isInMap(currTile) && obj->coveringAt(currTile.x, currTile.y)) - { - auto & container = objects[currTile.z][currTile.x][currTile.y]; - - container.push_back(obj->id); - boost::range::sort(container, MapObjectsSorter(*this)); - } - } - } -} - -void MapRendererContext::addMovingObject(const CGObjectInstance * object, const int3 & tileFrom, const int3 & tileDest) -{ - int xFrom = std::min(tileFrom.x, tileDest.x) - object->getWidth(); - int xDest = std::max(tileFrom.x, tileDest.x); - int yFrom = std::min(tileFrom.y, tileDest.y) - object->getHeight(); - int yDest = std::max(tileFrom.y, tileDest.y); - - for(int x = xFrom; x <= xDest; ++x) - { - for(int y = yFrom; y <= yDest; ++y) - { - int3 currTile(x, y, object->pos.z); - - if(isInMap(currTile)) - { - auto & container = objects[currTile.z][currTile.x][currTile.y]; - - container.push_back(object->id); - boost::range::sort(container, MapObjectsSorter(*this)); - } - } - } -} - -void MapRendererContext::removeObject(const CGObjectInstance * object) -{ - for(int z = 0; z < getMapSize().z; z++) - for(int x = 0; x < getMapSize().x; x++) - for(int y = 0; y < getMapSize().y; y++) - vstd::erase(objects[z][x][y], object->id); -} - -const MapRendererContext::MapObjectsList & MapRendererContext::getObjects(const int3 & coordinates) const -{ - assert(isInMap(coordinates)); - return objects[coordinates.z][coordinates.x][coordinates.y]; -} - -size_t MapRendererContext::objectGroupIndex(ObjectInstanceID objectID) const +uint32_t MapRendererBaseContext::getObjectRotation(ObjectInstanceID objectID) const { const CGObjectInstance * obj = getObject(objectID); - // TODO - static const std::vector moveGroups = {99, 10, 5, 6, 7, 8, 9, 12, 11}; - static const std::vector idleGroups = {99, 13, 0, 1, 2, 3, 4, 15, 14}; if(obj->ID == Obj::HERO) { const auto * hero = dynamic_cast(obj); - if(movementAnimation && movementAnimation->target == objectID) - return moveGroups[hero->moveDir]; - return idleGroups[hero->moveDir]; + return hero->moveDir; } if(obj->ID == Obj::BOAT) { const auto * boat = dynamic_cast(obj); - uint8_t direction = boat->hero ? boat->hero->moveDir : boat->direction; - - if(movementAnimation && movementAnimation->target == objectID) - return moveGroups[direction]; - return idleGroups[direction]; + if(boat->hero) + return boat->hero->moveDir; + return boat->direction; } return 0; } -Point MapRendererContext::objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const +int3 MapRendererBaseContext::getMapSize() const { - if(movementAnimation && movementAnimation->target == objectID) - { - int3 offsetTilesFrom = movementAnimation->tileFrom - coordinates; - int3 offsetTilesDest = movementAnimation->tileDest - coordinates; + return LOCPLINT->cb->getMapSize(); +} - Point offsetPixelsFrom = Point(offsetTilesFrom) * Point(32, 32); - Point offsetPixelsDest = Point(offsetTilesDest) * Point(32, 32); +bool MapRendererBaseContext::isInMap(const int3 & coordinates) const +{ + return LOCPLINT->cb->isInTheMap(coordinates); +} - Point result = vstd::lerp(offsetPixelsFrom, offsetPixelsDest, movementAnimation->progress); +bool MapRendererBaseContext::isVisible(const int3 & coordinates) const +{ + if(settingsSessionSpectate) + return LOCPLINT->cb->isInTheMap(coordinates); + else + return LOCPLINT->cb->isVisible(coordinates); +} - return result; - } +bool MapRendererBaseContext::tileAnimated(const int3 & coordinates) const +{ + return false; +} +const TerrainTile & MapRendererBaseContext::getMapTile(const int3 & coordinates) const +{ + return CGI->mh->getMap()->getTile(coordinates); +} + +const MapRendererBaseContext::MapObjectsList & MapRendererBaseContext::getObjects(const int3 & coordinates) const +{ + assert(isInMap(coordinates)); + return viewState.objects[coordinates.z][coordinates.x][coordinates.y]; +} + +const CGObjectInstance * MapRendererBaseContext::getObject(ObjectInstanceID objectID) const +{ + return CGI->mh->getMap()->objects.at(objectID.getNum()); +} + +const CGPath * MapRendererBaseContext::currentPath() const +{ + return nullptr; +} + +size_t MapRendererBaseContext::objectGroupIndex(ObjectInstanceID objectID) const +{ + static const std::vector idleGroups = {0, 13, 0, 1, 2, 3, 4, 15, 14}; + return idleGroups[getObjectRotation(objectID)]; +} + +Point MapRendererBaseContext::objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const +{ const CGObjectInstance * object = getObject(objectID); int3 offsetTiles(object->getPosition() - coordinates); return Point(offsetTiles) * Point(32, 32); } -double MapRendererContext::objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const +double MapRendererBaseContext::objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const { const CGObjectInstance * object = getObject(objectID); @@ -323,23 +121,213 @@ double MapRendererContext::objectTransparency(ObjectInstanceID objectID, const i if(hero->boat) return 0; } + return 1; +} - if(showAllTerrain) - { - if(object->isVisitable() && !LOCPLINT->cb->isVisible(coordinates)) - return 0; - } +size_t MapRendererBaseContext::objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const +{ + return 0; +} - if(fadeOutAnimation && objectID == fadeOutAnimation->target) - return 1.0 - fadeOutAnimation->progress; +size_t MapRendererBaseContext::terrainImageIndex(size_t groupSize) const +{ + return 0; +} - if(fadeInAnimation && objectID == fadeInAnimation->target) - return fadeInAnimation->progress; +size_t MapRendererBaseContext::overlayImageIndex(const int3 & coordinates) const +{ + return std::numeric_limits::max(); +} + +bool MapRendererBaseContext::filterGrayscale() const +{ + return false; +} + +bool MapRendererBaseContext::showRoads() const +{ + return true; +} + +bool MapRendererBaseContext::showRivers() const +{ + return true; +} + +bool MapRendererBaseContext::showBorder() const +{ + return false; +} + +bool MapRendererBaseContext::showOverlay() const +{ + return false; +} + +bool MapRendererBaseContext::showGrid() const +{ + return false; +} + +bool MapRendererBaseContext::showVisitable() const +{ + return false; +} + +bool MapRendererBaseContext::showBlockable() const +{ + return false; +} + +MapRendererAdventureContext::MapRendererAdventureContext(const MapRendererContextState & viewState) + : MapRendererBaseContext(viewState) +{ +} + +const CGPath * MapRendererAdventureContext::currentPath() const +{ + const auto * hero = adventureInt->curHero(); + + if(!hero) + return nullptr; + + if(!LOCPLINT->paths.hasPath(hero)) + return nullptr; + + return &LOCPLINT->paths.getPath(hero); +} + +size_t MapRendererAdventureContext::objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const +{ + assert(groupSize > 0); + + if(!settingsAdventureObjectAnimation) + return 0; + + if(groupSize == 0) + return 0; + + // usign objectID for frameCounter to add pseudo-random element per-object. + // Without it, animation of multiple visible objects of the same type will always be in sync + size_t baseFrameTime = 180; + size_t frameCounter = animationTime / baseFrameTime + objectID.getNum(); + size_t frameIndex = frameCounter % groupSize; + return frameIndex; +} + +size_t MapRendererAdventureContext::terrainImageIndex(size_t groupSize) const +{ + if(!settingsAdventureTerrainAnimation) + return 0; + + size_t baseFrameTime = 180; + size_t frameCounter = animationTime / baseFrameTime; + size_t frameIndex = frameCounter % groupSize; + return frameIndex; +} + +bool MapRendererAdventureContext::showBorder() const +{ + return true; +} + +bool MapRendererAdventureContext::showGrid() const +{ + return settingShowGrid; +} + +bool MapRendererAdventureContext::showVisitable() const +{ + return settingShowVisitable; +} + +bool MapRendererAdventureContext::showBlockable() const +{ + return settingShowBlockable; +} + +MapRendererAdventureFadingContext::MapRendererAdventureFadingContext(const MapRendererContextState & viewState) + : MapRendererAdventureContext(viewState) +{ +} + +bool MapRendererAdventureFadingContext::tileAnimated(const int3 & coordinates) const +{ + if(!isInMap(coordinates)) + return false; + + auto objects = getObjects(coordinates); + if(vstd::contains(objects, target)) + return true; + + return false; +} + +double MapRendererAdventureFadingContext::objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const +{ + if(objectID == target) + return progress; return 1.0; } -size_t MapRendererContext::selectOverlayImageForObject(const ObjectPosInfo & object) const +MapRendererAdventureMovingContext::MapRendererAdventureMovingContext(const MapRendererContextState & viewState) + : MapRendererAdventureContext(viewState) +{ +} + +size_t MapRendererAdventureMovingContext::objectGroupIndex(ObjectInstanceID objectID) const +{ + if(target == objectID) + { + static const std::vector moveGroups = {0, 10, 5, 6, 7, 8, 9, 12, 11}; + return moveGroups[getObjectRotation(objectID)]; + } + return MapRendererAdventureContext::objectGroupIndex(objectID); +} + +bool MapRendererAdventureMovingContext::tileAnimated(const int3 & coordinates) const +{ + if(!isInMap(coordinates)) + return false; + + auto objects = getObjects(coordinates); + if(vstd::contains(objects, target)) + return true; + + return false; +} + +Point MapRendererAdventureMovingContext::objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const +{ + if(target == objectID) + { + int3 offsetTilesFrom = tileFrom - coordinates; + int3 offsetTilesDest = tileDest - coordinates; + + Point offsetPixelsFrom = Point(offsetTilesFrom) * Point(32, 32); + Point offsetPixelsDest = Point(offsetTilesDest) * Point(32, 32); + + Point result = vstd::lerp(offsetPixelsFrom, offsetPixelsDest, progress); + + return result; + } + + return MapRendererAdventureContext::objectImageOffset(objectID, coordinates); +} + +size_t MapRendererAdventureMovingContext::objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const +{ + if(target != objectID) + return MapRendererAdventureContext::objectImageIndex(objectID, groupSize); + + int32_t baseFrameTime = 50; + size_t frameCounter = animationTime / baseFrameTime; + size_t frameIndex = frameCounter % groupSize; + return frameIndex; +} + +size_t MapRendererWorldViewContext::selectOverlayImageForObject(const ObjectPosInfo & object) const { size_t ownerIndex = PlayerColor::PLAYER_LIMIT.getNum() * static_cast(EWorldViewIcon::ICONS_PER_PLAYER); @@ -368,19 +356,18 @@ size_t MapRendererContext::selectOverlayImageForObject(const ObjectPosInfo & obj return std::numeric_limits::max(); } -size_t MapRendererContext::overlayImageIndex(const int3 & coordinates) const +MapRendererWorldViewContext::MapRendererWorldViewContext(const MapRendererContextState & viewState) + : MapRendererBaseContext(viewState) { - for(const auto & entry : additionalOverlayIcons) - { - if(entry.pos != coordinates) - continue; +} - size_t iconIndex = selectOverlayImageForObject(entry); - - if(iconIndex != std::numeric_limits::max()) - return iconIndex; - } +bool MapRendererWorldViewContext::showOverlay() const +{ + return true; +} +size_t MapRendererWorldViewContext::overlayImageIndex(const int3 & coordinates) const +{ if(!isVisible(coordinates)) return std::numeric_limits::max(); @@ -402,5 +389,93 @@ size_t MapRendererContext::overlayImageIndex(const int3 & coordinates) const if(iconIndex != std::numeric_limits::max()) return iconIndex; } + return std::numeric_limits::max(); } + +MapRendererSpellViewContext::MapRendererSpellViewContext(const MapRendererContextState & viewState) + : MapRendererWorldViewContext(viewState) +{ +} + +double MapRendererSpellViewContext::objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const +{ + if(showAllTerrain) + { + if(getObject(objectID)->isVisitable() && !MapRendererWorldViewContext::isVisible(coordinates)) + return 0; + } + + return MapRendererWorldViewContext::objectTransparency(objectID, coordinates); +} + +bool MapRendererSpellViewContext::isVisible(const int3 & coordinates) const +{ + if (showAllTerrain) + return isInMap(coordinates); + return MapRendererBaseContext::isVisible(coordinates); +} + +size_t MapRendererSpellViewContext::overlayImageIndex(const int3 & coordinates) const +{ + for(const auto & entry : additionalOverlayIcons) + { + if(entry.pos != coordinates) + continue; + + size_t iconIndex = selectOverlayImageForObject(entry); + + if(iconIndex != std::numeric_limits::max()) + return iconIndex; + } + + return MapRendererWorldViewContext::overlayImageIndex(coordinates); +} + +MapRendererPuzzleMapContext::MapRendererPuzzleMapContext(const MapRendererContextState & viewState) + : MapRendererBaseContext(viewState) +{ +} + +MapRendererPuzzleMapContext::~MapRendererPuzzleMapContext() = default; + +const CGPath * MapRendererPuzzleMapContext::currentPath() const +{ + return grailPos.get(); +} + +double MapRendererPuzzleMapContext::objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const +{ + const auto * object = getObject(objectID); + + if(!object) + return 0; + + if(object->isVisitable()) + return 0; + + if(object->ID == Obj::HOLE) + return 0; + + return MapRendererBaseContext::objectTransparency(objectID, coordinates); +} + +bool MapRendererPuzzleMapContext::isVisible(const int3 & coordinates) const +{ + return LOCPLINT->cb->isInTheMap(coordinates); +} + +bool MapRendererPuzzleMapContext::filterGrayscale() const +{ + return true; +} + +bool MapRendererPuzzleMapContext::showRoads() const +{ + return false; +} + +bool MapRendererPuzzleMapContext::showRivers() const +{ + return false; +} diff --git a/client/mapRenderer/MapRendererContext.h b/client/mapRenderer/MapRendererContext.h index 71b35709f..d9251cc90 100644 --- a/client/mapRenderer/MapRendererContext.h +++ b/client/mapRenderer/MapRendererContext.h @@ -11,101 +11,27 @@ #include "IMapRendererContext.h" -#include "../lib/int3.h" #include "../lib/GameConstants.h" +#include "../lib/int3.h" VCMI_LIB_NAMESPACE_BEGIN struct ObjectPosInfo; VCMI_LIB_NAMESPACE_END -class MapObjectsSorter -{ - IMapRendererContext & context; +struct MapRendererContextState; +class MapRendererBaseContext : public IMapRendererContext +{ public: - explicit MapObjectsSorter(IMapRendererContext & context); - - bool operator()(const ObjectInstanceID & left, const ObjectInstanceID & right) const; - bool operator()(const CGObjectInstance * left, const CGObjectInstance * right) const; -}; - -struct HeroAnimationState -{ - ObjectInstanceID target; - int3 tileFrom; - int3 tileDest; - double progress; -}; - -struct FadingAnimationState -{ - ObjectInstanceID target; - double progress; -}; - -// from VwSymbol.def -enum class EWorldViewIcon -{ - TOWN = 0, - HERO = 1, - ARTIFACT = 2, - TELEPORT = 3, - GATE = 4, - MINE_WOOD = 5, - MINE_MERCURY = 6, - MINE_STONE = 7, - MINE_SULFUR = 8, - MINE_CRYSTAL = 9, - MINE_GEM = 10, - MINE_GOLD = 11, - RES_WOOD = 12, - RES_MERCURY = 13, - RES_STONE = 14, - RES_SULFUR = 15, - RES_CRYSTAL = 16, - RES_GEM = 17, - RES_GOLD = 18, - - ICONS_PER_PLAYER = 19, - ICONS_TOTAL = 19 * 9 // 8 players + neutral set at the end -}; - -class MapRendererContext : public IMapRendererContext -{ - friend class MapViewController; - - boost::multi_array objects; - - uint32_t animationTime = 0; - - boost::optional movementAnimation; - boost::optional teleportAnimation; - - boost::optional fadeOutAnimation; - boost::optional fadeInAnimation; - - std::vector additionalOverlayIcons; - - bool worldViewModeActive = false; - bool showAllTerrain = false; + const MapRendererContextState & viewState; bool settingsSessionSpectate = false; - bool settingsAdventureObjectAnimation = true; - bool settingsAdventureTerrainAnimation = true; - bool settingsSessionShowGrid = false; - bool settingsSessionShowVisitable = false; - bool settingsSessionShowBlockable = false; - size_t selectOverlayImageForObject(const ObjectPosInfo & objectID) const; + explicit MapRendererBaseContext(const MapRendererContextState & viewState); -public: - MapRendererContext(); + uint32_t getObjectRotation(ObjectInstanceID objectID) const; - void addObject(const CGObjectInstance * object); - void addMovingObject(const CGObjectInstance * object, const int3 & tileFrom, const int3 & tileDest); - void removeObject(const CGObjectInstance * object); - - int3 getMapSize() const final; - bool isInMap(const int3 & coordinates) const final; + int3 getMapSize() const override; + bool isInMap(const int3 & coordinates) const override; bool isVisible(const int3 & coordinates) const override; bool tileAnimated(const int3 & coordinates) const override; @@ -116,7 +42,7 @@ public: size_t objectGroupIndex(ObjectInstanceID objectID) const override; Point objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const override; - double objectTransparency(ObjectInstanceID objectID, const int3 &coordinates) const override; + double objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const override; size_t objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const override; size_t terrainImageIndex(size_t groupSize) const override; size_t overlayImageIndex(const int3 & coordinates) const override; @@ -130,3 +56,94 @@ public: bool showVisitable() const override; bool showBlockable() const override; }; + +class MapRendererAdventureContext : public MapRendererBaseContext +{ +public: + uint32_t animationTime = 0; + bool settingShowGrid = false; + bool settingShowVisitable = false; + bool settingShowBlockable = false; + bool settingsAdventureObjectAnimation = true; + bool settingsAdventureTerrainAnimation = true; + + explicit MapRendererAdventureContext(const MapRendererContextState & viewState); + + const CGPath * currentPath() const override; + size_t objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const override; + size_t terrainImageIndex(size_t groupSize) const override; + + bool showBorder() const override; + bool showGrid() const override; + bool showVisitable() const override; + bool showBlockable() const override; +}; + +class MapRendererAdventureFadingContext : public MapRendererAdventureContext +{ +public: + ObjectInstanceID target; + double progress; + + explicit MapRendererAdventureFadingContext(const MapRendererContextState & viewState); + + bool tileAnimated(const int3 & coordinates) const override; + double objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const override; +}; + +class MapRendererAdventureMovingContext : public MapRendererAdventureContext +{ +public: + ObjectInstanceID target; + int3 tileFrom; + int3 tileDest; + double progress; + + explicit MapRendererAdventureMovingContext(const MapRendererContextState & viewState); + + bool tileAnimated(const int3 & coordinates) const override; + size_t objectGroupIndex(ObjectInstanceID objectID) const override; + Point objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const override; + size_t objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const override; +}; + +class MapRendererWorldViewContext : public MapRendererBaseContext +{ +protected: + size_t selectOverlayImageForObject(const ObjectPosInfo & object) const; + +public: + explicit MapRendererWorldViewContext(const MapRendererContextState & viewState); + + size_t overlayImageIndex(const int3 & coordinates) const override; + bool showOverlay() const override; +}; + +class MapRendererSpellViewContext : public MapRendererWorldViewContext +{ +public: + std::vector additionalOverlayIcons; + bool showAllTerrain = false; + + explicit MapRendererSpellViewContext(const MapRendererContextState & viewState); + + bool isVisible(const int3 & coordinates) const override; + double objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const override; + size_t overlayImageIndex(const int3 & coordinates) const override; +}; + +class MapRendererPuzzleMapContext : public MapRendererBaseContext +{ +public: + std::unique_ptr grailPos; + + explicit MapRendererPuzzleMapContext(const MapRendererContextState & viewState); + ~MapRendererPuzzleMapContext(); + + const CGPath * currentPath() const override; + double objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const override; + bool isVisible(const int3 & coordinates) const override; + bool filterGrayscale() const override; + bool showRoads() const override; + bool showRivers() const override; +}; diff --git a/client/mapRenderer/MapRendererContextState.cpp b/client/mapRenderer/MapRendererContextState.cpp new file mode 100644 index 000000000..d4d0e84c0 --- /dev/null +++ b/client/mapRenderer/MapRendererContextState.cpp @@ -0,0 +1,93 @@ +/* + * MapRendererContext.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 "MapRendererContextState.h" + +#include "IMapRendererContext.h" +#include "mapHandler.h" + +#include "../CGameInfo.h" +#include "../CPlayerInterface.h" +#include "../adventureMap/CAdvMapInt.h" +#include "../../CCallback.h" + +#include "../../lib/mapObjects/CGHeroInstance.h" +#include "../../lib/mapping/CMap.h" + +static bool compareObjectBlitOrder(ObjectInstanceID left, ObjectInstanceID right) +{ + //FIXME: remove mh access + return CGI->mh->compareObjectBlitOrder(CGI->mh->getMap()->objects[left.getNum()], CGI->mh->getMap()->objects[right.getNum()]); +} + +MapRendererContextState::MapRendererContextState() +{ + auto mapSize = LOCPLINT->cb->getMapSize(); + + objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]); + + for(const auto & obj : CGI->mh->getMap()->objects) + addObject(obj); +} + +void MapRendererContextState::addObject(const CGObjectInstance * obj) +{ + if(!obj) + return; + + for(int fx = 0; fx < obj->getWidth(); ++fx) + { + for(int fy = 0; fy < obj->getHeight(); ++fy) + { + int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z); + + if(LOCPLINT->cb->isInTheMap(currTile) && obj->coveringAt(currTile.x, currTile.y)) + { + auto & container = objects[currTile.z][currTile.x][currTile.y]; + + container.push_back(obj->id); + boost::range::sort(container, compareObjectBlitOrder); + } + } + } +} + +void MapRendererContextState::addMovingObject(const CGObjectInstance * object, const int3 & tileFrom, const int3 & tileDest) +{ + int xFrom = std::min(tileFrom.x, tileDest.x) - object->getWidth(); + int xDest = std::max(tileFrom.x, tileDest.x); + int yFrom = std::min(tileFrom.y, tileDest.y) - object->getHeight(); + int yDest = std::max(tileFrom.y, tileDest.y); + + for(int x = xFrom; x <= xDest; ++x) + { + for(int y = yFrom; y <= yDest; ++y) + { + int3 currTile(x, y, object->pos.z); + + if(LOCPLINT->cb->isInTheMap(currTile)) + { + auto & container = objects[currTile.z][currTile.x][currTile.y]; + + container.push_back(object->id); + boost::range::sort(container, compareObjectBlitOrder); + } + } + } +} + +void MapRendererContextState::removeObject(const CGObjectInstance * object) +{ + for(int z = 0; z < LOCPLINT->cb->getMapSize().z; z++) + for(int x = 0; x < LOCPLINT->cb->getMapSize().x; x++) + for(int y = 0; y < LOCPLINT->cb->getMapSize().y; y++) + vstd::erase(objects[z][x][y], object->id); +} diff --git a/client/mapRenderer/MapRendererContextState.h b/client/mapRenderer/MapRendererContextState.h new file mode 100644 index 000000000..058709ea6 --- /dev/null +++ b/client/mapRenderer/MapRendererContextState.h @@ -0,0 +1,62 @@ +/* + * MapRendererContext.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/int3.h" +#include "../lib/GameConstants.h" + +VCMI_LIB_NAMESPACE_BEGIN +struct ObjectPosInfo; +class CGObjectInstance; +VCMI_LIB_NAMESPACE_END + +class IMapRendererContext; + +// from VwSymbol.def +enum class EWorldViewIcon +{ + TOWN = 0, + HERO = 1, + ARTIFACT = 2, + TELEPORT = 3, + GATE = 4, + MINE_WOOD = 5, + MINE_MERCURY = 6, + MINE_STONE = 7, + MINE_SULFUR = 8, + MINE_CRYSTAL = 9, + MINE_GEM = 10, + MINE_GOLD = 11, + RES_WOOD = 12, + RES_MERCURY = 13, + RES_STONE = 14, + RES_SULFUR = 15, + RES_CRYSTAL = 16, + RES_GEM = 17, + RES_GOLD = 18, + + ICONS_PER_PLAYER = 19, + ICONS_TOTAL = 19 * 9 // 8 players + neutral set at the end +}; + +struct MapRendererContextState +{ +public: + MapRendererContextState(); + + using MapObject = ObjectInstanceID; + using MapObjectsList = std::vector; + + boost::multi_array objects; + + void addObject(const CGObjectInstance * object); + void addMovingObject(const CGObjectInstance * object, const int3 & tileFrom, const int3 & tileDest); + void removeObject(const CGObjectInstance * object); +}; diff --git a/client/mapRenderer/MapView.cpp b/client/mapRenderer/MapView.cpp index bd079b78f..70d8f4de1 100644 --- a/client/mapRenderer/MapView.cpp +++ b/client/mapRenderer/MapView.cpp @@ -32,9 +32,9 @@ #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapping/CMap.h" -MapView::~MapView() = default; +BasicMapView::~BasicMapView() = default; -std::shared_ptr MapView::createModel(const Point & dimensions) const +std::shared_ptr BasicMapView::createModel(const Point & dimensions) const { auto result = std::make_shared(); @@ -46,22 +46,18 @@ std::shared_ptr MapView::createModel(const Point & dimensions) con return result; } -MapView::MapView(const Point & offset, const Point & dimensions) +BasicMapView::BasicMapView(const Point & offset, const Point & dimensions) : model(createModel(dimensions)) - , controller(new MapViewController(model)) , tilesCache(new MapViewCache(model)) - , isSwiping(false) + , controller(new MapViewController(model, tilesCache)) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; pos += offset; pos.w = dimensions.x; pos.h = dimensions.y; - - actions = std::make_shared(*this, model); - actions->setContext(controller->getContext()); } -void MapView::render(Canvas & target, bool fullUpdate) +void BasicMapView::render(Canvas & target, bool fullUpdate) { Canvas targetClipped(target, pos); @@ -70,20 +66,35 @@ void MapView::render(Canvas & target, bool fullUpdate) tilesCache->render(controller->getContext(), targetClipped, fullUpdate); } -void MapView::show(SDL_Surface * to) +void BasicMapView::show(SDL_Surface * to) { Canvas target(to); CSDL_Ext::CClipRectGuard guard(to, pos); render(target, false); } -void MapView::showAll(SDL_Surface * to) +void BasicMapView::showAll(SDL_Surface * to) { Canvas target(to); CSDL_Ext::CClipRectGuard guard(to, pos); render(target, true); } +void MapView::show(SDL_Surface * to) +{ + actions->setContext(controller->getContext()); + BasicMapView::show(to); +} + +MapView::MapView(const Point & offset, const Point & dimensions) + : BasicMapView(offset, dimensions) + , isSwiping(false) +{ + OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; + actions = std::make_shared(*this, model); + actions->setContext(controller->getContext()); +} + void MapView::onMapLevelSwitched() { if(LOCPLINT->cb->getMapSize().z > 1) @@ -124,6 +135,7 @@ void MapView::onCenteredObject(const CGObjectInstance * target) void MapView::onViewSpellActivated(uint32_t tileSize, const std::vector & objectPositions, bool showTerrain) { + controller->activateSpellViewContext(); controller->setTileSize(Point(tileSize, tileSize)); controller->setOverlayVisibility(objectPositions); controller->setTerrainVisibility(showTerrain); @@ -131,12 +143,19 @@ void MapView::onViewSpellActivated(uint32_t tileSize, const std::vectoractivateWorldViewContext(); controller->setTileSize(Point(tileSize, tileSize)); } void MapView::onViewMapActivated() { + controller->activateAdventureContext(); controller->setTileSize(Point(32, 32)); - controller->setOverlayVisibility({}); - controller->setTerrainVisibility(false); +} + +PuzzleMapView::PuzzleMapView(const Point & offset, const Point & dimensions, const int3 & tileToCenter) + : BasicMapView(offset, dimensions) +{ + controller->setViewCenter(tileToCenter); + controller->activatePuzzleMapContext(tileToCenter); } diff --git a/client/mapRenderer/MapView.h b/client/mapRenderer/MapView.h index 33e2e3aef..f0ba3c74d 100644 --- a/client/mapRenderer/MapView.h +++ b/client/mapRenderer/MapView.h @@ -21,24 +21,38 @@ class MapViewController; class MapViewModel; class MapViewCache; -/// Main class that represents visible section of adventure map -/// Contains all public interface of view and translates calls to internal model -class MapView : public CIntObject +/// Internal class that contains logic shared between all map views +class BasicMapView : public CIntObject { +protected: std::shared_ptr model; std::shared_ptr tilesCache; std::shared_ptr controller; - std::shared_ptr actions; - - bool isSwiping; std::shared_ptr createModel(const Point & dimensions) const; void render(Canvas & target, bool fullUpdate); public: + BasicMapView(const Point & offset, const Point & dimensions); + ~BasicMapView() override; + + void show(SDL_Surface * to) override; + void showAll(SDL_Surface * to) override; +}; + +/// Main class that represents visible section of adventure map +/// Contains all public interface of view and translates calls to internal model +class MapView : public BasicMapView +{ + std::shared_ptr actions; + + bool isSwiping; + +public: + void show(SDL_Surface * to) override; + MapView(const Point & offset, const Point & dimensions); - ~MapView() override; /// Moves current view to another level, preserving position void onMapLevelSwitched(); @@ -55,9 +69,7 @@ public: /// Moves current view to specified tile void onCenteredTile(const int3 & tile); - /// Centers view on object and starts "tracking" it - /// Whenever object changes position, so will the object - /// Tracking will be disabled on any call that moves view + /// Moves current view to specified object void onCenteredObject(const CGObjectInstance * target); /// Switches view to "View Earth" / "View Air" mode, displaying downscaled map with overlay @@ -68,7 +80,11 @@ public: /// Switches view from View World mode back to standard view void onViewMapActivated(); - - void show(SDL_Surface * to) override; - void showAll(SDL_Surface * to) override; +}; + +/// Main class that represents map view for puzzle map +class PuzzleMapView : public BasicMapView +{ +public: + PuzzleMapView(const Point & offset, const Point & dimensions, const int3 & tileToCenter); }; diff --git a/client/mapRenderer/MapViewCache.cpp b/client/mapRenderer/MapViewCache.cpp index a477290a1..307505ebf 100644 --- a/client/mapRenderer/MapViewCache.cpp +++ b/client/mapRenderer/MapViewCache.cpp @@ -54,6 +54,33 @@ std::shared_ptr MapViewCache::getOverlayImageForTile(const std::shared_p return nullptr; } +void MapViewCache::invalidate(const std::shared_ptr & context, const int3 & tile) +{ + int cacheX = (terrainChecksum.shape()[0] + tile.x) % terrainChecksum.shape()[0]; + int cacheY = (terrainChecksum.shape()[1] + tile.y) % terrainChecksum.shape()[1]; + + auto & entry = terrainChecksum[cacheX][cacheY]; + + if (entry.tileX == tile.x && entry.tileY ==tile.y) + entry = TileChecksum{}; +} + +void MapViewCache::invalidate(const std::shared_ptr & context, const ObjectInstanceID & object) +{ + for (size_t cacheY = 0; cacheY < terrainChecksum.shape()[1]; ++cacheY) + { + for (size_t cacheX = 0; cacheX < terrainChecksum.shape()[0]; ++cacheX) + { + auto & entry = terrainChecksum[cacheX][cacheY]; + + int3 tile( entry.tileX, entry.tileY, cachedLevel); + + if (context->isInMap(tile) && vstd::contains(context->getObjects(tile), object)) + entry = TileChecksum{}; + } + } +} + void MapViewCache::updateTile(const std::shared_ptr & context, const int3 & coordinates) { int cacheX = (terrainChecksum.shape()[0] + coordinates.x) % terrainChecksum.shape()[0]; diff --git a/client/mapRenderer/MapViewCache.h b/client/mapRenderer/MapViewCache.h index d73efb9b5..8528bde89 100644 --- a/client/mapRenderer/MapViewCache.h +++ b/client/mapRenderer/MapViewCache.h @@ -17,6 +17,7 @@ class Canvas; class MapRenderer; class IMapRendererContext; class MapViewModel; +class ObjectInstanceID; /// Class responsible for rendering of entire map view /// uses rendering parameters provided by owner class @@ -57,6 +58,9 @@ public: explicit MapViewCache(const std::shared_ptr & model); ~MapViewCache(); + void invalidate(const std::shared_ptr & context, const int3 & tile); + void invalidate(const std::shared_ptr & context, const ObjectInstanceID & object); + /// updates internal terrain cache according to provided time delta void update(const std::shared_ptr & context); diff --git a/client/mapRenderer/MapViewController.cpp b/client/mapRenderer/MapViewController.cpp index b75858982..9823838bf 100644 --- a/client/mapRenderer/MapViewController.cpp +++ b/client/mapRenderer/MapViewController.cpp @@ -12,14 +12,18 @@ #include "MapViewController.h" #include "MapRendererContext.h" +#include "MapRendererContextState.h" #include "MapViewModel.h" +#include "MapViewCache.h" #include "../adventureMap/CAdvMapInt.h" +#include "../CPlayerInterface.h" #include "../../lib/CConfigHandler.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/MiscObjects.h" #include "../../lib/spells/ViewSpellInt.h" +#include "../../lib/CPathfinder.h" void MapViewController::setViewCenter(const int3 & position) { @@ -32,7 +36,7 @@ void MapViewController::setViewCenter(const Point & position, int level) Point upperLimit = Point(context->getMapSize()) * model->getSingleTileSize() + model->getSingleTileSize(); Point lowerLimit = Point(0,0); - if (context->worldViewModeActive) + if (worldViewContext) { Point area = model->getPixelsVisibleDimensions(); Point mapCenter = upperLimit / 2; @@ -74,10 +78,13 @@ void MapViewController::setTileSize(const Point & tileSize) setViewCenter(model->getMapViewCenter(), model->getLevel()); } -MapViewController::MapViewController(std::shared_ptr model) - : context(new MapRendererContext()) +MapViewController::MapViewController(std::shared_ptr model, std::shared_ptr view) + : state(new MapRendererContextState()) , model(std::move(model)) + , view(view) { + adventureContext = std::make_shared(*state); + context = adventureContext; } std::shared_ptr MapViewController::getContext() const @@ -85,14 +92,6 @@ std::shared_ptr MapViewController::getContext() const return context; } -void MapViewController::moveFocusToSelection() -{ - const auto * army = adventureInt->curArmy(); - - if (army) - setViewCenter(army->getSightCenter()); -} - void MapViewController::update(uint32_t timeDelta) { // confirmed to match H3 for @@ -103,13 +102,13 @@ void MapViewController::update(uint32_t timeDelta) // - teleporting ( 250 ms) static const double fadeOutDuration = 500; static const double fadeInDuration = 500; - static const double heroTeleportDuration = 250; + //static const double heroTeleportDuration = 250; //FIXME: remove code duplication? - if(context->movementAnimation) + if(movementContext) { - const auto * object = context->getObject(context->movementAnimation->target); + const auto * object = context->getObject(movementContext->target); const auto * hero = dynamic_cast(object); const auto * boat = dynamic_cast(object); @@ -118,172 +117,360 @@ void MapViewController::update(uint32_t timeDelta) if (!hero) hero = boat->hero; - // TODO: enemyMoveTime - double heroMoveTime = settings["adventure"]["heroMoveTime"].Float(); + double heroMoveTime = + LOCPLINT->makingTurn ? + settings["adventure"]["heroMoveTime"].Float(): + settings["adventure"]["enemyMoveTime"].Float(); - context->movementAnimation->progress += timeDelta / heroMoveTime; + movementContext->progress += timeDelta / heroMoveTime; - Point positionFrom = Point(hero->convertToVisitablePos(context->movementAnimation->tileFrom)) * model->getSingleTileSize() + model->getSingleTileSize() / 2; - Point positionDest = Point(hero->convertToVisitablePos(context->movementAnimation->tileDest)) * model->getSingleTileSize() + model->getSingleTileSize() / 2; + Point positionFrom = Point(hero->convertToVisitablePos(movementContext->tileFrom)) * model->getSingleTileSize() + model->getSingleTileSize() / 2; + Point positionDest = Point(hero->convertToVisitablePos(movementContext->tileDest)) * model->getSingleTileSize() + model->getSingleTileSize() / 2; - Point positionCurr = vstd::lerp(positionFrom, positionDest, context->movementAnimation->progress); + Point positionCurr = vstd::lerp(positionFrom, positionDest, movementContext->progress); - if(context->movementAnimation->progress >= 1.0) + if(movementContext->progress >= 1.0) { setViewCenter(hero->getSightCenter()); - context->removeObject(context->getObject(context->movementAnimation->target)); - context->addObject(context->getObject(context->movementAnimation->target)); - context->movementAnimation.reset(); + removeObject(context->getObject(movementContext->target)); + addObject(context->getObject(movementContext->target)); + + activateAdventureContext(movementContext->animationTime); } else { - setViewCenter(positionCurr, context->movementAnimation->tileDest.z); + setViewCenter(positionCurr, movementContext->tileDest.z); } } - if(context->teleportAnimation) - { - context->teleportAnimation->progress += timeDelta / heroTeleportDuration; - moveFocusToSelection(); - if(context->teleportAnimation->progress >= 1.0) - context->teleportAnimation.reset(); - } + //if(teleportContext) + //{ + // teleportContext->progress += timeDelta / heroTeleportDuration; + // moveFocusToSelection(); + // if(teleportContext->progress >= 1.0) + // teleportContext.reset(); + //} - if(context->fadeOutAnimation) + if(fadingOutContext) { - context->fadeOutAnimation->progress += timeDelta / fadeOutDuration; - moveFocusToSelection(); - if(context->fadeOutAnimation->progress >= 1.0) + fadingOutContext->progress -= timeDelta / fadeOutDuration; + + if(fadingOutContext->progress <= 0.0) { - context->removeObject(context->getObject(context->fadeOutAnimation->target)); - context->fadeOutAnimation.reset(); + removeObject(context->getObject(fadingOutContext->target)); + + activateAdventureContext(fadingOutContext->animationTime); } } - if(context->fadeInAnimation) + if(fadingInContext) { - context->fadeInAnimation->progress += timeDelta / fadeInDuration; - moveFocusToSelection(); - if(context->fadeInAnimation->progress >= 1.0) - context->fadeInAnimation.reset(); + fadingInContext->progress += timeDelta / fadeInDuration; + + if(fadingInContext->progress >= 1.0) + { + activateAdventureContext(fadingInContext->animationTime); + } } - context->animationTime += timeDelta; - context->worldViewModeActive = model->getSingleTileSize() != Point(32,32); - - context->settingsSessionSpectate = settings["session"]["spectate"].Bool(); - context->settingsAdventureObjectAnimation = settings["adventure"]["objectAnimation"].Bool(); - context->settingsAdventureTerrainAnimation = settings["adventure"]["terrainAnimation"].Bool(); - context->settingsSessionShowGrid = settings["gameTweaks"]["showGrid"].Bool(); - context->settingsSessionShowVisitable = settings["session"]["showVisitable"].Bool(); - context->settingsSessionShowBlockable = settings["session"]["showBlockable"].Bool(); + if (adventureContext) + { + adventureContext->animationTime += timeDelta; + adventureContext->settingsSessionSpectate = settings["session"]["spectate"].Bool(); + adventureContext->settingsAdventureObjectAnimation = settings["adventure"]["objectAnimation"].Bool(); + adventureContext->settingsAdventureTerrainAnimation = settings["adventure"]["terrainAnimation"].Bool(); + adventureContext->settingShowGrid = settings["gameTweaks"]["showGrid"].Bool(); + adventureContext->settingShowVisitable = settings["session"]["showVisitable"].Bool(); + adventureContext->settingShowBlockable = settings["session"]["showBlockable"].Bool(); + } } -void MapViewController::onObjectFadeIn(const CGObjectInstance * obj) +bool MapViewController::isEventVisible(const CGObjectInstance * obj) { - bool actionVisible = context->isVisible(obj->pos); + if (adventureContext == nullptr) + return false; - assert(!context->fadeInAnimation); + if (!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() < 0) + return false; // enemy move speed set to "hidden/none" - if (actionVisible) - context->fadeInAnimation = FadingAnimationState{obj->id, 0.0}; - context->addObject(obj); -} - -void MapViewController::onObjectFadeOut(const CGObjectInstance * obj) -{ - bool actionVisible = context->isVisible(obj->pos); - - assert(!context->fadeOutAnimation); - - if (actionVisible) - context->fadeOutAnimation = FadingAnimationState{obj->id, 0.0}; + if (obj->isVisitable()) + return context->isVisible(obj->visitablePos()); else - context->removeObject(obj); + return context->isVisible(obj->pos); } -void MapViewController::onObjectInstantAdd(const CGObjectInstance * obj) +bool MapViewController::isEventVisible(const CGHeroInstance * obj, const int3 & from, const int3 & dest) { - context->addObject(obj); -}; + if (adventureContext == nullptr) + return false; -void MapViewController::onObjectInstantRemove(const CGObjectInstance * obj) -{ - context->removeObject(obj); -}; + if (!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() < 0) + return false; // enemy move speed set to "hidden/none" -void MapViewController::onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) -{ - assert(!context->teleportAnimation); - bool actionVisible = context->isVisible(from) || context->isVisible(dest); - - if (actionVisible) - { - context->teleportAnimation = HeroAnimationState{obj->id, from, dest, 0.0}; - } - else - { - context->removeObject(obj); - context->addObject(obj); - } -} - -void MapViewController::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) -{ - assert(!context->movementAnimation); - bool actionVisible = context->isVisible(from) || context->isVisible(dest); - - const CGObjectInstance * movingObject = obj; - if(obj->boat) - movingObject = obj->boat; - - context->removeObject(movingObject); - - if(settings["adventure"]["heroMoveTime"].Float() > 1 && actionVisible) - { - context->addMovingObject(movingObject, from, dest); - context->movementAnimation = HeroAnimationState{movingObject->id, from, dest, 0.0}; - } - else - { - // instant movement - context->addObject(movingObject); - - if (actionVisible) - setViewCenter(movingObject->visitablePos()); - } -} - -void MapViewController::onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) -{ - //TODO. Or no-op? -} - -bool MapViewController::hasOngoingAnimations() -{ - if(context->movementAnimation) + if (context->isVisible(obj->convertToVisitablePos(from))) return true; - if(context->teleportAnimation) - return true; - - if(context->fadeOutAnimation) - return true; - - if(context->fadeInAnimation) + if (context->isVisible(obj->convertToVisitablePos(dest))) return true; return false; } +void MapViewController::fadeOutObject(const CGObjectInstance * obj) +{ + fadingOutContext = std::make_shared(*state); + fadingOutContext->animationTime = adventureContext->animationTime; + adventureContext = fadingOutContext; + context = fadingOutContext; + + fadingOutContext->target = obj->id; + fadingOutContext->progress = 1.0; +} + +void MapViewController::fadeInObject(const CGObjectInstance * obj) +{ + fadingInContext = std::make_shared(*state); + fadingInContext->animationTime = adventureContext->animationTime; + adventureContext = fadingInContext; + context = fadingInContext; + + fadingInContext->target = obj->id; + fadingInContext->progress = 0.0; +} + +void MapViewController::removeObject(const CGObjectInstance * obj) +{ + view->invalidate(context, obj->id); + state->removeObject(obj); +} + +void MapViewController::addObject(const CGObjectInstance * obj) +{ + state->addObject(obj); + view->invalidate(context, obj->id); +} + +void MapViewController::onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) +{ + if (isEventVisible(obj, from, dest)) + { + onObjectFadeOut(obj); + setViewCenter(obj->getSightCenter()); + } + else + removeObject(obj); +} + +void MapViewController::onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) +{ + if (isEventVisible(obj, from, dest)) + setViewCenter(obj->getSightCenter()); +} + +void MapViewController::onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) +{ + if (isEventVisible(obj, from, dest)) + setViewCenter(obj->getSightCenter()); +} + +void MapViewController::onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) +{ + if (isEventVisible(obj, from, dest)) + { + onObjectFadeIn(obj); + setViewCenter(obj->getSightCenter()); + } + addObject(obj); +} + +void MapViewController::onObjectFadeIn(const CGObjectInstance * obj) +{ + assert(!hasOngoingAnimations()); + + if (isEventVisible(obj)) + fadeInObject(obj); + + addObject(obj); +} + +void MapViewController::onObjectFadeOut(const CGObjectInstance * obj) +{ + assert(!hasOngoingAnimations()); + + if (isEventVisible(obj)) + fadeOutObject(obj); + else + removeObject(obj); +} + +void MapViewController::onObjectInstantAdd(const CGObjectInstance * obj) +{ + addObject(obj); +}; + +void MapViewController::onObjectInstantRemove(const CGObjectInstance * obj) +{ + removeObject(obj); +}; + +void MapViewController::onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) +{ + assert(!hasOngoingAnimations()); + + if (isEventVisible(obj, from, dest)) + { + // TODO: generate view with old state + setViewCenter(obj->getSightCenter()); + } +} + +void MapViewController::onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) +{ + assert(!hasOngoingAnimations()); + + if (isEventVisible(obj, from, dest)) + { + // TODO: animation + setViewCenter(obj->getSightCenter()); + } + else + { + removeObject(obj); + addObject(obj); + } +} + +void MapViewController::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) +{ + assert(!hasOngoingAnimations()); + + const CGObjectInstance * movingObject = obj; + if(obj->boat) + movingObject = obj->boat; + + removeObject(movingObject); + + if (!isEventVisible(obj, from, dest)) + { + addObject(movingObject); + return; + } + + double movementTime = + LOCPLINT->playerID == obj->tempOwner ? + settings["adventure"]["heroMoveTime"].Float(): + settings["adventure"]["enemyMoveTime"].Float(); + + if(movementTime > 1) + { + movementContext = std::make_shared(*state); + movementContext->animationTime = adventureContext->animationTime; + adventureContext = movementContext; + context = movementContext; + + state->addMovingObject(movingObject, from, dest); + + movementContext->target = movingObject->id; + movementContext->tileFrom = from; + movementContext->tileDest = dest; + movementContext->progress = 0.0; + } + else // instant movement + { + addObject(movingObject); + setViewCenter(movingObject->visitablePos()); + } +} + +bool MapViewController::hasOngoingAnimations() +{ + if(movementContext) + return true; + + if(fadingOutContext) + return true; + + if(fadingInContext) + return true; + + return false; +} + +void MapViewController::activateAdventureContext(uint32_t animationTime) +{ + resetContext(); + + adventureContext = std::make_shared(*state); + adventureContext->animationTime = animationTime; + context = adventureContext; +} + +void MapViewController::activateAdventureContext() +{ + activateAdventureContext(0); +} + +void MapViewController::activateWorldViewContext() +{ + if (worldViewContext) + return; + + resetContext(); + + worldViewContext = std::make_shared(*state); + context = worldViewContext; +} + +void MapViewController::activateSpellViewContext() +{ + if (spellViewContext) + return; + + resetContext(); + + spellViewContext = std::make_shared(*state); + worldViewContext = spellViewContext; + context = spellViewContext; +} + +void MapViewController::activatePuzzleMapContext(const int3 & grailPosition) +{ + resetContext(); + + puzzleMapContext = std::make_shared(*state); + context = puzzleMapContext; + + CGPathNode fakeNode; + fakeNode.coord = grailPosition; + + puzzleMapContext->grailPos = std::make_unique(); + + // create two nodes since 1st one is normally not visible + puzzleMapContext->grailPos->nodes.push_back(fakeNode); + puzzleMapContext->grailPos->nodes.push_back(fakeNode); +} + +void MapViewController::resetContext() +{ + adventureContext.reset(); + movementContext.reset(); + fadingOutContext.reset(); + fadingInContext.reset(); + worldViewContext.reset(); + spellViewContext.reset(); + puzzleMapContext.reset(); +} + void MapViewController::setTerrainVisibility(bool showAllTerrain) { - context->showAllTerrain = showAllTerrain; + assert(spellViewContext); + spellViewContext->showAllTerrain = showAllTerrain; } void MapViewController::setOverlayVisibility(const std::vector & objectPositions) { - context->additionalOverlayIcons = objectPositions; + assert(spellViewContext); + spellViewContext->additionalOverlayIcons = objectPositions; } diff --git a/client/mapRenderer/MapViewController.h b/client/mapRenderer/MapViewController.h index 9f020d9f7..4612ce814 100644 --- a/client/mapRenderer/MapViewController.h +++ b/client/mapRenderer/MapViewController.h @@ -16,31 +16,66 @@ class Point; struct ObjectPosInfo; VCMI_LIB_NAMESPACE_END +struct MapRendererContextState; + +class MapViewCache; class MapViewModel; class IMapRendererContext; -class MapRendererContext; +class MapRendererAdventureContext; +class MapRendererAdventureFadingContext; +class MapRendererAdventureMovingContext; +class MapRendererWorldViewContext; +class MapRendererSpellViewContext; +class MapRendererPuzzleMapContext; /// Class responsible for updating view state, /// such as its position and any animations class MapViewController : public IMapObjectObserver { - std::shared_ptr context; + std::shared_ptr context; + std::shared_ptr state; std::shared_ptr model; + std::shared_ptr view; + + // all potential contexts for view + // only some are present at any given moment, rest are nullptr's + std::shared_ptr adventureContext; + std::shared_ptr movementContext; + //std::shared_ptr teleportContext; + std::shared_ptr fadingOutContext; + std::shared_ptr fadingInContext; + std::shared_ptr worldViewContext; + std::shared_ptr spellViewContext; + std::shared_ptr puzzleMapContext; private: + bool isEventVisible(const CGObjectInstance * obj); + bool isEventVisible(const CGHeroInstance * obj, const int3 & from, const int3 & dest); + + void fadeOutObject(const CGObjectInstance * obj); + void fadeInObject(const CGObjectInstance * obj); + + void removeObject(const CGObjectInstance * obj); + void addObject(const CGObjectInstance * obj); + // IMapObjectObserver impl bool hasOngoingAnimations() override; void onObjectFadeIn(const CGObjectInstance * obj) override; void onObjectFadeOut(const CGObjectInstance * obj) override; void onObjectInstantAdd(const CGObjectInstance * obj) override; void onObjectInstantRemove(const CGObjectInstance * obj) override; - void onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override; + void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override; + void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override; void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override; - void onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override; + void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override; + void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override; + void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override; + void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override; + + void resetContext(); - void moveFocusToSelection(); public: - explicit MapViewController(std::shared_ptr model); + MapViewController(std::shared_ptr model, std::shared_ptr view); std::shared_ptr getContext() const; @@ -49,6 +84,12 @@ public: void setTileSize(const Point & tileSize); void update(uint32_t timeDelta); + void activateAdventureContext(uint32_t animationTime); + void activateAdventureContext(); + void activateWorldViewContext(); + void activateSpellViewContext(); + void activatePuzzleMapContext(const int3 & grailPosition); + void setTerrainVisibility(bool showAllTerrain); void setOverlayVisibility(const std::vector & objectPositions); diff --git a/client/mapRenderer/mapHandler.cpp b/client/mapRenderer/mapHandler.cpp index 4facabba9..f7655f863 100644 --- a/client/mapRenderer/mapHandler.cpp +++ b/client/mapRenderer/mapHandler.cpp @@ -22,49 +22,6 @@ #include "../../lib/CGeneralTextHandler.h" #include "../../lib/TerrainHandler.h" -/* -void CMapPuzzleViewBlitter::drawObjects(SDL_Surface * targetSurf, const TerrainTile2 & tile) const -{ - CMapBlitter::drawObjects(targetSurf, tile); - - // grail X mark - if(pos.x == info->grailPos.x && pos.y == info->grailPos.y) - { - const auto mark = graphics->heroMoveArrows->getImage(0); - mark->draw(targetSurf,realTileRect.x,realTileRect.y); - } -} -*/ -/* -void CMapPuzzleViewBlitter::postProcessing(SDL_Surface * targetSurf) const -{ - CSDL_Ext::applyEffect(targetSurf, info->drawBounds, static_cast(!ADVOPT.puzzleSepia)); -} -*/ -/* -bool CMapPuzzleViewBlitter::canDrawObject(const CGObjectInstance * obj) const -{ - if (!CMapBlitter::canDrawObject(obj)) - return false; - - //don't print flaggable objects in puzzle mode - if (obj->isVisitable()) - return false; - - if(std::find(unblittableObjects.begin(), unblittableObjects.end(), obj->ID) != unblittableObjects.end()) - return false; - - return true; -} -*/ -/* -CMapPuzzleViewBlitter::CMapPuzzleViewBlitter(CMapHandler * parent) - : CMapNormalBlitter(parent) -{ - unblittableObjects.push_back(Obj::HOLE); -} -*/ - bool CMapHandler::hasOngoingAnimations() { for (auto * observer : observers) @@ -196,6 +153,30 @@ void CMapHandler::onObjectFadeOut(const CGObjectInstance * obj) observer->onObjectFadeOut(obj); } +void CMapHandler::onBeforeHeroEmbark(const CGHeroInstance *obj, const int3 &from, const int3 &dest) +{ + for (auto * observer : observers) + observer->onBeforeHeroEmbark(obj, from, dest); +} + +void CMapHandler::onAfterHeroEmbark(const CGHeroInstance *obj, const int3 &from, const int3 &dest) +{ + for (auto * observer : observers) + observer->onAfterHeroEmbark(obj, from, dest); +} + +void CMapHandler::onBeforeHeroDisembark(const CGHeroInstance *obj, const int3 &from, const int3 &dest) +{ + for (auto * observer : observers) + observer->onBeforeHeroDisembark(obj, from, dest); +} + +void CMapHandler::onAfterHeroDisembark(const CGHeroInstance *obj, const int3 &from, const int3 &dest) +{ + for (auto * observer : observers) + observer->onAfterHeroDisembark(obj, from, dest); +} + void CMapHandler::onObjectInstantAdd(const CGObjectInstance * obj) { for (auto * observer : observers) @@ -208,11 +189,18 @@ void CMapHandler::onObjectInstantRemove(const CGObjectInstance * obj) observer->onObjectInstantRemove(obj); } -void CMapHandler::onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) +void CMapHandler::onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) { assert(obj->pos == dest); for (auto * observer : observers) - observer->onHeroTeleported(obj, from, dest); + observer->onAfterHeroTeleported(obj, from, dest); +} + +void CMapHandler::onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) +{ + assert(obj->pos == from); + for (auto * observer : observers) + observer->onBeforeHeroTeleported(obj, from, dest); } void CMapHandler::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) @@ -222,13 +210,6 @@ void CMapHandler::onHeroMoved(const CGHeroInstance * obj, const int3 & from, con observer->onHeroMoved(obj, from, dest); } -void CMapHandler::onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) -{ - assert(obj->pos == from); - for (auto * observer : observers) - observer->onHeroRotated(obj, from, dest); -} - void CMapHandler::addMapObserver(IMapObjectObserver * object) { observers.push_back(object); diff --git a/client/mapRenderer/mapHandler.h b/client/mapRenderer/mapHandler.h index 0835816e5..bac9c6afb 100644 --- a/client/mapRenderer/mapHandler.h +++ b/client/mapRenderer/mapHandler.h @@ -59,9 +59,13 @@ public: void onObjectFadeOut(const CGObjectInstance * obj); void onObjectInstantAdd(const CGObjectInstance * obj); void onObjectInstantRemove(const CGObjectInstance * obj); - void onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest); + void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest); + void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest); + void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest); + void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest); + void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest); + void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest); void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest); - void onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest); /// Add object to receive notifications on any changes in visible map state void addMapObserver(IMapObjectObserver * observer); diff --git a/client/windows/CPuzzleWindow.cpp b/client/windows/CPuzzleWindow.cpp index 70e54bdf2..954e192d3 100644 --- a/client/windows/CPuzzleWindow.cpp +++ b/client/windows/CPuzzleWindow.cpp @@ -16,6 +16,7 @@ #include "../adventureMap/CResDataBar.h" #include "../gui/CGuiHandler.h" #include "../gui/TextAlignment.h" +#include "../mapRenderer/MapView.h" #include "../widgets/Buttons.h" #include "../widgets/Images.h" #include "../widgets/TextControls.h" @@ -38,6 +39,8 @@ CPuzzleWindow::CPuzzleWindow(const int3 & GrailPos, double discoveredRatio) quitb->assignedKeys.insert(SDLK_ESCAPE); quitb->setBorderColor(Colors::METALLIC_GOLD); + mapView = std::make_shared(Point(8,9), Point(591, 544), grailPos); + logo = std::make_shared("PUZZLOGO", 607, 3); title = std::make_shared(700, 95, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[463]); resDataBar = std::make_shared("ARESBAR.bmp", 3, 575, 32, 2, 85, 85); @@ -68,16 +71,6 @@ CPuzzleWindow::CPuzzleWindow(const int3 & GrailPos, double discoveredRatio) void CPuzzleWindow::showAll(SDL_Surface * to) { - assert(0); - //int3 moveInt = int3(8, 9, 0); - //Rect mapRect = Rect(Point(pos.x + 8, pos.y + 7), Point(544, 591)); - //int3 topTile = grailPos - moveInt; - - //MapDrawingInfo info(topTile, LOCPLINT->cb->getVisibilityMap(), mapRect); - //info.puzzleMode = true; - //info.grailPos = grailPos; - //CGI->mh->drawTerrainRectNew(to, &info); - CWindowObject::showAll(to); } diff --git a/client/windows/CPuzzleWindow.h b/client/windows/CPuzzleWindow.h index 91b401afe..8a448bd9c 100644 --- a/client/windows/CPuzzleWindow.h +++ b/client/windows/CPuzzleWindow.h @@ -15,12 +15,14 @@ class CLabel; class CButton; class CResDataBar; +class PuzzleMapView; /// Puzzle screen which gets uncovered when you visit obilisks class CPuzzleWindow : public CWindowObject { private: int3 grailPos; + std::shared_ptr mapView; std::shared_ptr logo; std::shared_ptr title; std::shared_ptr quitb; diff --git a/config/terrains.json b/config/terrains.json index b381072e1..6cddc2f84 100644 --- a/config/terrains.json +++ b/config/terrains.json @@ -138,7 +138,7 @@ "transitionRequired" : true, "terrainViewPatterns" : "water", "horseSound" : "horse08", - "horseSoundPenalty" : "horse28", + "horseSoundPenalty" : "horse08", "sounds": { "ambient": ["LOOPOCEA"] } diff --git a/lib/NetPacks.h b/lib/NetPacks.h index a0e3aa87d..4d823544f 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -593,8 +593,6 @@ struct DLL_LINKAGE TryMoveHero : public CPackForClient std::unordered_set fowRevealed; //revealed tiles boost::optional attackedFrom; // Set when stepping into endangered tile. - bool humanKnows = false; //used locally during applying to client - virtual void visitTyped(ICPackVisitor & visitor) override; bool stopMovement() const diff --git a/mapeditor/maphandler.cpp b/mapeditor/maphandler.cpp index 66d9a5eda..ee40402a0 100644 --- a/mapeditor/maphandler.cpp +++ b/mapeditor/maphandler.cpp @@ -21,7 +21,6 @@ #include "../lib/CHeroHandler.h" #include "../lib/CTownHandler.h" #include "../lib/CModHandler.h" -#include "../lib/mapping/CMap.h" #include "../lib/GameConstants.h" #include "../lib/JsonDetail.h"