Prechádzať zdrojové kódy

Merge pull request #84 from Fayth/test/advMapFading

If no one objects, I will try to merge this.
DjWarmonger 10 rokov pred
rodič
commit
2a082e6c21

+ 42 - 42
client/CPlayerInterface.cpp

@@ -89,9 +89,9 @@ CondSh<EMoveState> stillMoveHero; //used during hero movement
 
 int CPlayerInterface::howManyPeople = 0;
 
-static bool objectBlitOrderSorter(const std::pair<const CGObjectInstance*,SDL_Rect>  & a, const std::pair<const CGObjectInstance*,SDL_Rect> & b)
+static bool objectBlitOrderSorter(const TerrainTileObject  & a, const TerrainTileObject & b)
 {
-	return CMapHandler::compareObjectBlitOrder(a.first, b.first);
+	return CMapHandler::compareObjectBlitOrder(a.obj, b.obj);
 }
 
 CPlayerInterface::CPlayerInterface(PlayerColor Player)
@@ -205,9 +205,9 @@ STRONG_INLINE void subRect(const int & x, const int & y, const int & z, const SD
 {
 	TerrainTile2 & hlp = CGI->mh->ttiles[x][y][z];
 	for(auto & elem : hlp.objects)
-		if(elem.first->id == hid)
+		if(elem.obj->id == hid)
 		{
-			elem.second = r;
+			elem.rect = r;
 			return;
 		}
 }
@@ -216,7 +216,7 @@ STRONG_INLINE void delObjRect(const int & x, const int & y, const int & z, const
 {
 	TerrainTile2 & hlp = CGI->mh->ttiles[x][y][z];
 	for(int h=0; h<hlp.objects.size(); ++h)
-		if(hlp.objects[h].first->id == hid)
+		if(hlp.objects[h].obj->id == hid)
 		{
 			hlp.objects.erase(hlp.objects.begin()+h);
 			return;
@@ -239,8 +239,8 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 		// TODO -> we should not need full CGHeroInstance structure to display animation or it should not be handled by playerint (but by the client itself)
 		const TerrainTile2 &tile = CGI->mh->ttiles[hp.x-1][hp.y][hp.z];
 		for(auto & elem : tile.objects)
-			if(elem.first->id == details.id)
-				hero = dynamic_cast<const CGHeroInstance *>(elem.first);
+			if(elem.obj->id == details.id)
+				hero = dynamic_cast<const CGHeroInstance *>(elem.obj);
 
 		if(!hero) //still nothing...
 			return;
@@ -276,7 +276,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 
 				}
 			}
-			adventureInt->centerOn(hero); //actualizing screen pos
+			adventureInt->centerOn(hero, true); //actualizing screen pos
 			adventureInt->minimap.redraw();
 			adventureInt->heroList.update(hero);
 			return;	//teleport - no fancy moving animation
@@ -1676,17 +1676,17 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns
 	{
 		//ho->moveDir = 1;
 		ho->isStanding = false;
-		CGI->mh->ttiles[hp.x-3][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -31, -31)));
-		CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 1, -31)));
-		CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 33, -31)));
-		CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 65, -31)));
+		CGI->mh->ttiles[hp.x-3][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, -31)));
+		CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 1, -31)));
+		CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 33, -31)));
+		CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 65, -31)));
 
-		CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -31, 1)));
+		CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 1)));
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, 1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 33, 1), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 65, 1), ho->id);
 
-		CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -31, 33)));
+		CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 33)));
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, 1, 33), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 33, 33), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 65, 33), ho->id);
@@ -1704,9 +1704,9 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns
 	{
 		//ho->moveDir = 2;
 		ho->isStanding = false;
-		CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 0, -31)));
-		CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 32, -31)));
-		CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 64, -31)));
+		CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 0, -31)));
+		CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 32, -31)));
+		CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 64, -31)));
 
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 0, 1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 32, 1), ho->id);
@@ -1724,20 +1724,20 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns
 	{
 		//ho->moveDir = 3;
 		ho->isStanding = false;
-		CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -1, -31)));
-		CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 31, -31)));
-		CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 63, -31)));
-		CGI->mh->ttiles[hp.x+1][hp.y-2][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 95, -31)));
+		CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -1, -31)));
+		CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 31, -31)));
+		CGI->mh->ttiles[hp.x][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 63, -31)));
+		CGI->mh->ttiles[hp.x+1][hp.y-2][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, -31)));
 
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, 1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, 1), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 63, 1), ho->id);
-		CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 95, 1)));
+		CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 1)));
 
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, -1, 33), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 31, 33), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 63, 33), ho->id);
-		CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 95, 33)));
+		CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 33)));
 
 		std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
 		std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y-2][hp.z].objects.end(), objectBlitOrderSorter);
@@ -1755,12 +1755,12 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, 0), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, 0), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 63, 0), ho->id);
-		CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 95, 0)));
+		CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 0)));
 
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, -1, 32), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 31, 32), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 63, 32), ho->id);
-		CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 95, 32)));
+		CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 32)));
 
 		std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter);
 
@@ -1773,17 +1773,17 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, -1, -1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 31, -1), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 63, -1), ho->id);
-		CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 95, -1)));
+		CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, -1)));
 
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, -1, 31), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 31, 31), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 63, 31), ho->id);
-		CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 95, 31)));
+		CGI->mh->ttiles[hp.x+1][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 31)));
 
-		CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -1, 63)));
-		CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 31, 63)));
-		CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 63, 63)));
-		CGI->mh->ttiles[hp.x+1][hp.y+1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 95, 63)));
+		CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -1, 63)));
+		CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 31, 63)));
+		CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 63, 63)));
+		CGI->mh->ttiles[hp.x+1][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 95, 63)));
 
 		std::stable_sort(CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x+1][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter);
 
@@ -1806,9 +1806,9 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 32, 31), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 64, 31), ho->id);
 
-		CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 0, 63)));
-		CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 32, 63)));
-		CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 64, 63)));
+		CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 0, 63)));
+		CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 32, 63)));
+		CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 64, 63)));
 
 		std::stable_sort(CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
 		std::stable_sort(CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.end(), objectBlitOrderSorter);
@@ -1818,20 +1818,20 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns
 	{
 		//ho->moveDir = 7;
 		ho->isStanding = false;
-		CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -31, -1)));
+		CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, -1)));
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, -1), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 33, -1), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 65, -1), ho->id);
 
-		CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -31, 31)));
+		CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 31)));
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, 1, 31), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 33, 31), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 65, 31), ho->id);
 
-		CGI->mh->ttiles[hp.x-3][hp.y+1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -31, 63)));
-		CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 1, 63)));
-		CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 33, 63)));
-		CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, 65, 63)));
+		CGI->mh->ttiles[hp.x-3][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 63)));
+		CGI->mh->ttiles[hp.x-2][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 1, 63)));
+		CGI->mh->ttiles[hp.x-1][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 33, 63)));
+		CGI->mh->ttiles[hp.x][hp.y+1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, 65, 63)));
 
 		std::stable_sort(CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.begin(), CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.end(), objectBlitOrderSorter);
 
