From 7cf00ba87dd076bab4fcbaf6794a10faca0e758c Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 18 Feb 2023 17:37:09 +0200 Subject: [PATCH] Implemented basic hero movement animation & object fade-out --- client/CMakeLists.txt | 2 - client/CPlayerInterface.cpp | 14 +- client/NetPacksClient.cpp | 7 +- client/adventureMap/CAdvMapInt.cpp | 6 +- client/adventureMap/CTerrainRect.cpp | 36 +-- client/adventureMap/CTerrainRect.h | 6 - client/adventureMap/MapRenderer.cpp | 191 ++++--------- client/adventureMap/MapRenderer.h | 15 +- client/adventureMap/MapRendererContext.h | 33 ++- client/adventureMap/MapView.cpp | 325 +++++++++++++++++++---- client/adventureMap/MapView.h | 117 +++++--- client/adventureMap/mapHandler.cpp | 64 +++-- client/adventureMap/mapHandler.h | 30 +-- client/render/CFadeAnimation.cpp | 101 ------- client/render/CFadeAnimation.h | 53 ---- 15 files changed, 491 insertions(+), 509 deletions(-) delete mode 100644 client/render/CFadeAnimation.cpp delete mode 100644 client/render/CFadeAnimation.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 4bb9529b1..4b79a82d5 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -53,7 +53,6 @@ set(client_SRCS render/CAnimation.cpp render/CBitmapHandler.cpp render/CDefFile.cpp - render/CFadeAnimation.cpp render/Canvas.cpp render/ColorFilter.cpp render/Colors.cpp @@ -169,7 +168,6 @@ set(client_HEADERS render/CAnimation.h render/CBitmapHandler.h render/CDefFile.h - render/CFadeAnimation.h render/Canvas.h render/ColorFilter.h render/Colors.h diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 3820ddda0..b2dc3174a 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -457,22 +457,10 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose) return; } - adventureInt->centerOn(hero); //actualizing screen pos adventureInt->minimap->redraw(); adventureInt->heroList->redraw(); - auto waitFrame = [&]() - { - int frameNumber = GH.mainFPSmng->getFrameNumber(); - - auto unlockPim = vstd::makeUnlockGuard(*pim); - while(frameNumber == GH.mainFPSmng->getFrameNumber()) - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - }; - - //main moving - while (CGI->mh->hasActiveAnimations()) - waitFrame(); //for animation purposes + CGI->mh->waitForOngoingAnimations(); //finishing move hero->isStanding = true; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 45e005591..13ea342f1 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -344,6 +344,8 @@ void ApplyFirstClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack) CGObjectInstance *obj = gs.getObjInstance(pack.objid); if(CGI->mh) CGI->mh->onObjectFadeOut(obj); + + CGI->mh->waitForOngoingAnimations(); } void ApplyClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack) { @@ -351,6 +353,7 @@ void ApplyClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack) if(CGI->mh) CGI->mh->onObjectFadeIn(obj); + CGI->mh->waitForOngoingAnimations(); cl.invalidatePaths(); } @@ -426,6 +429,8 @@ void ApplyFirstClientNetPackVisitor::visitRemoveObject(RemoveObject & pack) if(gs.isVisible(o, i->first) || (!cl.getPlayerState(i->first)->human && o->ID == Obj::HERO && o->tempOwner != i->first)) i->second->objectRemoved(o); } + + CGI->mh->waitForOngoingAnimations(); } void ApplyClientNetPackVisitor::visitRemoveObject(RemoveObject & pack) @@ -938,7 +943,6 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack) callInterfaceIfPresent(cl, obj1->tempOwner, &IGameEventsReceiver::showTavernWindow, obj2); break; } - } void ApplyClientNetPackVisitor::visitCenterView(CenterView & pack) @@ -959,6 +963,7 @@ void ApplyClientNetPackVisitor::visitNewObject(NewObject & pack) if(gs.isVisible(obj, i->first)) i->second->newObject(obj); } + CGI->mh->waitForOngoingAnimations(); } void ApplyClientNetPackVisitor::visitSetAvailableArtifacts(SetAvailableArtifacts & pack) diff --git a/client/adventureMap/CAdvMapInt.cpp b/client/adventureMap/CAdvMapInt.cpp index d13bab098..8177656da 100644 --- a/client/adventureMap/CAdvMapInt.cpp +++ b/client/adventureMap/CAdvMapInt.cpp @@ -319,7 +319,7 @@ void CAdvMapInt::fsleepWake() void CAdvMapInt::fmoveHero() { const CGHeroInstance *h = curHero(); - if (!h || !LOCPLINT->paths.hasPath(h) || CGI->mh->hasActiveAnimations()) + if (!h || !LOCPLINT->paths.hasPath(h) || CGI->mh->hasOngoingAnimations()) return; LOCPLINT->moveHero(h, LOCPLINT->paths.getPath(h)); @@ -833,7 +833,7 @@ void CAdvMapInt::keyPressed(const SDL_Keycode & key) if(!h || !isActive()) return; - if (CGI->mh->hasActiveAnimations()) + if (CGI->mh->hasOngoingAnimations()) return; if(*direction == Point(0,0)) @@ -1138,7 +1138,7 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos) if(LOCPLINT->paths.hasPath(currentHero) && LOCPLINT->paths.getPath(currentHero).endPos() == mapPos)//we'll be moving { - if(!CGI->mh->hasActiveAnimations()) + if(!CGI->mh->hasOngoingAnimations()) LOCPLINT->moveHero(currentHero, LOCPLINT->paths.getPath(currentHero)); return; } diff --git a/client/adventureMap/CTerrainRect.cpp b/client/adventureMap/CTerrainRect.cpp index 5e8cd4109..4801085b1 100644 --- a/client/adventureMap/CTerrainRect.cpp +++ b/client/adventureMap/CTerrainRect.cpp @@ -19,7 +19,6 @@ #include "../gui/CursorHandler.h" #include "../gui/CGuiHandler.h" #include "../render/CAnimation.h" -#include "../render/CFadeAnimation.h" #include "../render/IImage.h" #include "../renderSDL/SDL_Extensions.h" #include "../widgets/TextControls.h" @@ -35,9 +34,7 @@ #define ADVOPT (conf.go()->ac) CTerrainRect::CTerrainRect() - : fadeSurface(nullptr) - , fadeAnim(std::make_shared()) - , curHoveredTile(-1, -1, -1) + : curHoveredTile(-1, -1, -1) , isSwiping(false) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; @@ -51,20 +48,14 @@ CTerrainRect::CTerrainRect() renderer = std::make_shared( Point(0,0), pos.dimensions() ); } -CTerrainRect::~CTerrainRect() -{ - if(fadeSurface) - SDL_FreeSurface(fadeSurface); -} - void CTerrainRect::setViewCenter(const int3 &coordinates) { - renderer->setViewCenter(coordinates); + renderer->getController()->setViewCenter(coordinates); } void CTerrainRect::setViewCenter(const Point & position, int level) { - renderer->setViewCenter(position, level); + renderer->getController()->setViewCenter(position, level); } void CTerrainRect::deactivate() @@ -220,30 +211,17 @@ Rect CTerrainRect::visibleTilesArea() void CTerrainRect::fadeFromCurrentView() { - if (!ADVOPT.screenFading) - return; - if (adventureInt->mode == EAdvMapMode::WORLD_VIEW) - return; - - if (!fadeSurface) - fadeSurface = CSDL_Ext::newSurface(pos.w, pos.h); - CSDL_Ext::blitSurface(screen, fadeSurface, Point(0,0)); - fadeAnim->init(CFadeAnimation::EMode::OUT, fadeSurface); + assert(0);//TODO } void CTerrainRect::setLevel(int level) { - renderer->setViewCenter(renderer->getModel()->getMapViewCenter(), level); + renderer->getController()->setViewCenter(renderer->getModel()->getMapViewCenter(), level); } void CTerrainRect::moveViewBy(const Point & delta) { - renderer->setViewCenter(renderer->getModel()->getMapViewCenter() + delta, getLevel()); -} - -int3 CTerrainRect::getTileCenter() -{ - return renderer->getModel()->getTileCenter(); + renderer->getController()->setViewCenter(renderer->getModel()->getMapViewCenter() + delta, getLevel()); } Point CTerrainRect::getViewCenter() @@ -258,5 +236,5 @@ int CTerrainRect::getLevel() void CTerrainRect::setTileSize(int sizePixels) { - renderer->setTileSize(Point(sizePixels, sizePixels)); + renderer->getController()->setTileSize(Point(sizePixels, sizePixels)); } diff --git a/client/adventureMap/CTerrainRect.h b/client/adventureMap/CTerrainRect.h index a2166a055..814fd15b8 100644 --- a/client/adventureMap/CTerrainRect.h +++ b/client/adventureMap/CTerrainRect.h @@ -16,7 +16,6 @@ VCMI_LIB_NAMESPACE_BEGIN struct CGPath; VCMI_LIB_NAMESPACE_END -class CFadeAnimation; class MapView; /// Holds information about which tiles of the terrain are shown/not shown at the screen @@ -24,9 +23,6 @@ class CTerrainRect : public CIntObject { std::shared_ptr renderer; - SDL_Surface * fadeSurface; - std::shared_ptr fadeAnim; - Point swipeInitialViewPos; Point swipeInitialRealPos; bool isSwiping; @@ -48,7 +44,6 @@ class CTerrainRect : public CIntObject public: CTerrainRect(); - ~CTerrainRect(); void moveViewBy(const Point & delta); void setViewCenter(const int3 & coordinates); @@ -56,7 +51,6 @@ public: void setLevel(int level); void setTileSize(int sizePixels); - int3 getTileCenter(); Point getViewCenter(); int getLevel(); diff --git a/client/adventureMap/MapRenderer.cpp b/client/adventureMap/MapRenderer.cpp index 1ecce92a9..add2ba05e 100644 --- a/client/adventureMap/MapRenderer.cpp +++ b/client/adventureMap/MapRenderer.cpp @@ -182,8 +182,8 @@ void MapRendererRiver::renderTile(const IMapRendererContext & context, Canvas & } } -MapRendererRoad::MapRendererRoad(): - storage(VLC->roadTypeHandler->objects.size()) +MapRendererRoad::MapRendererRoad() + : storage(VLC->roadTypeHandler->objects.size()) { for(const auto & road : VLC->roadTypeHandler->objects) storage.load(road->getIndex(), road->tilesFilename); @@ -191,19 +191,19 @@ MapRendererRoad::MapRendererRoad(): void MapRendererRoad::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates) { - const int3 coordinatesAbove = coordinates - int3(0,1,0); + const int3 coordinatesAbove = coordinates - int3(0, 1, 0); - if (context.isInMap(coordinatesAbove)) + if(context.isInMap(coordinatesAbove)) { const TerrainTile & mapTileAbove = context.getMapTile(coordinatesAbove); - if (mapTileAbove.roadType->getId() != Road::NO_ROAD) + if(mapTileAbove.roadType->getId() != Road::NO_ROAD) { int32_t terrainIndex = mapTileAbove.roadType->getIndex(); int32_t imageIndex = mapTileAbove.roadDir; int32_t rotationIndex = (mapTileAbove.extTileFlags >> 4) % 4; const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex); - target.draw(image, Point(0,0), Rect(0, 16, 32, 16)); + target.draw(image, Point(0, 0), Rect(0, 16, 32, 16)); } } @@ -215,7 +215,7 @@ void MapRendererRoad::renderTile(const IMapRendererContext & context, Canvas & t int32_t rotationIndex = (mapTile.extTileFlags >> 4) % 4; const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex); - target.draw(image, Point(0,16), Rect(0, 0, 32, 16)); + target.draw(image, Point(0, 16), Rect(0, 0, 32, 16)); } } @@ -231,31 +231,31 @@ size_t MapRendererBorder::getIndexForTile(const IMapRendererContext & context, c int3 size = context.getMapSize(); - if (tile.x < -1 || tile.x > size.x || tile.y < -1 || tile.y > size.y) - return std::abs(tile.x) % 4 + 4*(std::abs(tile.y) % 4); + if(tile.x < -1 || tile.x > size.x || tile.y < -1 || tile.y > size.y) + return std::abs(tile.x) % 4 + 4 * (std::abs(tile.y) % 4); - if (tile.x == -1 && tile.y == -1) + if(tile.x == -1 && tile.y == -1) return 16; - if (tile.x == size.x && tile.y == -1) + if(tile.x == size.x && tile.y == -1) return 17; - if (tile.x == size.x && tile.y == size.y) + if(tile.x == size.x && tile.y == size.y) return 18; - if (tile.x == -1 && tile.y == size.y) + if(tile.x == -1 && tile.y == size.y) return 19; - if (tile.y == -1) + if(tile.y == -1) return 20 + (tile.x % 4); - if (tile.x == size.x) + if(tile.x == size.x) return 24 + (tile.y % 4); - if (tile.y == size.y) + if(tile.y == size.y) return 28 + (tile.x % 4); - if (tile.x == -1) + if(tile.x == -1) return 32 + (tile.y % 4); //else - visible area, no renderable border @@ -266,7 +266,7 @@ size_t MapRendererBorder::getIndexForTile(const IMapRendererContext & context, c void MapRendererBorder::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates) { const auto & image = animation->getImage(getIndexForTile(context, coordinates)); - target.draw(image, Point(0,0)); + target.draw(image, Point(0, 0)); } MapRendererFow::MapRendererFow() @@ -278,7 +278,7 @@ MapRendererFow::MapRendererFow() static const std::vector rotations = {22, 15, 2, 13, 12, 16, 28, 17, 20, 19, 7, 24, 26, 25, 30, 32, 27}; - size_t size = fogOfWarPartialHide->size(0);//group size after next rotation + size_t size = fogOfWarPartialHide->size(0); //group size after next rotation for(const int rotation : rotations) { @@ -295,8 +295,8 @@ void MapRendererFow::renderTile(const IMapRendererContext & context, Canvas & ta const NeighborTilesInfo neighborInfo(context, coordinates); - int retBitmapID = neighborInfo.getBitmapID();// >=0 -> partial hide, <0 - full hide - if (retBitmapID < 0) + int retBitmapID = neighborInfo.getBitmapID(); // >=0 -> partial hide, <0 - full hide + if(retBitmapID < 0) { // generate a number that is predefined for each tile, // but appears random to break visible pattern in large areas of fow @@ -304,15 +304,15 @@ void MapRendererFow::renderTile(const IMapRendererContext & context, Canvas & ta size_t pseudorandomNumber = ((coordinates.x * 997) ^ (coordinates.y * 1009)) / 101; size_t imageIndex = pseudorandomNumber % fogOfWarFullHide->size(); - target.draw(fogOfWarFullHide->getImage(imageIndex), Point(0,0)); + target.draw(fogOfWarFullHide->getImage(imageIndex), Point(0, 0)); } else { - target.draw(fogOfWarPartialHide->getImage(retBitmapID), Point(0,0)); + target.draw(fogOfWarPartialHide->getImage(retBitmapID), Point(0, 0)); } } -std::shared_ptr MapRendererObjects::getAnimation(const CGObjectInstance* obj) +std::shared_ptr MapRendererObjects::getAnimation(const CGObjectInstance * obj) { const auto & info = obj->appearance; @@ -333,36 +333,26 @@ std::shared_ptr MapRendererObjects::getAnimation(const CGObjectInsta std::shared_ptr MapRendererObjects::getAnimation(const std::string & filename, bool generateMovementGroups) { - if (animations.count(filename)) + if(animations.count(filename)) return animations[filename]; auto ret = std::make_shared(filename); animations[filename] = ret; ret->preload(); - if (generateMovementGroups) + if(generateMovementGroups) { - ret->createFlippedGroup(1,13); - ret->createFlippedGroup(2,14); - ret->createFlippedGroup(3,15); + ret->createFlippedGroup(1, 13); + ret->createFlippedGroup(2, 14); + ret->createFlippedGroup(3, 15); - ret->createFlippedGroup(6,10); - ret->createFlippedGroup(7,11); - ret->createFlippedGroup(8,12); + ret->createFlippedGroup(6, 10); + ret->createFlippedGroup(7, 11); + ret->createFlippedGroup(8, 12); } return ret; } -MapRendererObjects::MapRendererObjects(const IMapRendererContext & context) -{ - auto mapSize = context.getMapSize(); - - objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]); - - for(const auto & obj : context.getAllObjects()) - onObjectInstantAdd(context, obj); -} - std::shared_ptr MapRendererObjects::getFlagAnimation(const CGObjectInstance* obj) { //TODO: relocate to config file? @@ -401,7 +391,7 @@ std::shared_ptr MapRendererObjects::getImage(const IMapRendererContext & if(!animation) return nullptr; - size_t groupIndex = getAnimationGroup(context, obj); + size_t groupIndex = context.objectGroupIndex(obj->id); if(animation->size(groupIndex) == 0) return nullptr; @@ -412,52 +402,37 @@ std::shared_ptr MapRendererObjects::getImage(const IMapRendererContext & return animation->getImage(frameIndex, groupIndex); } -size_t MapRendererObjects::getAnimationGroup(const IMapRendererContext & context, const CGObjectInstance * obj) const -{ - // 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); - return idleGroups[hero->moveDir]; - } - - if(obj->ID == Obj::BOAT) - { - const auto * boat = dynamic_cast(obj); - return idleGroups[boat->direction]; - } - - return 0; -} - -void MapRendererObjects::renderImage(Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr& image) +void MapRendererObjects::renderImage(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr& image) { if(!image) return; + uint8_t transparency = static_cast(std::round(255 * context.objectTransparency(object->id))); + + if (transparency == 0) + return; + + image->setAlpha(transparency); image->setFlagColor(object->tempOwner); - int3 offsetTiles(object->getPosition() - coordinates); + Point offsetPixels = context.objectImageOffset(object->id, coordinates); - Point offsetPixels(offsetTiles.x * 32, offsetTiles.y * 32); - Point imagePos = image->dimensions() - offsetPixels - Point(32, 32); - Point tileDimensions(32, 32); - - target.draw(image, Point(0, 0), Rect(imagePos, tileDimensions)); + if ( offsetPixels.x < image->dimensions().x && offsetPixels.y < image->dimensions().y) + { + Point imagePos = image->dimensions() - offsetPixels - Point(32, 32); + target.draw(image, Point(0, 0), Rect(imagePos, Point(32,32))); + } } void MapRendererObjects::renderObject(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance* instance) { - renderImage(target, coordinates, instance, getImage(context, instance, getAnimation(instance))); - renderImage(target, coordinates, instance, getImage(context, instance, getFlagAnimation(instance))); + renderImage(context, target, coordinates, instance, getImage(context, instance, getAnimation(instance))); + renderImage(context, target, coordinates, instance, getImage(context, instance, getFlagAnimation(instance))); } void MapRendererObjects::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates) { - for(const auto & objectID : objects[coordinates.z][coordinates.x][coordinates.y]) + for(const auto & objectID : context.getObjects(coordinates)) { const auto * objectInstance = context.getObject(objectID); @@ -472,61 +447,6 @@ void MapRendererObjects::renderTile(const IMapRendererContext & context, Canvas } } -void MapRendererObjects::onObjectInstantAdd(const IMapRendererContext & context, const CGObjectInstance * obj) -{ - if(!obj) - return; - - if(obj->ID == Obj::HERO && dynamic_cast(obj)->inTownGarrison) - return; - - if(obj->ID == Obj::BOAT && dynamic_cast(obj)->hero) - return; - - std::shared_ptr animation = getAnimation(obj); - - //no animation at all, e.g. Event - if(!animation) - return; - - //empty animation. Illegal? - assert(animation->size(0) > 0); - if(animation->size(0) == 0) - return; - - auto image = animation->getImage(0, 0); - - int imageWidthTiles = (image->width() + 31) / 32; - int imageHeightTiles = (image->height() + 31) / 32; - - int objectWidth = std::min(obj->getWidth(), imageWidthTiles); - int objectHeight = std::min(obj->getHeight(), imageHeightTiles); - - for(int fx = 0; fx < objectWidth; ++fx) - { - for(int fy = 0; fy < objectHeight; ++fy) - { - int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z); - - if(context.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(context)); - } - } - } -} - -void MapRendererObjects::onObjectInstantRemove(const IMapRendererContext & context, const CGObjectInstance * object) -{ - for(int z = 0; z < context.getMapSize().z; z++) - for(int x = 0; x < context.getMapSize().x; x++) - for(int y = 0; y < context.getMapSize().y; y++) - vstd::erase(objects[z][x][y], object->id); -} - void MapRendererDebugGrid::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates) { if(context.showGrid()) @@ -625,14 +545,9 @@ void MapRendererPath::renderTile(const IMapRendererContext & context, Canvas & t renderImageCross(target, reachableToday, iter->coord); } -MapRenderer::MapRenderer(const IMapRendererContext & context) - : rendererObjects(context) -{ -} - void MapRenderer::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates) { - if (!context.isInMap(coordinates)) + if(!context.isInMap(coordinates)) { rendererBorder.renderTile(context, target, coordinates); return; @@ -640,7 +555,7 @@ void MapRenderer::renderTile(const IMapRendererContext & context, Canvas & targe const NeighborTilesInfo neighborInfo(context, coordinates); - if (neighborInfo.areAllHidden()) + if(neighborInfo.areAllHidden()) { rendererFow.renderTile(context, target, coordinates); } @@ -652,8 +567,8 @@ void MapRenderer::renderTile(const IMapRendererContext & context, Canvas & targe rendererObjects.renderTile(context, target, coordinates); rendererPath.renderTile(context, target, coordinates); - if (!context.isVisible(coordinates)) + if(!context.isVisible(coordinates)) rendererFow.renderTile(context, target, coordinates); } - rendererDebugGrid.renderTile(context, target,coordinates); + rendererDebugGrid.renderTile(context, target, coordinates); } diff --git a/client/adventureMap/MapRenderer.h b/client/adventureMap/MapRenderer.h index d29d9b030..86a79018a 100644 --- a/client/adventureMap/MapRenderer.h +++ b/client/adventureMap/MapRenderer.h @@ -72,12 +72,8 @@ public: void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates); }; -class MapRendererObjects : public IMapObjectObserver +class MapRendererObjects { - using MapObject = ObjectInstanceID; - using MapTile = std::vector; - - boost::multi_array objects; std::map> animations; std::shared_ptr getFlagAnimation(const CGObjectInstance * obj); @@ -85,17 +81,12 @@ class MapRendererObjects : public IMapObjectObserver std::shared_ptr getAnimation(const std::string & filename, bool generateMovementGroups); std::shared_ptr getImage(const IMapRendererContext & context, const CGObjectInstance * obj, const std::shared_ptr & animation) const; - size_t getAnimationGroup(const IMapRendererContext & context, const CGObjectInstance * obj) const; - void renderImage(Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr & image); + void renderImage(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr & image); void renderObject(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance * obj); public: - explicit MapRendererObjects(const IMapRendererContext & context); void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates); - - void onObjectInstantAdd(const IMapRendererContext & context, const CGObjectInstance * object) final override; - void onObjectInstantRemove(const IMapRendererContext & context, const CGObjectInstance * object) final override; }; class MapRendererBorder @@ -150,7 +141,5 @@ class MapRenderer MapRendererDebugGrid rendererDebugGrid; public: - explicit MapRenderer(const IMapRendererContext & context); - void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates); }; diff --git a/client/adventureMap/MapRendererContext.h b/client/adventureMap/MapRendererContext.h index e65b7e98a..7c6718feb 100644 --- a/client/adventureMap/MapRendererContext.h +++ b/client/adventureMap/MapRendererContext.h @@ -15,9 +15,9 @@ VCMI_LIB_NAMESPACE_BEGIN class int3; class Point; -class ObjectInstanceID; class CGObjectInstance; class CGHeroInstance; +class ObjectInstanceID; struct TerrainTile; struct CGPath; @@ -26,9 +26,10 @@ VCMI_LIB_NAMESPACE_END class IMapRendererContext { public: - virtual ~IMapRendererContext() = default; + using MapObject = ObjectInstanceID; + using MapObjectsList = std::vector; - using ObjectsVector = std::vector>; + virtual ~IMapRendererContext() = default; /// returns dimensions of current map virtual int3 getMapSize() const = 0; @@ -39,8 +40,8 @@ public: /// returns tile by selected coordinates. Coordinates MUST be valid virtual const TerrainTile & getMapTile(const int3 & coordinates) const = 0; - /// returns vector of all objects present on current map - virtual ObjectsVector getAllObjects() const = 0; + /// returns all objects visible on specified tile + virtual const MapObjectsList & getObjects(const int3 & coordinates) const = 0; /// returns specific object by ID, or nullptr if not found virtual const CGObjectInstance * getObject(ObjectInstanceID objectID) const = 0; @@ -51,6 +52,12 @@ public: /// returns true if specified tile is visible in current context virtual bool isVisible(const int3 & coordinates) const = 0; + virtual size_t objectGroupIndex(ObjectInstanceID objectID) const = 0; + virtual Point objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const = 0; + + /// returns object animation transparency. IF set to 0, object will not be visible + virtual double objectTransparency(ObjectInstanceID objectID) const = 0; + /// returns how long should each frame of animation be visible, in milliseconds virtual uint32_t getAnimationPeriod() const = 0; @@ -70,24 +77,26 @@ public: IMapObjectObserver(); virtual ~IMapObjectObserver(); + virtual bool hasOngoingAnimations() = 0; + /// Plays fade-in animation and adds object to map - virtual void onObjectFadeIn(const IMapRendererContext & context, const CGObjectInstance * obj) {} + virtual void onObjectFadeIn(const CGObjectInstance * obj) {} /// Plays fade-out animation and removed object from map - virtual void onObjectFadeOut(const IMapRendererContext & context, const CGObjectInstance * obj) {} + virtual void onObjectFadeOut(const CGObjectInstance * obj) {} /// Adds object to map instantly, with no animation - virtual void onObjectInstantAdd(const IMapRendererContext & context, const CGObjectInstance * obj) {} + virtual void onObjectInstantAdd(const CGObjectInstance * obj) {} /// Removes object from map instantly, with no animation - virtual void onObjectInstantRemove(const IMapRendererContext & context, const CGObjectInstance * obj) {} + virtual void onObjectInstantRemove(const CGObjectInstance * obj) {} /// Perform hero teleportation animation with terrain fade animation - virtual void onHeroTeleported(const IMapRendererContext & context, const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} + virtual void onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} /// Perform hero movement animation, moving hero across terrain - virtual void onHeroMoved(const IMapRendererContext & context, const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} + virtual void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} /// Instantly rotates hero to face destination tile - virtual void onHeroRotated(const IMapRendererContext & context, const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} + virtual void onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} }; diff --git a/client/adventureMap/MapView.cpp b/client/adventureMap/MapView.cpp index 445a833b7..0ef5d2abb 100644 --- a/client/adventureMap/MapView.cpp +++ b/client/adventureMap/MapView.cpp @@ -11,6 +11,8 @@ #include "StdInc.h" #include "MapView.h" +#include + #include "MapRenderer.h" #include "mapHandler.h" #include "CAdvMapInt.h" @@ -20,7 +22,6 @@ #include "../CPlayerInterface.h" #include "../gui/CGuiHandler.h" #include "../render/CAnimation.h" -#include "../render/CFadeAnimation.h" #include "../render/Canvas.h" #include "../render/Colors.h" #include "../render/Graphics.h" @@ -41,51 +42,45 @@ #include "../../lib/mapObjects/CObjectClassesHandler.h" #include "../../lib/mapping/CMap.h" -MapCache::~MapCache() = default; +MapViewCache::~MapViewCache() = default; -MapCache::MapCache(const std::shared_ptr & model) +MapViewCache::MapViewCache(const std::shared_ptr & model) : model(model) - , context(new MapRendererContext()) - , mapRenderer(new MapRenderer(*context)) + , mapRenderer(new MapRenderer()) + , terrain(new Canvas(model->getCacheDimensionsPixels())) { - terrain = std::make_unique(model->getCacheDimensionsPixels()); } -Canvas MapCache::getTile(const int3 & coordinates) +Canvas MapViewCache::getTile(const int3 & coordinates) { return Canvas(*terrain, model->getCacheTileArea(coordinates)); } -void MapCache::updateTile(const int3 & coordinates) +void MapViewCache::updateTile(const std::shared_ptr & context, const int3 & coordinates) { Canvas target = getTile(coordinates); mapRenderer->renderTile(*context, target, coordinates); } -void MapCache::update(uint32_t timeDelta) +void MapViewCache::update(const std::shared_ptr & context) { - context->advanceAnimations(timeDelta); - context->setTileSize(model->getSingleTileSize()); - Rect dimensions = model->getTilesTotalRect(); for(int y = dimensions.top(); y < dimensions.bottom(); ++y) for(int x = dimensions.left(); x < dimensions.right(); ++x) - updateTile({x, y, model->getLevel()}); + updateTile(context, {x, y, model->getLevel()}); } -void MapCache::render(Canvas & target) +void MapViewCache::render(Canvas & target) { - update(GH.mainFPSmng->getElapsedMilliseconds()); - Rect dimensions = model->getTilesTotalRect(); for(int y = dimensions.top(); y < dimensions.bottom(); ++y) { for(int x = dimensions.left(); x < dimensions.right(); ++x) { - int3 tile( x,y,model->getLevel()); + int3 tile(x, y, model->getLevel()); Canvas source = getTile(tile); Rect targetRect = model->getTargetTileArea(tile); target.draw(source, targetRect.topLeft()); @@ -98,8 +93,8 @@ std::shared_ptr MapView::createModel(const Point & dimensions) con auto result = std::make_shared(); result->setLevel(0); - result->setTileSize(Point(32,32)); - result->setViewCenter(Point(0,0)); + result->setTileSize(Point(32, 32)); + result->setViewCenter(Point(0, 0)); result->setViewDimensions(dimensions); return result; @@ -107,12 +102,13 @@ std::shared_ptr MapView::createModel(const Point & dimensions) con MapView::MapView(const Point & offset, const Point & dimensions) : model(createModel(dimensions)) - , tilesCache(new MapCache(model)) + , context(new MapRendererContext()) + , controller(new MapViewController(context, model)) + , tilesCache(new MapViewCache(model)) { pos += offset; pos.w = dimensions.x; pos.h = dimensions.y; - } void MapView::show(SDL_Surface * to) @@ -122,6 +118,8 @@ void MapView::show(SDL_Surface * to) CSDL_Ext::CClipRectGuard guard(to, pos); + controller->update(GH.mainFPSmng->getElapsedMilliseconds()); + tilesCache->update(context); tilesCache->render(targetClipped); } @@ -130,11 +128,6 @@ void MapView::showAll(SDL_Surface * to) show(to); } -void MapRendererContext::advanceAnimations(uint32_t ms) -{ - animationTime += ms; -} - int3 MapRendererContext::getMapSize() const { return LOCPLINT->cb->getMapSize(); @@ -150,11 +143,6 @@ const TerrainTile & MapRendererContext::getMapTile(const int3 & coordinates) con return CGI->mh->getMap()->getTile(coordinates); } -MapRendererContext::ObjectsVector MapRendererContext::getAllObjects() const -{ - return CGI->mh->getMap()->objects; -} - const CGObjectInstance * MapRendererContext::getObject(ObjectInstanceID objectID) const { return CGI->mh->getMap()->objects.at(objectID.getNum()); @@ -165,11 +153,6 @@ bool MapRendererContext::isVisible(const int3 & coordinates) const return LOCPLINT->cb->isVisible(coordinates) || settings["session"]["spectate"].Bool(); } -void MapRendererContext::setTileSize(const Point & dimensions) -{ - tileSize = dimensions; -} - const CGPath * MapRendererContext::currentPath() const { const auto * hero = adventureInt->curHero(); @@ -208,19 +191,19 @@ bool MapRendererContext::showGrid() const return true; // settings["session"]["showGrid"].Bool(); } -void MapView::setViewCenter(const int3 & position) +void MapViewController::setViewCenter(const int3 & position) { model->setViewCenter(Point(position.x, position.y) * model->getSingleTileSize()); model->setLevel(position.z); } -void MapView::setViewCenter(const Point & position, int level) +void MapViewController::setViewCenter(const Point & position, int level) { model->setViewCenter(position); model->setLevel(level); } -void MapView::setTileSize(const Point & tileSize) +void MapViewController::setTileSize(const Point & tileSize) { model->setTileSize(tileSize); } @@ -290,11 +273,6 @@ Rect MapViewModel::getTilesTotalRect() const ); } -int3 MapViewModel::getTileCenter() const -{ - return getTileAtPoint(getMapViewCenter()); -} - int3 MapViewModel::getTileAtPoint(const Point & position) const { Point topLeftOffset = getMapViewCenter() - getPixelsVisibleDimensions() / 2; @@ -335,8 +313,259 @@ Rect MapViewModel::getTargetTileArea(const int3 & coordinates) const Point tilePosAbsolute = Point(coordinates) * tileSize; Point tilePosRelative = tilePosAbsolute - topLeftOffset; - return { - tilePosRelative, - tileSize - }; + return Rect(tilePosRelative, tileSize); +} + +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 +{ + 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]; + } + + if(obj->ID == Obj::BOAT) + { + const auto * boat = dynamic_cast(obj); + if (movementAnimation && movementAnimation->target == objectID) + return moveGroups[boat->direction]; + return idleGroups[boat->direction]; + } + return 0; +} + +Point MapRendererContext::objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const +{ + if (movementAnimation && movementAnimation->target == objectID) + { + int3 offsetTilesFrom = movementAnimation->tileFrom - coordinates; + int3 offsetTilesDest = movementAnimation->tileDest - coordinates; + + Point offsetPixelsFrom = Point(offsetTilesFrom) * Point(32,32); + Point offsetPixelsDest = Point(offsetTilesDest) * Point(32,32); + + Point result = vstd::lerp(offsetPixelsFrom, offsetPixelsDest, movementAnimation->progress); + + return result; + } + + const CGObjectInstance * object = getObject(objectID); + int3 offsetTiles(object->getPosition() - coordinates); + return Point(offsetTiles) * Point(32, 32); +} + +double MapRendererContext::objectTransparency(ObjectInstanceID objectID) const +{ + if (fadeOutAnimation && objectID == fadeOutAnimation->target) + return 1.0 - fadeOutAnimation->progress; + + if (fadeInAnimation && objectID == fadeInAnimation->target) + return fadeInAnimation->progress; + + return 1.0; +} + +MapViewController::MapViewController(std::shared_ptr context, std::shared_ptr model) + : context(std::move(context)) + , model(std::move(model)) +{ +} + +void MapViewController::update(uint32_t timeDelta) +{ + static const double fadeOutDuration = 1.0; + static const double fadeInDuration = 1.0; + static const double heroMoveDuration = 1.0; + static const double heroTeleportDuration = 1.0; + + //FIXME: remove code duplication? + + if (context->movementAnimation) + { + context->movementAnimation->progress += heroMoveDuration * timeDelta / 1000; + + Point positionFrom = Point(context->movementAnimation->tileFrom) * model->getSingleTileSize(); + Point positionDest = Point(context->movementAnimation->tileDest) * model->getSingleTileSize(); + + Point positionCurr = vstd::lerp(positionFrom, positionDest, context->movementAnimation->progress); + + setViewCenter(positionCurr, context->movementAnimation->tileDest.z); + + if (context->movementAnimation->progress >= 1.0) + { + setViewCenter(context->movementAnimation->tileDest); + + context->removeObject(context->getObject(context->movementAnimation->target)); + context->addObject(context->getObject(context->movementAnimation->target)); + context->movementAnimation.reset(); + } + } + + if (context->teleportAnimation) + { + context->teleportAnimation->progress += heroTeleportDuration * timeDelta / 1000; + if (context->teleportAnimation->progress >= 1.0) + context->teleportAnimation.reset(); + } + + if (context->fadeOutAnimation) + { + context->fadeOutAnimation->progress += fadeOutDuration * timeDelta / 1000; + if (context->fadeOutAnimation->progress >= 1.0) + { + context->removeObject(context->getObject(context->fadeOutAnimation->target)); + context->fadeOutAnimation.reset(); + } + } + + if (context->fadeInAnimation) + { + context->fadeInAnimation->progress += fadeInDuration * timeDelta / 1000; + if (context->fadeInAnimation->progress >= 1.0) + context->fadeInAnimation.reset(); + } + + context->animationTime += timeDelta; + context->tileSize =model->getSingleTileSize(); +} + +void MapViewController::onObjectFadeIn(const CGObjectInstance * obj) +{ + assert(!context->fadeInAnimation); + context->fadeInAnimation = FadingAnimationState{obj->id, 0.0}; + context->addObject(obj); +} + +void MapViewController::onObjectFadeOut(const CGObjectInstance * obj) +{ + assert(!context->fadeOutAnimation); + context->fadeOutAnimation = FadingAnimationState{obj->id, 0.0}; +} + +void MapViewController::onObjectInstantAdd(const CGObjectInstance * obj) +{ + context->addObject(obj); +}; + +void MapViewController::onObjectInstantRemove(const CGObjectInstance * obj) +{ + context->removeObject(obj); +}; + +void MapViewController::onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) +{ + assert(!context->teleportAnimation); + context->teleportAnimation = HeroAnimationState{ obj->id, from, dest, 0.0 }; +} + +void MapViewController::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) +{ + assert(!context->movementAnimation); + context->removeObject(obj); + context->addMovingObject(obj, from, dest); + context->movementAnimation = HeroAnimationState{ obj->id, from, dest, 0.0 }; +} + +void MapViewController::onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) +{ + //TODO +} + +std::shared_ptr MapView::getController() +{ + return controller; +} + +bool MapViewController::hasOngoingAnimations() +{ + if(context->movementAnimation) + return true; + + if(context->teleportAnimation) + return true; + + if(context->fadeOutAnimation) + return true; + + if(context->fadeInAnimation) + return true; + + return false; } diff --git a/client/adventureMap/MapView.h b/client/adventureMap/MapView.h index c8e3d7e47..0b9aadb1f 100644 --- a/client/adventureMap/MapView.h +++ b/client/adventureMap/MapView.h @@ -9,37 +9,65 @@ */ #pragma once -#include "../gui/CIntObject.h" - #include "MapRendererContext.h" +#include "../gui/CIntObject.h" +#include "../lib/int3.h" class Canvas; class MapRenderer; +class MapViewController; + +struct HeroAnimationState +{ + ObjectInstanceID target; + int3 tileFrom; + int3 tileDest; + double progress; +}; + +struct FadingAnimationState +{ + ObjectInstanceID target; + double progress; +}; class MapRendererContext : public IMapRendererContext { + friend class MapViewController; + + boost::multi_array objects; + Point tileSize = Point(32, 32); uint32_t animationTime = 0; + boost::optional movementAnimation; + boost::optional teleportAnimation; + + boost::optional fadeOutAnimation; + boost::optional fadeInAnimation; + public: - void advanceAnimations(uint32_t ms); - void setTileSize(const Point & dimensions); + MapRendererContext(); - int3 getMapSize() const override; - bool isInMap(const int3 & coordinates) const override; - const TerrainTile & getMapTile(const int3 & coordinates) const override; - - ObjectsVector getAllObjects() const override; - const CGObjectInstance * getObject(ObjectInstanceID objectID) const override; - - const CGPath * currentPath() const override; + 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; bool isVisible(const int3 & coordinates) const override; + const TerrainTile & getMapTile(const int3 & coordinates) const override; + const MapObjectsList & getObjects(const int3 & coordinates) const override; + const CGObjectInstance * getObject(ObjectInstanceID objectID) const override; + const CGPath * currentPath() const override; + + size_t objectGroupIndex(ObjectInstanceID objectID) const override; + Point objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const override; + double objectTransparency(ObjectInstanceID objectID) const override; uint32_t getAnimationPeriod() const override; uint32_t getAnimationTime() const override; Point getTileSize() const override; - bool showGrid() const override; }; @@ -81,50 +109,79 @@ public: /// returns area covered by specified tile in target view Rect getTargetTileArea(const int3 & coordinates) const; - int getLevel() const; - int3 getTileCenter() const; - /// returns tile under specified position in target view int3 getTileAtPoint(const Point & position) const; + + /// returns currently visible map level + int getLevel() const; }; -class MapCache +/// Class responsible for rendering of entire map view +/// uses rendering parameters provided by owner class +class MapViewCache { - std::unique_ptr terrain; std::shared_ptr model; - std::unique_ptr context; + std::unique_ptr terrain; std::unique_ptr mapRenderer; Canvas getTile(const int3 & coordinates); - void updateTile(const int3 & coordinates); + void updateTile(const std::shared_ptr & context, const int3 & coordinates); public: - explicit MapCache(const std::shared_ptr & model); - ~MapCache(); + explicit MapViewCache(const std::shared_ptr & model); + ~MapViewCache(); - void update(uint32_t timeDelta); + /// updates internal terrain cache according to provided time delta + void update(const std::shared_ptr & context); + + /// renders updated terrain cache onto provided canvas void render(Canvas & target); }; +/// Class responsible for updating view state, +/// such as its position and any animations +class MapViewController : public IMapObjectObserver +{ + std::shared_ptr context; + std::shared_ptr model; + +private: + // 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 onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override; + void onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override; + +public: + MapViewController(std::shared_ptr context, std::shared_ptr model); + + void setViewCenter(const int3 & position); + void setViewCenter(const Point & position, int level); + void setTileSize(const Point & tileSize); + void update(uint32_t timeDelta); +}; + +/// Main map rendering class that mostly acts as container for component classes class MapView : public CIntObject { std::shared_ptr model; - std::unique_ptr tilesCache; + std::shared_ptr context; + std::unique_ptr tilesCache; + std::shared_ptr controller; std::shared_ptr createModel(const Point & dimensions) const; public: std::shared_ptr getModel() const; + std::shared_ptr getController(); MapView(const Point & offset, const Point & dimensions); - void setViewCenter(const int3 & position); - void setViewCenter(const Point & position, int level); - void setTileSize(const Point & tileSize); - - void moveHero(); - void show(SDL_Surface * to) override; void showAll(SDL_Surface * to) override; }; diff --git a/client/adventureMap/mapHandler.cpp b/client/adventureMap/mapHandler.cpp index 161449510..6754fb7a6 100644 --- a/client/adventureMap/mapHandler.cpp +++ b/client/adventureMap/mapHandler.cpp @@ -14,7 +14,6 @@ #include "MapView.h" #include "../render/CAnimation.h" -#include "../render/CFadeAnimation.h" #include "../render/Colors.h" #include "../gui/CGuiHandler.h" #include "../renderSDL/SDL_Extensions.h" @@ -27,6 +26,7 @@ #include "../../CCallback.h" +#include "../../lib/UnlockGuard.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CObjectClassesHandler.h" #include "../../lib/mapping/CMap.h" @@ -476,10 +476,24 @@ bool CMapHandler::hideObject(const CGObjectInstance * obj, bool fadeout) } */ -bool CMapHandler::hasActiveAnimations() +bool CMapHandler::hasOngoingAnimations() { - return false; // !fadeAnims.empty(); // don't allow movement during fade animation + for (auto * observer : observers) + if (observer->hasOngoingAnimations()) + return true; + + return false; } + +void CMapHandler::waitForOngoingAnimations() +{ + while (CGI->mh->hasOngoingAnimations()) + { + auto unlockPim = vstd::makeUnlockGuard(*CPlayerInterface::pim); + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } +} + /* void CMapHandler::updateWater() //shift colors in palettes of water tiles { @@ -600,26 +614,6 @@ bool CMapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObj return false; } -TerrainTileObject::TerrainTileObject(const CGObjectInstance * obj_, Rect rect_, bool visitablePos) - : obj(obj_), - rect(rect_), - fadeAnimKey(-1) -{ - // We store information about ambient sound is here because object might disappear while sound is updating - if(obj->getAmbientSound()) - { - // All tiles of static objects are sound sources. E.g Volcanos and special terrains - // For visitable object only their visitable tile is sound source - if(!CCS->soundh->ambientCheckVisitable() || !obj->isVisitable() || visitablePos) - ambientSound = obj->getAmbientSound(); - } -} - -TerrainTileObject::~TerrainTileObject() -{ - -} - CMapHandler::CMapHandler(const CMap * map) : map(map) { @@ -652,45 +646,47 @@ std::vector CMapHandler::getAmbientSounds(const int3 & tile) void CMapHandler::onObjectFadeIn(const CGObjectInstance * obj) { - onObjectInstantAdd(obj); + for (auto * observer : observers) + observer->onObjectFadeIn(obj); } void CMapHandler::onObjectFadeOut(const CGObjectInstance * obj) { - onObjectInstantRemove(obj); + for (auto * observer : observers) + observer->onObjectFadeOut(obj); } void CMapHandler::onObjectInstantAdd(const CGObjectInstance * obj) { - MapRendererContext context; for (auto * observer : observers) - observer->onObjectInstantAdd(context, obj); + observer->onObjectInstantAdd(obj); } void CMapHandler::onObjectInstantRemove(const CGObjectInstance * obj) { - MapRendererContext context; for (auto * observer : observers) - observer->onObjectInstantRemove(context, obj); + observer->onObjectInstantRemove(obj); } void CMapHandler::onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) { assert(obj->pos == dest); - onObjectInstantRemove(obj); - onObjectInstantAdd(obj); + for (auto * observer : observers) + observer->onHeroTeleported(obj, from, dest); } void CMapHandler::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) { assert(obj->pos == dest); - onObjectInstantRemove(obj); - onObjectInstantAdd(obj); + for (auto * observer : observers) + observer->onHeroMoved(obj, from, dest); } void CMapHandler::onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) { - //TODO + assert(obj->pos == from); + for (auto * observer : observers) + observer->onHeroRotated(obj, from, dest); } void CMapHandler::addMapObserver(IMapObjectObserver * object) diff --git a/client/adventureMap/mapHandler.h b/client/adventureMap/mapHandler.h index 66fd16738..956ffef2c 100644 --- a/client/adventureMap/mapHandler.h +++ b/client/adventureMap/mapHandler.h @@ -38,7 +38,6 @@ VCMI_LIB_NAMESPACE_END struct SDL_Surface; class CAnimation; class IImage; -class CFadeAnimation; class CMapHandler; class IMapObjectObserver; @@ -63,30 +62,6 @@ enum class EWorldViewIcon RES_CRYSTAL, RES_GEM, RES_GOLD, - -}; - -enum class EMapObjectFadingType -{ - NONE, - IN, - OUT -}; - -struct TerrainTileObject -{ - const CGObjectInstance *obj; - Rect rect; - int fadeAnimKey; - boost::optional ambientSound; - - TerrainTileObject(const CGObjectInstance *obj_, Rect rect_, bool visitablePos = false); - ~TerrainTileObject(); -}; - -struct TerrainTile2 -{ - std::vector objects; //pointers to objects being on this tile with rects to be easier to blit this tile on screen }; class CMapHandler @@ -125,7 +100,10 @@ public: bool hasObjectHole(const int3 & pos) const; /// determines if the map is ready to handle new hero movement (not available during fading animations) - bool hasActiveAnimations(); + bool hasOngoingAnimations(); + + /// blocking wait until all ongoing animatins are over + void waitForOngoingAnimations(); static bool compareObjectBlitOrder(const CGObjectInstance * a, const CGObjectInstance * b); }; diff --git a/client/render/CFadeAnimation.cpp b/client/render/CFadeAnimation.cpp deleted file mode 100644 index d2e5c9e10..000000000 --- a/client/render/CFadeAnimation.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * CFadeAnimation.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 "CFadeAnimation.h" - -#include "../renderSDL/SDL_Extensions.h" - -#include - -float CFadeAnimation::initialCounter() const -{ - if (fadingMode == EMode::OUT) - return 1.0f; - return 0.0f; -} - -void CFadeAnimation::update() -{ - if (!fading) - return; - - if (fadingMode == EMode::OUT) - fadingCounter -= delta; - else - fadingCounter += delta; - - if (isFinished()) - { - fading = false; - if (shouldFreeSurface) - { - SDL_FreeSurface(fadingSurface); - fadingSurface = nullptr; - } - } -} - -bool CFadeAnimation::isFinished() const -{ - if (fadingMode == EMode::OUT) - return fadingCounter <= 0.0f; - return fadingCounter >= 1.0f; -} - -CFadeAnimation::CFadeAnimation() - : delta(0), fadingSurface(nullptr), fading(false), fadingCounter(0), shouldFreeSurface(false), - fadingMode(EMode::NONE) -{ -} - -CFadeAnimation::~CFadeAnimation() -{ - if (fadingSurface && shouldFreeSurface) - SDL_FreeSurface(fadingSurface); -} - -void CFadeAnimation::init(EMode mode, SDL_Surface * sourceSurface, bool freeSurfaceAtEnd, float animDelta) -{ - if (fading) - { - // in that case, immediately finish the previous fade - // (alternatively, we could just return here to ignore the new fade request until this one finished (but we'd need to free the passed bitmap to avoid leaks)) - logGlobal->warn("Tried to init fading animation that is already running."); - if (fadingSurface && shouldFreeSurface) - SDL_FreeSurface(fadingSurface); - } - if (animDelta <= 0.0f) - { - logGlobal->warn("Fade anim: delta should be positive; %f given.", animDelta); - animDelta = DEFAULT_DELTA; - } - - if (sourceSurface) - fadingSurface = sourceSurface; - - delta = animDelta; - fadingMode = mode; - fadingCounter = initialCounter(); - fading = true; - shouldFreeSurface = freeSurfaceAtEnd; -} - -void CFadeAnimation::draw(SDL_Surface * targetSurface, const Point &targetPoint) -{ - if (!fading || !fadingSurface || fadingMode == EMode::NONE) - { - fading = false; - return; - } - - CSDL_Ext::setAlpha(fadingSurface, (int)(fadingCounter * 255)); - CSDL_Ext::blitSurface(fadingSurface, targetSurface, targetPoint); //FIXME - CSDL_Ext::setAlpha(fadingSurface, 255); -} diff --git a/client/render/CFadeAnimation.h b/client/render/CFadeAnimation.h deleted file mode 100644 index b253ce828..000000000 --- a/client/render/CFadeAnimation.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * CFadeAnimation.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 - -#ifdef IN -#undef IN -#endif - -#ifdef OUT -#undef OUT -#endif - -VCMI_LIB_NAMESPACE_BEGIN -class Point; -VCMI_LIB_NAMESPACE_END - -struct SDL_Surface; - -const float DEFAULT_DELTA = 0.05f; - -class CFadeAnimation -{ -public: - enum class EMode - { - NONE, IN, OUT - }; -private: - float delta; - SDL_Surface * fadingSurface; - bool fading; - float fadingCounter; - bool shouldFreeSurface; - - float initialCounter() const; - bool isFinished() const; -public: - EMode fadingMode; - - CFadeAnimation(); - ~CFadeAnimation(); - void init(EMode mode, SDL_Surface * sourceSurface, bool freeSurfaceAtEnd = false, float animDelta = DEFAULT_DELTA); - void update(); - void draw(SDL_Surface * targetSurface, const Point & targetPoint); - bool isFading() const { return fading; } -};