Explorar el Código

Implemented basic hero movement animation & object fade-out

Ivan Savenko hace 2 años
padre
commit
7cf00ba87d

+ 0 - 2
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

+ 1 - 13
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;

+ 6 - 1
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)

+ 3 - 3
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;
 			}

+ 7 - 29
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<CFadeAnimation>())
-	, 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<MapView>( 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));
 }

+ 0 - 6
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<MapView> renderer;
 
-	SDL_Surface * fadeSurface;
-	std::shared_ptr<CFadeAnimation> 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();
 

+ 52 - 137
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<int> 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<CAnimation> MapRendererObjects::getAnimation(const CGObjectInstance* obj)
+std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const CGObjectInstance * obj)
 {
 	const auto & info = obj->appearance;
 
@@ -333,36 +333,26 @@ std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const CGObjectInsta
 
 std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const std::string & filename, bool generateMovementGroups)
 {
-	if (animations.count(filename))
+	if(animations.count(filename))
 		return animations[filename];
 
 	auto ret = std::make_shared<CAnimation>(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<CAnimation> MapRendererObjects::getFlagAnimation(const CGObjectInstance* obj)
 {
 	//TODO: relocate to config file?
@@ -401,7 +391,7 @@ std::shared_ptr<IImage> 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<IImage> MapRendererObjects::getImage(const IMapRendererContext &
 	return animation->getImage(frameIndex, groupIndex);
 }
 
-size_t MapRendererObjects::getAnimationGroup(const IMapRendererContext & context, const CGObjectInstance * obj) const
+void MapRendererObjects::renderImage(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr<IImage>& image)
 {
-	// TODO
-	//static const std::vector<size_t> moveGroups = {99, 10, 5, 6, 7, 8, 9, 12, 11};
-	static const std::vector<size_t> idleGroups = {99, 13, 0, 1, 2, 3, 4, 15, 14};
-
-	if(obj->ID == Obj::HERO)
-	{
-		const auto * hero = dynamic_cast<const CGHeroInstance *>(obj);
-		return idleGroups[hero->moveDir];
-	}
-
-	if(obj->ID == Obj::BOAT)
-	{
-		const auto * boat = dynamic_cast<const CGBoat *>(obj);
-		return idleGroups[boat->direction];
-	}
+	if(!image)
+		return;
 
-	return 0;
-}
+	uint8_t transparency = static_cast<uint8_t>(std::round(255 * context.objectTransparency(object->id)));
 
-void MapRendererObjects::renderImage(Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr<IImage>& image)
-{
-	if(!image)
+	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<const CGHeroInstance *>(obj)->inTownGarrison)
-		return;
-
-	if(obj->ID == Obj::BOAT && dynamic_cast<const CGBoat *>(obj)->hero)
-		return;
-
-	std::shared_ptr<CAnimation> 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);
 }

+ 2 - 13
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<MapObject>;
-
-	boost::multi_array<MapTile, 3> objects;
 	std::map<std::string, std::shared_ptr<CAnimation>> animations;
 
 	std::shared_ptr<CAnimation> getFlagAnimation(const CGObjectInstance * obj);
@@ -85,17 +81,12 @@ class MapRendererObjects : public IMapObjectObserver
 	std::shared_ptr<CAnimation> getAnimation(const std::string & filename, bool generateMovementGroups);
 
 	std::shared_ptr<IImage> getImage(const IMapRendererContext & context, const CGObjectInstance * obj, const std::shared_ptr<CAnimation> & 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<IImage> & image);
+	void renderImage(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr<IImage> & 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);
 };

+ 21 - 12
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<MapObject>;
 
-	using ObjectsVector = std::vector<ConstTransitivePtr<CGObjectInstance>>;
+	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) {}
 };

+ 277 - 48
client/adventureMap/MapView.cpp

@@ -11,6 +11,8 @@
 #include "StdInc.h"
 #include "MapView.h"
 
+#include <utility>
+
 #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<MapViewModel> & model)
+MapViewCache::MapViewCache(const std::shared_ptr<MapViewModel> & model)
 	: model(model)