@@ -1846,12 +1846,12 @@ void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroIns
 	{
 		//ho->moveDir = 8;
 		ho->isStanding = false;
-		CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -31, 0)));
+		CGI->mh->ttiles[hp.x-3][hp.y-1][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 0)));
 		subRect(hp.x-2, hp.y-1, hp.z, genRect(32, 32, 1, 0), ho->id);
 		subRect(hp.x-1, hp.y-1, hp.z, genRect(32, 32, 33, 0), ho->id);
 		subRect(hp.x, hp.y-1, hp.z, genRect(32, 32, 65, 0), ho->id);
 
-		CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.push_back(std::make_pair(ho, genRect(32, 32, -31, 32)));
+		CGI->mh->ttiles[hp.x-3][hp.y][hp.z].objects.push_back(TerrainTileObject(ho, genRect(32, 32, -31, 32)));
 		subRect(hp.x-2, hp.y, hp.z, genRect(32, 32, 1, 32), ho->id);
 		subRect(hp.x-1, hp.y, hp.z, genRect(32, 32, 33, 32), ho->id);
 		subRect(hp.x, hp.y, hp.z, genRect(32, 32, 65, 32), ho->id);

+ 4 - 4
client/NetPacksClient.cpp

@@ -336,7 +336,7 @@ void RemoveObject::applyFirstCl( CClient *cl )
 {
 	const CGObjectInstance *o = cl->getObj(id);
 
-	CGI->mh->hideObject(o);
+	CGI->mh->hideObject(o, true);
 
 	//notify interfaces about removal
 	for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++)
@@ -367,7 +367,7 @@ void TryMoveHero::applyFirstCl( CClient *cl )
 	}
 
 	if(result == TELEPORTATION  ||  result == EMBARK  ||  result == DISEMBARK  ||  !humanKnows)
-		CGI->mh->removeObject(h);
+		CGI->mh->removeObject(h, result == EMBARK && humanKnows);
 
 
 	if(result == DISEMBARK)
@@ -381,7 +381,7 @@ void TryMoveHero::applyCl( CClient *cl )
 
 	if(result == TELEPORTATION  ||  result == EMBARK  ||  result == DISEMBARK)
 	{
-		CGI->mh->printObject(h);
+		CGI->mh->printObject(h, result == DISEMBARK);
 	}
 
 	if(result == EMBARK)
@@ -908,7 +908,7 @@ void NewObject::applyCl(CClient *cl)
 	cl->invalidatePaths();
 
 	const CGObjectInstance *obj = cl->getObj(id);
-	CGI->mh->printObject(obj);
+	CGI->mh->printObject(obj, true);
 
 	for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++)
 	{

+ 88 - 0
client/gui/CAnimation.cpp

@@ -1222,3 +1222,91 @@ void CAnimation::getAnimInfo()
             logGlobal->errorStream()<<", "<<anim->images.begin()->second.size()<<" image loaded in group "<< anim->images.begin()->first;
 	}
 }
+
+
+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()
+	: fadingSurface(nullptr),
+	  fading(false),
+	  fadingMode(EMode::NONE)
+{
+}
+
+CFadeAnimation::~CFadeAnimation()
+{
+	if (fadingSurface && shouldFreeSurface)
+		SDL_FreeSurface(fadingSurface);		
+}
+
+void CFadeAnimation::init(EMode mode, SDL_Surface * sourceSurface, bool freeSurfaceAtEnd /* = false */, float animDelta /* = DEFAULT_DELTA */)
+{
+	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->warnStream() << "Tried to init fading animation that is already running.";
+		if (fadingSurface && shouldFreeSurface)
+			SDL_FreeSurface(fadingSurface); 
+	}		
+	if (animDelta <= 0.0f)
+	{
+		logGlobal->warnStream() << "Fade anim: delta should be positive; " << animDelta << " given.";
+		animDelta = DEFAULT_DELTA;
+	}
+	
+	if (sourceSurface)
+		fadingSurface = sourceSurface;
+	
+	delta = animDelta;
+	fadingMode = mode;
+	fadingCounter = initialCounter();
+	fading = true;
+	shouldFreeSurface = freeSurfaceAtEnd;
+}
+
+void CFadeAnimation::draw(SDL_Surface * targetSurface, const SDL_Rect * sourceRect, SDL_Rect * destRect)
+{	
+	if (!fading || !fadingSurface || fadingMode == EMode::NONE)
+	{
+		fading = false;
+		return;
+	}
+	
+	SDL_SetSurfaceAlphaMod(fadingSurface, fadingCounter * 255);
+	SDL_BlitSurface(fadingSurface, sourceRect, targetSurface, destRect);
+	SDL_SetSurfaceAlphaMod(fadingSurface, 255);
+}

+ 28 - 0
client/gui/CAnimation.h

@@ -220,3 +220,31 @@ public:
 	//total count of frames in group (including not loaded)
 	size_t size(size_t group=0) const;
 };
+
+class CFadeAnimation
+{
+public:
+	enum class EMode
+	{
+		NONE, IN, OUT
+	};
+private:
+	static constexpr float DEFAULT_DELTA = 0.05f;
+	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 SDL_Rect * sourceRect, SDL_Rect * destRect);
+	bool isFading() const { return fading; }
+};

+ 261 - 143
client/mapHandler.cpp

@@ -12,6 +12,7 @@
 #include "mapHandler.h"
 
 #include "CBitmapHandler.h"
+#include "gui/CAnimation.h"
 #include "gui/SDL_Extensions.h"
 #include "CGameInfo.h"
 #include "../lib/mapObjects/CGHeroInstance.h"
@@ -75,9 +76,9 @@ std::string nameFromType (int typ)
 	return std::string();
 }
 
-static bool objectBlitOrderSorter(const std::pair<const CGObjectInstance*,SDL_Rect>  & a, const std::pair<const CGObjectInstance*,SDL_Rect> & b)
+static bool objectBlitOrderSorter(const TerrainTileObject & a, const TerrainTileObject & b)
 {
-	return CMapHandler::compareObjectBlitOrder(a.first, b.first);
+	return CMapHandler::compareObjectBlitOrder(a.obj, b.obj);
 }
 
 struct NeighborTilesInfo
@@ -185,10 +186,12 @@ void CMapHandler::prepareFOWDefs()
 	}
 }
 
-void CMapHandler::drawTerrainRectNew(SDL_Surface * targetSurface, const MapDrawingInfo * info)
+EMapAnimRedrawStatus CMapHandler::drawTerrainRectNew(SDL_Surface * targetSurface, const MapDrawingInfo * info, bool redrawOnlyAnim /* = false */)
 {
 	assert(info);
+	bool hasActiveFade = updateObjectsFade();
 	resolveBlitter(info)->blit(targetSurface, info);
+	return hasActiveFade ? EMapAnimRedrawStatus::REDRAW_REQUESTED : EMapAnimRedrawStatus::OK;
 }
 
 void CMapHandler::roadsRiverTerrainInit()
@@ -346,7 +349,7 @@ void CMapHandler::initObjectRects()
 				cr.h = 32;
 				cr.x = bitmap->w - fx * 32 - 32;
 				cr.y = bitmap->h - fy * 32 - 32;
-				std::pair<const CGObjectInstance*,SDL_Rect> toAdd = std::make_pair(obj,cr);
+				TerrainTileObject toAdd(obj,cr);
 
 
 				if( map->isInTheMap(currTile) && // within map
@@ -609,12 +612,12 @@ void CMapHandler::CMapWorldViewBlitter::drawTileOverlay(SDL_Surface * targetSurf
 	auto & objects = tile.objects;
 	for(auto & object : objects)
 	{
-		const CGObjectInstance * obj = object.first;
+		const CGObjectInstance * obj = object.obj;
 
 		if (obj->pos.z != pos.z)
 			continue;
 		if (!(*info->visibilityMap)[pos.x][pos.y][pos.z])
-			continue; // TODO needs to skip this check if we have artifacts-aura-like spell cast
+			continue; // TODO needs to skip this check if we have view-air-like spell cast
 		if (!obj->visitableAt(pos.x, pos.y))
 			continue;
 
@@ -668,12 +671,6 @@ void CMapHandler::CMapWorldViewBlitter::drawTileOverlay(SDL_Surface * targetSurf
 	}
 }
 
-void CMapHandler::CMapWorldViewBlitter::drawNormalObject(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect) const
-{
-	Rect scaledSourceRect(sourceRect->x * info->scale, sourceRect->y * info->scale, tileSize, tileSize);
-	CMapBlitter::drawNormalObject(targetSurf, sourceSurf, &scaledSourceRect);
-}
-
 void CMapHandler::CMapWorldViewBlitter::drawHeroFlag(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, SDL_Rect * destRect, bool moving) const
 {
 	if (moving)
@@ -682,13 +679,13 @@ void CMapHandler::CMapWorldViewBlitter::drawHeroFlag(SDL_Surface * targetSurf, S
 	CMapBlitter::drawHeroFlag(targetSurf, sourceSurf, sourceRect, destRect, false);
 }
 
-void CMapHandler::CMapWorldViewBlitter::drawHero(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, bool moving) const
+void CMapHandler::CMapWorldViewBlitter::drawObject(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, bool moving) const
 {
 	if (moving)
 		return;
 
 	Rect scaledSourceRect(sourceRect->x * info->scale, sourceRect->y * info->scale, sourceRect->w, sourceRect->h);
-	CMapBlitter::drawHero(targetSurf, sourceSurf, &scaledSourceRect, false);
+	CMapBlitter::drawObject(targetSurf, sourceSurf, &scaledSourceRect, false);
 }
 
 void CMapHandler::CMapBlitter::drawTileTerrain(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile2 & tile) const
@@ -788,21 +785,15 @@ void CMapHandler::CMapBlitter::drawFrame(SDL_Surface * targetSurf) const
 	drawElement(EMapCacheType::FRAME, parent->ttiles[pos.x][pos.y][topTile.z].terbitmap, nullptr, targetSurf, &destRect);
 }
 
-void CMapHandler::CMapBlitter::drawNormalObject(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect) const
-{
-	Rect destRect(realTileRect);
-	drawElement(EMapCacheType::OBJECTS, sourceSurf, sourceRect, targetSurf, &destRect, true);
-}
-
 void CMapHandler::CMapBlitter::drawHeroFlag(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, SDL_Rect * destRect, bool moving) const
 {
 	drawElement(EMapCacheType::HERO_FLAGS, sourceSurf, sourceRect, targetSurf, destRect, false);
 }
 
-void CMapHandler::CMapBlitter::drawHero(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, bool moving) const
+void CMapHandler::CMapBlitter::drawObject(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, bool moving) const
 {
 	Rect dstRect(realTileRect);
-	drawElement(EMapCacheType::HEROES, sourceSurf, sourceRect, targetSurf, &dstRect, true);
+	drawElement(EMapCacheType::OBJECTS, sourceSurf, sourceRect, targetSurf, &dstRect, true);
 }
 
 void CMapHandler::CMapBlitter::drawObjects(SDL_Surface * targetSurf, const TerrainTile2 & tile) const
@@ -810,7 +801,20 @@ void CMapHandler::CMapBlitter::drawObjects(SDL_Surface * targetSurf, const Terra
 	auto & objects = tile.objects;
 	for(auto & object : objects)
 	{
-		const CGObjectInstance * obj = object.first;
+		if (object.fadeAnimKey >= 0)
+		{			
+			auto fadeIter = parent->fadeAnims.find(object.fadeAnimKey);
+			if (fadeIter != parent->fadeAnims.end())
+			{
+				// this object is currently fading, so skip normal drawing
+				Rect r2(realTileRect);
+				CFadeAnimation * fade = (*fadeIter).second.second;
+				fade->draw(targetSurf, nullptr, &r2);
+				continue;
+			}
+		}
+		
+		const CGObjectInstance * obj = object.obj;
 		if (!graphics->getDef(obj))
 			processDef(obj->appearance);
 		if (!graphics->getDef(obj) && !obj->appearance.animationFile.empty())
@@ -818,122 +822,28 @@ void CMapHandler::CMapBlitter::drawObjects(SDL_Surface * targetSurf, const Terra
 
 		if (!canDrawObject(obj))
 			continue;
-
-		PlayerColor color = obj->tempOwner;
-
-		SDL_Rect pp = object.second;
-		pp.h = tileSize;
-		pp.w = tileSize;
-
-		const CGHeroInstance * hero = (obj->ID != Obj::HERO
-			? nullptr
-			: static_cast<const CGHeroInstance*>(obj));
-
-		//print hero / boat and flag
-		if((hero && hero->moveDir && hero->type) || (obj->ID == Obj::BOAT)) //it's hero or boat
-		{
-			const int IMGVAL = 8; //frames per group of movement animation
-			ui8 dir;
-			std::vector<Cimage> * iv = nullptr;
-			std::vector<CDefEssential *> Graphics::*flg = nullptr;
-			SDL_Surface * tb = nullptr; //surface to blitted
-
-			if(hero) //hero
+		
+		auto objData = findObjectBitmap(obj, info->anim);
+		if (objData.objBitmap)
+		{			
+			Rect srcRect(object.rect.x, object.rect.y, tileSize, tileSize);
+			
+			drawObject(targetSurf, objData.objBitmap, &srcRect, objData.isMoving);
+			if (objData.flagBitmap)
 			{
-				if(hero->tempOwner >= PlayerColor::PLAYER_LIMIT) //Neutral hero?
-				{
-					logGlobal->errorStream() << "A neutral hero (" << hero->name << ") at " << hero->pos << ". Should not happen!";
-					continue;
-				}
-
-				dir = hero->moveDir;
-
-				//pick graphics of hero (or boat if hero is sailing)
-				if (hero->boat)
-					iv = &graphics->boatAnims[hero->boat->subID]->ourImages;
-				else
-					iv = &graphics->heroAnims[hero->appearance.animationFile]->ourImages;
-
-				//pick appropriate flag set
-				if(hero->boat)
-				{
-					switch (hero->boat->subID)
-					{
-						case 0: flg = &Graphics::flags1; break;
-						case 1: flg = &Graphics::flags2; break;
-						case 2: flg = &Graphics::flags3; break;
-						default: logGlobal->errorStream() << "Not supported boat subtype: " << hero->boat->subID;
-					}
-				}
-				else
+				if (objData.isMoving)
 				{
-					flg = &Graphics::flags4;
+					srcRect.y += FRAMES_PER_MOVE_ANIM_GROUP * 2 - tileSize;
+					Rect dstRect(realPos.x, realPos.y - tileSize / 2, tileSize, tileSize);
+					drawHeroFlag(targetSurf, objData.flagBitmap, &srcRect, &dstRect, true);
 				}
-			}
-			else //boat
-			{
-				const CGBoat *boat = static_cast<const CGBoat*>(obj);
-				dir = boat->direction;
-				iv = &graphics->boatAnims[boat->subID]->ourImages;
-			}
-
-			if(hero && !hero->isStanding) //hero is moving
-			{
-				size_t gg;
-				for(gg=0; gg<iv->size(); ++gg)
-				{
-					if((*iv)[gg].groupNumber == getHeroFrameNum(dir, true))
-					{
-						tb = (*iv)[gg+info->getHeroAnim()%IMGVAL].bitmap;
-						break;
-					}
-				}
-				drawHero(targetSurf, tb, &pp, true);
-
-				pp.y += IMGVAL * 2 - tileSize;
-				Rect destRect(realPos.x, realPos.y - tileSize / 2, tileSize, tileSize);
-				drawHeroFlag(targetSurf, (graphics->*flg)[color.getNum()]->ourImages[gg + info->getHeroAnim() % IMGVAL + 35].bitmap, &pp, &destRect, true);
-
-			}
-			else //hero / boat stands still
-			{
-				size_t gg;
-				for(gg=0; gg < iv->size(); ++gg)
-				{
-					if((*iv)[gg].groupNumber == getHeroFrameNum(dir, false))
-					{
-						tb = (*iv)[gg].bitmap;
-						break;
-					}
-				}
-				drawHero(targetSurf, tb, &pp, false);
-
-				//printing flag
-				if(flg
-					&&  obj->pos.x == pos.x
-					&&  obj->pos.y == pos.y)
+				else if (obj->pos.x == pos.x && obj->pos.y == pos.y)
 				{
 					Rect dstRect(realPos.x - 2 * tileSize, realPos.y - tileSize, 3 * tileSize, 2 * tileSize);
-					if (dstRect.x - info->drawBounds->x > -tileSize * 2)
-					{
-						auto surf = (graphics->*flg)[color.getNum()]->ourImages
-								[getHeroFrameNum(dir, false) * 8 + (info->getHeroAnim() / 4) % IMGVAL].bitmap;
-						drawHeroFlag(targetSurf, surf, nullptr, &dstRect, false);
-					}
+					drawHeroFlag(targetSurf, objData.flagBitmap, nullptr, &dstRect, false);					
 				}
 			}
 		}
-		else //blit normal object
-		{
-			const std::vector<Cimage> &ourImages = graphics->getDef(obj)->ourImages;
-			SDL_Surface *bitmap = ourImages[(info->anim + getPhaseShift(obj)) % ourImages.size()].bitmap;
-
-			//setting appropriate flag color
-			if(color < PlayerColor::PLAYER_LIMIT || color==PlayerColor::NEUTRAL)
-				CSDL_Ext::setPlayerColor(bitmap, color);
-
-			drawNormalObject(targetSurf, bitmap, &pp);
-		}
 	}
 }
 
@@ -1086,6 +996,109 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
 	SDL_SetClipRect(targetSurf, &prevClip);
 }
 
+CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findHeroBitmap(const CGHeroInstance * hero, int anim) const
+{
+	if(hero && hero->moveDir && hero->type) //it's hero or boat
+	{
+		if(hero->tempOwner >= PlayerColor::PLAYER_LIMIT) //Neutral hero?
+		{
+			logGlobal->errorStream() << "A neutral hero (" << hero->name << ") at " << hero->pos << ". Should not happen!";
+			return CMapHandler::AnimBitmapHolder();
+		}
+
+		//pick graphics of hero (or boat if hero is sailing)
+		CDefEssential * def = nullptr;
+		if (hero->boat)
+			def = graphics->boatAnims[hero->boat->subID];
+		else
+			def = graphics->heroAnims[hero->appearance.animationFile];
+
+		bool moving = !hero->isStanding;
+		int framesOffset = moving ? anim % FRAMES_PER_MOVE_ANIM_GROUP : 0;
+		int index = findAnimIndexByGroup(def, getHeroFrameNum(hero->moveDir, moving));
+		if (index >= 0)
+		{
+			auto heroBitmap = def->ourImages[index + framesOffset].bitmap;
+			auto flagBitmap = findFlagBitmap(hero, anim, &hero->tempOwner, index + 35);
+			return CMapHandler::AnimBitmapHolder(heroBitmap, flagBitmap, moving);
+		}
+	}
+	return CMapHandler::AnimBitmapHolder();
+}
+
+CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findBoatBitmap(const CGBoat * boat, int anim) const
+{
+	auto def = graphics->boatAnims[boat->subID];
+	int index = findAnimIndexByGroup(def, getHeroFrameNum(boat->direction, false));
+	if (index < 0)
+		return CMapHandler::AnimBitmapHolder();
+	return CMapHandler::AnimBitmapHolder(def->ourImages[index].bitmap);
+}
+
+SDL_Surface * CMapHandler::CMapBlitter::findFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor * color, int indexOffset) const
+{	
+	if (!hero)
+		return nullptr;
+	
+	if (hero->boat)
+		return findBoatFlagBitmap(hero->boat, anim, color, indexOffset, hero->moveDir);
+	return findHeroFlagBitmap(hero, anim, color, indexOffset);
+}
+
+SDL_Surface * CMapHandler::CMapBlitter::findHeroFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor * color, int indexOffset) const
+{
+	return findFlagBitmapInternal(graphics->flags4[color->getNum()], anim, indexOffset, hero->moveDir, !hero->isStanding);
+}
+
+SDL_Surface * CMapHandler::CMapBlitter::findBoatFlagBitmap(const CGBoat * boat, int anim, const PlayerColor * color, int indexOffset, ui8 dir) const
+{
+	std::vector<CDefEssential *> Graphics::*flg = nullptr;
+	switch (boat->subID)
+	{
+		case 0: flg = &Graphics::flags1; break;
+		case 1: flg = &Graphics::flags2; break;
+		case 2: flg = &Graphics::flags3; break;
+		default: logGlobal->errorStream() << "Not supported boat subtype: " << boat->subID; return nullptr;
+	}
+	return findFlagBitmapInternal((graphics->*flg)[color->getNum()], anim, indexOffset, dir, false);
+}
+
+SDL_Surface * CMapHandler::CMapBlitter::findFlagBitmapInternal(const CDefEssential * def, int anim, int indexOffset, ui8 dir, bool moving) const
+{
+	if (moving)
+		return def->ourImages[indexOffset + anim % FRAMES_PER_MOVE_ANIM_GROUP].bitmap;	
+	return def->ourImages[getHeroFrameNum(dir, false) * FRAMES_PER_MOVE_ANIM_GROUP + (anim / 4) % FRAMES_PER_MOVE_ANIM_GROUP].bitmap;	
+}
+
+int CMapHandler::CMapBlitter::findAnimIndexByGroup(const CDefEssential * def, int groupNum) const
+{
+	auto iter = std::find_if(def->ourImages.begin(), def->ourImages.end(), [&](const Cimage &img){ return img.groupNumber == groupNum; });
+	if (iter == def->ourImages.end())
+		return -1;
+	return static_cast<int>(iter - def->ourImages.begin());
+}
+
+CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findObjectBitmap(const CGObjectInstance * obj, int anim) const
+{
+	if (!obj)
+		return CMapHandler::AnimBitmapHolder();
+	if (obj->ID == Obj::HERO)
+		return findHeroBitmap(static_cast<const CGHeroInstance*>(obj), anim);
+	if (obj->ID == Obj::BOAT)
+		return findBoatBitmap(static_cast<const CGBoat*>(obj), anim);
+	
+	// normal object	
+	
+	const std::vector<Cimage> &ourImages = graphics->getDef(obj)->ourImages;
+	SDL_Surface *bitmap = ourImages[(anim + getPhaseShift(obj)) % ourImages.size()].bitmap;
+
+	//setting appropriate flag color
+	const PlayerColor &color = obj->tempOwner;
+	if(color < PlayerColor::PLAYER_LIMIT || color==PlayerColor::NEUTRAL)
+		CSDL_Ext::setPlayerColor(bitmap, color);
+	return CMapHandler::AnimBitmapHolder(bitmap);
+}
+
 ui8 CMapHandler::CMapBlitter::getPhaseShift(const CGObjectInstance *object) const
 {
 	auto i = parent->animationPhase.find(object);
@@ -1145,12 +1158,79 @@ std::pair<SDL_Surface *, bool> CMapHandler::CMapBlitter::getVisBitmap() const
 	}
 }
 