-	, context(new MapRendererContext())
-	, mapRenderer(new MapRenderer(*context))
+	, mapRenderer(new MapRenderer())
+	, terrain(new Canvas(model->getCacheDimensionsPixels()))
 {
-	terrain = std::make_unique<Canvas>(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<MapRendererContext> & 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<MapRendererContext> & 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<MapViewModel> MapView::createModel(const Point & dimensions) con
 	auto result = std::make_shared<MapViewModel>();
 
 	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<MapViewModel> 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<size_t> moveGroups = {99, 10, 5, 6, 7, 8, 9, 12, 11};
+	static const std::vector<size_t> idleGroups = {99, 13, 0, 1, 2, 3, 4, 15, 14};
+
+	if(obj->ID == Obj::HERO)
+	{
+		const auto * hero = dynamic_cast<const CGHeroInstance *>(obj);
+		if (movementAnimation && movementAnimation->target == objectID)
+			return moveGroups[hero->moveDir];
+		return idleGroups[hero->moveDir];
+	}
+
+	if(obj->ID == Obj::BOAT)
+	{
+		const auto * boat = dynamic_cast<const CGBoat *>(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<MapRendererContext> context, std::shared_ptr<MapViewModel> 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<MapViewController> 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;
 }

+ 86 - 29
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<MapObjectsList, 3> objects;
+
 	Point tileSize = Point(32, 32);
 	uint32_t animationTime = 0;
 
-public:
-	void advanceAnimations(uint32_t ms);
-	void setTileSize(const Point & dimensions);
+	boost::optional<HeroAnimationState> movementAnimation;
+	boost::optional<HeroAnimationState> teleportAnimation;
 
-	int3 getMapSize() const override;
-	bool isInMap(const int3 & coordinates) const override;
-	const TerrainTile & getMapTile(const int3 & coordinates) const override;
+	boost::optional<FadingAnimationState> fadeOutAnimation;
+	boost::optional<FadingAnimationState> fadeInAnimation;
 
-	ObjectsVector getAllObjects() const override;
-	const CGObjectInstance * getObject(ObjectInstanceID objectID) const override;
+public:
+	MapRendererContext();
 
-	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,49 +109,78 @@ 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<Canvas> terrain;
 	std::shared_ptr<MapViewModel> model;
 
-	std::unique_ptr<MapRendererContext> context;
+	std::unique_ptr<Canvas> terrain;
 	std::unique_ptr<MapRenderer> mapRenderer;
 
 	Canvas getTile(const int3 & coordinates);
-	void updateTile(const int3 & coordinates);
+	void updateTile(const std::shared_ptr<MapRendererContext> & context, const int3 & coordinates);
 
 public:
-	explicit MapCache(const std::shared_ptr<MapViewModel> & model);
-	~MapCache();
+	explicit MapViewCache(const std::shared_ptr<MapViewModel> & model);
+	~MapViewCache();
 
-	void update(uint32_t timeDelta);
+	/// updates internal terrain cache according to provided time delta
+	void update(const std::shared_ptr<MapRendererContext> & context);
+
+	/// renders updated terrain cache onto provided canvas
 	void render(Canvas & target);
 };
 
-class MapView : public CIntObject
+/// Class responsible for updating view state,
+/// such as its position and any animations
+class MapViewController : public IMapObjectObserver
 {
+	std::shared_ptr<MapRendererContext> context;
 	std::shared_ptr<MapViewModel> model;
-	std::unique_ptr<MapCache> tilesCache;
 
-	std::shared_ptr<MapViewModel> createModel(const Point & dimensions) const;
+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:
-	std::shared_ptr<const MapViewModel> getModel() const;
-
-	MapView(const Point & offset, const Point & dimensions);
+	MapViewController(std::shared_ptr<MapRendererContext> context, std::shared_ptr<MapViewModel> 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<MapViewModel> model;
+	std::shared_ptr<MapRendererContext> context;
+	std::unique_ptr<MapViewCache> tilesCache;
+	std::shared_ptr<MapViewController> controller;
 
-	void moveHero();
+	std::shared_ptr<MapViewModel> createModel(const Point & dimensions) const;
+
+public:
+	std::shared_ptr<const MapViewModel> getModel() const;
+	std::shared_ptr<MapViewController> getController();
+
+	MapView(const Point & offset, const Point & dimensions);
 
 	void show(SDL_Surface * to) override;
 	void showAll(SDL_Surface * to) override;

+ 30 - 34
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<std::string> 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)

+ 4 - 26
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<std::string> ambientSound;
-
-	TerrainTileObject(const CGObjectInstance *obj_, Rect rect_, bool visitablePos = false);
-	~TerrainTileObject();
-};
-
-struct TerrainTile2
-{
-	std::vector<TerrainTileObject> 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);
 };

+ 0 - 101
client/render/CFadeAnimation.cpp

@@ -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 <SDL_surface.h>
-
-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);
-}

+ 0 - 53
client/render/CFadeAnimation.h

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