-bool CMapHandler::printObject(const CGObjectInstance *obj)
+bool CMapHandler::updateObjectsFade()
+{	
+	for (auto iter = fadeAnims.begin(); iter != fadeAnims.end(); )
+	{
+		int3 pos = (*iter).second.first;
+		CFadeAnimation * anim = (*iter).second.second;
+		
+		anim->update();
+		
+		if (anim->isFading())
+			++iter;
+		else // fade finished
+		{
+			auto &objs = ttiles[pos.x][pos.y][pos.z].objects;
+			for (auto objIter = objs.begin(); objIter != objs.end(); ++objIter)
+			{
+				if ((*objIter).fadeAnimKey == (*iter).first)
+				{						
+					if (anim->fadingMode == CFadeAnimation::EMode::OUT)
+						objs.erase(objIter); // if this was fadeout, remove the object from the map
+					else
+						(*objIter).fadeAnimKey = -1; // for fadein, just remove its connection to the finished fade
+					break;
+				}
+			}
+			iter = fadeAnims.erase(iter);
+			logAnim->traceStream() << "Fade anim finished, remaining: " << fadeAnims.size();
+		}
+	}
+	
+	return !fadeAnims.empty();
+}
+
+bool CMapHandler::startObjectFade(TerrainTileObject & obj, bool in, int3 pos)
+{
+	SDL_Surface * fadeBitmap;
+	
+	auto objData = normalBlitter->findObjectBitmap(obj.obj, 0);
+	if (objData.objBitmap)
+	{
+		if (objData.isMoving) // ignore fading of moving objects (for now?)
+		{
+			logAnim->debugStream() << "Ignoring fade of moving object";
+			return false;
+		}
+				
+		fadeBitmap = CSDL_Ext::newSurface(32, 32); // TODO cache these bitmaps instead of creating new ones?
+		Rect objSrcRect(obj.rect.x, obj.rect.y, 32, 32);
+		CSDL_Ext::blit8bppAlphaTo24bpp(objData.objBitmap, &objSrcRect, fadeBitmap, nullptr);
+		if (objData.flagBitmap)
+		{
+			if (obj.obj->pos.x - 1 == pos.x && obj.obj->pos.y - 1 == pos.y) // -1 to draw flag in top-center instead of right-bottom; kind of a hack
+			{
+				Rect flagSrcRect(32, 0, 32, 32);
+				CSDL_Ext::blitSurface(objData.flagBitmap, &flagSrcRect, fadeBitmap, nullptr);
+			}
+		}
+		auto anim = new CFadeAnimation();
+		anim->init(in ? CFadeAnimation::EMode::IN : CFadeAnimation::EMode::OUT, fadeBitmap, true);
+		fadeAnims[++fadeAnimCounter] = std::pair<int3, CFadeAnimation*>(pos, anim);
+		obj.fadeAnimKey = fadeAnimCounter;
+		return true;
+	}
+	
+	return false;
+}
+
+bool CMapHandler::printObject(const CGObjectInstance *obj, bool fadein /* = false */)
 {
 	if (!graphics->getDef(obj))
 		processDef(obj->appearance);
 
-	const SDL_Surface *bitmap = graphics->getDef(obj)->ourImages[0].bitmap;
+	SDL_Surface *bitmap = graphics->getDef(obj)->ourImages[0].bitmap;
 	const int tilesW = bitmap->w/32;
 	const int tilesH = bitmap->h/32;
 
@@ -1163,11 +1243,18 @@ bool CMapHandler::printObject(const CGObjectInstance *obj)
 			cr.h = 32;
 			cr.x = fx*32;
 			cr.y = fy*32;
-			std::pair<const CGObjectInstance*,SDL_Rect> toAdd = std::make_pair(obj, cr);
+			TerrainTileObject toAdd(obj, cr);
+			
 			if((obj->pos.x + fx - tilesW+1)>=0 && (obj->pos.x + fx - tilesW+1)<ttiles.size()-frameW && (obj->pos.y + fy - tilesH+1)>=0 && (obj->pos.y + fy - tilesH+1)<ttiles[0].size()-frameH)
 			{
-				TerrainTile2 & curt = ttiles[obj->pos.x + fx - tilesW+1][obj->pos.y + fy - tilesH+1][obj->pos.z];
-
+				int3 pos(obj->pos.x + fx - tilesW + 1, obj->pos.y + fy - tilesH + 1, obj->pos.z);
+				TerrainTile2 & curt = ttiles[pos.x][pos.y][pos.z];					
+				
+				if (fadein && ADVOPT.objectFading)
+				{
+					startObjectFade(toAdd, true, pos);
+				}
+				
 				auto i = curt.objects.begin();
 				for(; i != curt.objects.end(); i++)
 				{
@@ -1188,8 +1275,9 @@ bool CMapHandler::printObject(const CGObjectInstance *obj)
 	return true;
 }
 
-bool CMapHandler::hideObject(const CGObjectInstance *obj)
+bool CMapHandler::hideObject(const CGObjectInstance *obj, bool fadeout /* = false */)
 {
+	// do we actually need to search through the whole map for this?
 	for (size_t i=0; i<map->width; i++)
 	{
 		for (size_t j=0; j<map->height; j++)
@@ -1198,9 +1286,15 @@ bool CMapHandler::hideObject(const CGObjectInstance *obj)
 			{
 				for(size_t x=0; x < ttiles[i][j][k].objects.size(); x++)
 				{
-					if (ttiles[i][j][k].objects[x].first->id == obj->id)
+					if (ttiles[i][j][k].objects[x].obj->id == obj->id)
 					{
-						ttiles[i][j][k].objects.erase(ttiles[i][j][k].objects.begin() + x);
+						if (fadeout && ADVOPT.objectFading) // object should be faded == erase is delayed until the end of fadeout
+						{
+							if (!startObjectFade(ttiles[i][j][k].objects[x], false, int3(i, j, k)))
+								ttiles[i][j][k].objects.erase(ttiles[i][j][k].objects.begin() + x);
+						}
+						else
+							ttiles[i][j][k].objects.erase(ttiles[i][j][k].objects.begin() + x);
 						break;
 					}
 				}
@@ -1209,9 +1303,9 @@ bool CMapHandler::hideObject(const CGObjectInstance *obj)
 	}
 	return true;
 }
-bool CMapHandler::removeObject(CGObjectInstance *obj)
+bool CMapHandler::removeObject(CGObjectInstance *obj, bool fadeout /* = false */)
 {
-	hideObject(obj);
+	hideObject(obj, fadeout);
 	return true;
 }
 
@@ -1286,6 +1380,11 @@ ui8 CMapHandler::getDir(const int3 &a, const int3 &b)
 	return -2; //shouldn't happen
 }
 
+bool CMapHandler::canStartHeroMovement()
+{
+	return fadeAnims.empty(); // don't allow movement during fade animation
+}
+
 void shiftColors(SDL_Surface *img, int from, int howMany) //shifts colors in palette
 {
 	//works with at most 16 colors, if needed more -> increase values
@@ -1331,6 +1430,8 @@ CMapHandler::~CMapHandler()
 {
 	delete graphics->FoWfullHide;
 	delete graphics->FoWpartialHide;
+//	if (fadingOffscreenBitmapSurface)
+//		delete fadingOffscreenBitmapSurface;
 
 	delete normalBlitter;
 	delete worldViewBlitter;
@@ -1347,6 +1448,11 @@ CMapHandler::~CMapHandler()
 		for(int j=0; j < elem.size(); ++j)
 			SDL_FreeSurface(elem[j]);
 	}
+	
+	for (auto & elem : fadeAnims)
+	{
+		delete elem.second.second;
+	}
 	terrainGraphics.clear();
 }
 
@@ -1358,6 +1464,7 @@ CMapHandler::CMapHandler()
 	normalBlitter = new CMapNormalBlitter(this);
 	worldViewBlitter = new CMapWorldViewBlitter(this);
 	puzzleViewBlitter = new CMapPuzzleViewBlitter(this);
+	fadeAnimCounter = 0;
 }
 
 void CMapHandler::getTerrainDescr( const int3 &pos, std::string & out, bool terName )
@@ -1367,9 +1474,9 @@ void CMapHandler::getTerrainDescr( const int3 &pos, std::string & out, bool terN
 	const TerrainTile &t = map->getTile(pos);
 	for(auto & elem : tt.objects)
 	{
-		if(elem.first->ID == Obj::HOLE) //Hole
+		if(elem.obj->ID == Obj::HOLE) //Hole
 		{
-			out = elem.first->getObjectName();
+			out = elem.obj->getObjectName();
 			return;
 		}
 	}
@@ -1480,3 +1587,14 @@ bool CMapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObj
 	return false;
 }
 
+TerrainTileObject::TerrainTileObject(const CGObjectInstance * obj_, SDL_Rect rect_)
+	: obj(obj_),
+	  rect(rect_),
+	  fadeAnimKey(-1)
+{
+}
+
+TerrainTileObject::~TerrainTileObject()
+{
+}
+

+ 69 - 13
client/mapHandler.h

@@ -17,14 +17,16 @@
 
 class CGObjectInstance;
 class CGHeroInstance;
+class CGBoat;
 class CMap;
 class CGDefInfo;
-class CGObjectInstance;
 class CDefHandler;
 struct TerrainTile;
 struct SDL_Surface;
 struct SDL_Rect;
 class CDefEssential;
+class CFadeAnimation;
+class PlayerColor;
 
 enum class EWorldViewIcon
 {
@@ -50,11 +52,34 @@ enum class EWorldViewIcon
 
 };
 
+enum class EMapObjectFadingType
+{
+	NONE,
+	IN,
+	OUT
+};
+
+enum class EMapAnimRedrawStatus
+{
+	OK,
+	REDRAW_REQUESTED // map blitter requests quick redraw due to current animation
+};
+
+struct TerrainTileObject
+{
+	const CGObjectInstance *obj;
+	SDL_Rect rect;
+	int fadeAnimKey;
+	
+	TerrainTileObject(const CGObjectInstance *obj_, SDL_Rect rect_);
+	~TerrainTileObject();
+};
+
 struct TerrainTile2
 {
 	SDL_Surface * terbitmap; //bitmap of terrain
 
-	std::vector < std::pair<const CGObjectInstance*,SDL_Rect> > objects; //pointers to objects being on this tile with rects to be easier to blit this tile on screen
+	std::vector<TerrainTileObject> objects; //pointers to objects being on this tile with rects to be easier to blit this tile on screen
 	TerrainTile2();
 };
 
@@ -156,11 +181,26 @@ class CMapHandler
 		SDL_Surface * cacheWorldViewEntry(EMapCacheType type, intptr_t key, SDL_Surface * entry);
 		intptr_t genKey(intptr_t realPtr, ui8 mod);
 	};
+	
+	/// helper struct to pass around resolved bitmaps of an object; surfaces can be nullptr if object doesn't have bitmap of that type
+	struct AnimBitmapHolder
+	{
+		SDL_Surface * objBitmap; // main object bitmap
+		SDL_Surface * flagBitmap; // flag bitmap for the object (probably only for heroes and boats with heroes)
+		bool isMoving; // indicates if the object is moving (again, heroes/boats only)
+		
+		AnimBitmapHolder(SDL_Surface * objBitmap_ = nullptr, SDL_Surface * flagBitmap_ = nullptr, bool moving = false)
+			: objBitmap(objBitmap_),
+			  flagBitmap(flagBitmap_),
+			  isMoving(moving)
+		{}
+	};
 
 
 	class CMapBlitter
-	{
+	{		
 	protected:
+		static constexpr int FRAMES_PER_MOVE_ANIM_GROUP = 8;
 		CMapHandler * parent; // ptr to enclosing map handler; generally for legacy reasons, probably could/should be refactored out of here
 		int tileSize; // size of a tile drawn on map [in pixels]
 		int halfTileSizeCeil; // half of the tile size, rounded up
@@ -187,10 +227,8 @@ class CMapHandler
 		virtual void drawRoad(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile * tinfoUpper) const;
 		/// draws all objects on current tile (higher-level logic, unlike other draw*** methods)
 		virtual void drawObjects(SDL_Surface * targetSurf, const TerrainTile2 & tile) const;
-		/// current tile: draws non-hero object with given image/position
-		virtual void drawNormalObject(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect) const;
+		virtual void drawObject(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, bool moving) const;
 		virtual void drawHeroFlag(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, SDL_Rect * destRect, bool moving) const;
-		virtual void drawHero(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, bool moving) const;
 
 		// second drawing pass
 
@@ -220,10 +258,22 @@ class CMapHandler
 
 		virtual bool canDrawObject(const CGObjectInstance * obj) const;
 		virtual bool canDrawCurrentTile() const;
+		
+		// internal helper methods to choose correct bitmap(s) for object; called internally by findObjectBitmap
+		AnimBitmapHolder findHeroBitmap(const CGHeroInstance * hero, int anim) const;
+		AnimBitmapHolder findBoatBitmap(const CGBoat * hero, int anim) const;		
+		SDL_Surface * findFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int indexOffset) const;
+		SDL_Surface * findHeroFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor * color, int indexOffset) const;
+		SDL_Surface * findBoatFlagBitmap(const CGBoat * obj, int anim, const PlayerColor * color, int indexOffset, ui8 dir) const;
+		SDL_Surface * findFlagBitmapInternal(const CDefEssential * def, int anim, int indexOffset, ui8 dir, bool moving) const;
+		int findAnimIndexByGroup(const CDefEssential * def, int groupNum) const;
+		
 	public:
 		CMapBlitter(CMapHandler * p) : parent(p) {}
 		virtual ~CMapBlitter(){}
 		void blit(SDL_Surface * targetSurf, const MapDrawingInfo * info);
+		/// helper method that chooses correct bitmap(s) for given object
+		AnimBitmapHolder findObjectBitmap(const CGObjectInstance * obj, int anim) const;
 
 	};
 
@@ -248,14 +298,13 @@ class CMapHandler
 						 SDL_Surface * targetSurf, SDL_Rect * destRect, bool alphaBlit = false, ui8 rotationInfo = 0u) const override;
 
 		void drawTileOverlay(SDL_Surface * targetSurf, const TerrainTile2 & tile) const override;
-		void drawNormalObject(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect) const override;
 		void drawHeroFlag(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, SDL_Rect * destRect, bool moving) const override;
-		void drawHero(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, bool moving) const;
+		void drawObject(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, bool moving) const override;
 		void drawFrame(SDL_Surface * targetSurf) const override {}
 		void init(const MapDrawingInfo * info) override;
 		SDL_Rect clip(SDL_Surface * targetSurf) const override;
 
-		ui8 getHeroFrameNum(ui8 dir, bool isMoving) const override { return 0u; }
+//		ui8 getHeroFrameNum(ui8 dir, bool isMoving) const override { return 0u; }
 		ui8 getPhaseShift(const CGObjectInstance *object) const override { return 0u; }
 
 		void drawScaledRotatedElement(EMapCacheType type, SDL_Surface * baseSurf, SDL_Surface * targetSurf, ui8 rotation,
@@ -283,8 +332,13 @@ class CMapHandler
 	CMapBlitter * normalBlitter;
 	CMapBlitter * worldViewBlitter;
 	CMapBlitter * puzzleViewBlitter;
+	
+	std::map<int, std::pair<int3, CFadeAnimation*>> fadeAnims;
+	int fadeAnimCounter;
 
 	CMapBlitter * resolveBlitter(const MapDrawingInfo * info) const;
+	bool updateObjectsFade();
+	bool startObjectFade(TerrainTileObject & obj, bool in, int3 pos);
 public:
 	PseudoV< PseudoV< PseudoV<TerrainTile2> > > ttiles; //informations about map tiles
 	int3 sizes; //map size (x = width, y = height, z = number of levels)
@@ -320,9 +374,9 @@ public:
 
 	void getTerrainDescr(const int3 &pos, std::string & out, bool terName); //if tername == false => empty string when tile is clear
 	CGObjectInstance * createObject(int id, int subid, int3 pos, int owner=254); //creates a new object with a certain id and subid
-	bool printObject(const CGObjectInstance * obj); //puts appropriate things to ttiles, so obj will be visible on map
-	bool hideObject(const CGObjectInstance * obj); //removes appropriate things from ttiles, so obj will be no longer visible on map (but still will exist)
-	bool removeObject(CGObjectInstance * obj); //removes object from each place in VCMI (I hope)
+	bool printObject(const CGObjectInstance * obj, bool fadein = false); //puts appropriate things to ttiles, so obj will be visible on map
+	bool hideObject(const CGObjectInstance * obj, bool fadeout = false); //removes appropriate things from ttiles, so obj will be no longer visible on map (but still will exist)
+	bool removeObject(CGObjectInstance * obj, bool fadeout = false); //removes object from each place in VCMI (I hope)
 	void init();
 	void calculateBlockedPos();
 	void initObjectRects();
@@ -330,10 +384,12 @@ public:
 	void roadsRiverTerrainInit();
 	void prepareFOWDefs();
 
-	void drawTerrainRectNew(SDL_Surface * targetSurface, const MapDrawingInfo * info);
+	EMapAnimRedrawStatus drawTerrainRectNew(SDL_Surface * targetSurface, const MapDrawingInfo * info, bool redrawOnlyAnim = false);
 	void updateWater();
 	void validateRectTerr(SDL_Rect * val, const SDL_Rect * ext); //terrainRect helper
 	static ui8 getDir(const int3 & a, const int3 & b);  //returns direction number in range 0 - 7 (0 is left top, clockwise) [direction: form a to b]
+	/// determines if the map is ready to handle new hero movement (not available during fading animations)
+	bool canStartHeroMovement();
 
 	void discardWorldViewCache();
 

+ 67 - 8
client/windows/CAdvmapInterface.cpp

@@ -18,6 +18,7 @@
 #include "../Graphics.h"
 #include "../mapHandler.h"
 
+#include "../gui/CAnimation.h"
 #include "../gui/CCursorHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Extensions.h"
@@ -61,7 +62,10 @@ CAdvMapInt *adventureInt;
 
 
 CTerrainRect::CTerrainRect()
-	: curHoveredTile(-1,-1,-1), currentPath(nullptr)
+	: fadeSurface(nullptr), 
+	  fadeAnim(new CFadeAnimation()),
+	  curHoveredTile(-1,-1,-1), 
+	  currentPath(nullptr)
 {
 	tilesw=(ADVOPT.advmapW+31)/32;
 	tilesh=(ADVOPT.advmapH+31)/32;
@@ -73,6 +77,13 @@ CTerrainRect::CTerrainRect()
 	addUsedEvents(LCLICK | RCLICK | HOVER | MOVE);
 }
 
+CTerrainRect::~CTerrainRect()
+{
+	if (fadeSurface)
+		SDL_FreeSurface(fadeSurface);
+	delete fadeAnim;
+}
+
 void CTerrainRect::deactivate()
 {
 	CIntObject::deactivate();
@@ -272,8 +283,14 @@ void CTerrainRect::show(SDL_Surface * to)
 		info.heroAnim = adventureInt->heroAnim;
 		if (ADVOPT.smoothMove)
 			info.movement = int3(moveX, moveY, 0);
-
-		CGI->mh->drawTerrainRectNew(to, &info);
+		
+		lastRedrawStatus = CGI->mh->drawTerrainRectNew(to, &info);
+		if (fadeAnim->isFading())
+		{
+			Rect r(pos);
+			fadeAnim->update();
+			fadeAnim->draw(to, nullptr, &r);
+		}
 
 		if (currentPath/* && adventureInt->position.z==currentPath->startPos().z*/) //drawing path
 		{
@@ -298,6 +315,14 @@ void CTerrainRect::showAll(SDL_Surface * to)
 	}
 }
 
+void CTerrainRect::showAnim(SDL_Surface * to)
+{	
+	if (fadeAnim->isFading())
+		show(to);
+	else if (lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED)
+		show(to); // currently the same; maybe we should pass some flag to map handler so it redraws ONLY tiles that need redraw instead of full
+}
+
 int3 CTerrainRect::whichTileIsIt(const int & x, const int & y)
 {
 	int3 ret;
@@ -326,6 +351,24 @@ int3 CTerrainRect::tileCountOnScreen()
 	}
 }
 
+void CTerrainRect::fadeFromCurrentView()
+{
+	if (!ADVOPT.screenFading)
+		return;
+	if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
+		return;
+	
+	if (!fadeSurface)
+		fadeSurface = CSDL_Ext::newSurface(pos.w, pos.h);
+	SDL_BlitSurface(screen, &pos, fadeSurface, nullptr);
+	fadeAnim->init(CFadeAnimation::EMode::OUT, fadeSurface);
+}
+
+bool CTerrainRect::needsAnimUpdate()
+{
+	return fadeAnim->isFading() || lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED;
+}
+
 void CResDataBar::clickRight(tribool down, bool previousState)
 {
 }
@@ -647,7 +690,7 @@ void CAdvMapInt::fsleepWake()
 void CAdvMapInt::fmoveHero()
 {
 	const CGHeroInstance *h = curHero();
-	if (!h || !terrain.currentPath)
+	if (!h || !terrain.currentPath || !CGI->mh->canStartHeroMovement())
 		return;
 
 	LOCPLINT->moveHero(h, *terrain.currentPath);
@@ -917,6 +960,13 @@ void CAdvMapInt::show(SDL_Surface * to)
 		updateScreen=false;
 		LOCPLINT->cingconsole->showAll(to);
 	}
+	else if (terrain.needsAnimUpdate())
+	{
+		terrain.showAnim(to);
+		for(int i=0;i<4;i++)
+			blitAt(gems[i]->ourImages[LOCPLINT->playerID.getNum()].bitmap,ADVOPT.gemX[i],ADVOPT.gemY[i],to);
+	}
+	
 	infoBar.show(to);
 	statusbar.showAll(to);
 }
@@ -928,9 +978,14 @@ void CAdvMapInt::selectionChanged()
 		select(to);
 }
 
-void CAdvMapInt::centerOn(int3 on)
+void CAdvMapInt::centerOn(int3 on, bool fade /* = false */)
 {
 	bool switchedLevels = on.z != position.z;
+	
+	if (fade)
+	{
+		terrain.fadeFromCurrentView();
+	}
 
 	switch (mode)
 	{
@@ -962,9 +1017,9 @@ void CAdvMapInt::centerOn(int3 on)
 		terrain.redraw();
 }
 
-void CAdvMapInt::centerOn(const CGObjectInstance *obj)
+void CAdvMapInt::centerOn(const CGObjectInstance *obj, bool fade /* = false */)
 {
-	centerOn(obj->getSightCenter());
+	centerOn(obj->getSightCenter(), fade);
 }
 
 void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
@@ -1111,6 +1166,9 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 			#endif // VCMI_SDL1
 			if(k < 0 || k > 8)
 				return;
+			
+			if (!CGI->mh->canStartHeroMovement())
+				return;
 
 			int3 dir = directions[k];
 
@@ -1381,7 +1439,8 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
 		{
 			if (terrain.currentPath  &&  terrain.currentPath->endPos() == mapPos)//we'll be moving
 			{
-				LOCPLINT->moveHero(currentHero,*terrain.currentPath);
+				if (CGI->mh->canStartHeroMovement())
+					LOCPLINT->moveHero(currentHero,*terrain.currentPath);
 				return;
 			}
 			else/* if(mp.z == currentHero->pos.z)*/ //remove old path and find a new one if we clicked on the map level on which hero is present

+ 12 - 2
client/windows/CAdvmapInterface.h

@@ -15,6 +15,8 @@ class CGTownInstance;
 class CHeroWindow;
 class CSpell;
 class IShipyard;
+enum class EMapAnimRedrawStatus;
+class CFadeAnimation;
 
 /*****************************/
 
@@ -48,12 +50,16 @@ public:
 class CTerrainRect
 	:  public CIntObject
 {
+	SDL_Surface * fadeSurface;
+	EMapAnimRedrawStatus lastRedrawStatus;
+	CFadeAnimation * fadeAnim;
 public:
 	int tilesw, tilesh; //width and height of terrain to blit in tiles
 	int3 curHoveredTile;
 	int moveX, moveY; //shift between actual position of screen and the one we wil blit; ranges from -31 to 31 (in pixels)
 
 	CTerrainRect();
+	virtual ~CTerrainRect();
 	CGPath * currentPath;
 	void deactivate();
 	void clickLeft(tribool down, bool previousState);
@@ -62,11 +68,15 @@ public:
 	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
 	void show(SDL_Surface * to);
 	void showAll(SDL_Surface * to);
+	void showAnim(SDL_Surface * to);
 	void showPath(const SDL_Rect * extRect, SDL_Surface * to);
 	int3 whichTileIsIt(const int & x, const int & y); //x,y are cursor position
 	int3 whichTileIsIt(); //uses current cursor pos
 	/// @returns number of visible tiles on screen respecting current map scaling
 	int3 tileCountOnScreen();
+	/// animates view by caching current surface and crossfading it with normal screen
+	void fadeFromCurrentView();
+	bool needsAnimUpdate();
 };
 
 /// Resources bar which shows information about how many gold, crystals,... you have
@@ -176,8 +186,8 @@ public:
 
 	void select(const CArmedInstance *sel, bool centerView = true);
 	void selectionChanged();
-	void centerOn(int3 on);
-	void centerOn(const CGObjectInstance *obj);
+	void centerOn(int3 on, bool fade = false);
+	void centerOn(const CGObjectInstance *obj, bool fade = false);
 	int3 verifyPos(int3 ver);
 	void handleRightClick(std::string text, tribool down);
 	void keyPressed(const SDL_KeyboardEvent & key);

+ 1 - 1
config/resolutions.json

@@ -4,7 +4,7 @@
 		{
 			"resolution": { "x": 800, "y": 600 },
 			"InGameConsole": { "maxInputPerLine": 60, "maxOutputPerLine": 39 },
-			"AdvMap": { "x": 7, "y": 7, "width": 594, "height": 546, "smoothMove": 1, "puzzleSepia": 1 },
+			"AdvMap": { "x": 7, "y": 7, "width": 594, "height": 546, "smoothMove": 1, "puzzleSepia": 1, "objectFading" : 1, "screenFading" : 1 },
 			"InfoBox": { "x": 605, "y": 389 },
 			"gem0": { "x": 6, "y": 508, "graphic": "agemLL.def" },
 			"gem1": { "x": 556, "y": 508, "graphic": "agemLR.def" },

+ 2 - 0
lib/CConfigHandler.cpp

@@ -228,6 +228,8 @@ void config::CConfigHandler::init()
 		current->ac.advmapH = g["AdvMap"]["height"].Float();
 		current->ac.smoothMove = g["AdvMap"]["smoothMove"].Float();
 		current->ac.puzzleSepia = g["AdvMap"]["puzzleSepia"].Float();
+		current->ac.screenFading = g["AdvMap"]["screenFading"].isNull() ? true : g["AdvMap"]["screenFading"].Float(); // enabled by default
+		current->ac.objectFading = g["AdvMap"]["objectFading"].isNull() ? true : g["AdvMap"]["objectFading"].Float();
 
 		current->ac.infoboxX = g["InfoBox"]["x"].Float();
 		current->ac.infoboxY = g["InfoBox"]["y"].Float();

+ 2 - 0
lib/CConfigHandler.h

@@ -133,6 +133,8 @@ namespace config
 		int advmapX, advmapY, advmapW, advmapH;
 		bool smoothMove;
 		bool puzzleSepia;
+		bool screenFading;
+		bool objectFading;
 		//general properties
 		std::string mainGraphic;
 		std::string worldViewGraphic;