Переглянути джерело

Merge pull request #372 from vcmi/guiCleanup2

Gui cleanup2
ArseniyShestakov 8 роки тому
батько
коміт
d01ecbba90

+ 0 - 24
client/CDefHandler.cpp

@@ -29,12 +29,9 @@ static long long pow(long long a, int b)
 
 CDefHandler::CDefHandler()
 {
-	notFreeImgs = false;
 }
 CDefHandler::~CDefHandler()
 {
-	if (notFreeImgs)
-		return;
 	for (auto & elem : ourImages)
 	{
 		if (elem.bitmap)
@@ -44,11 +41,6 @@ CDefHandler::~CDefHandler()
 		}
 	}
 }
-CDefEssential::~CDefEssential()
-{
-	for(auto & elem : ourImages)
-		SDL_FreeSurface(elem.bitmap);
-}
 
 void CDefHandler::openFromMemory(ui8 *table, const std::string & name)
 {
@@ -350,14 +342,6 @@ SDL_Surface * CDefHandler::getSprite (int SIndex, const ui8 * FDef, const SDL_Co
 	return ret;
 }
 
-CDefEssential * CDefHandler::essentialize()
-{
-	auto ret = new CDefEssential();
-	ret->ourImages = ourImages;
-	notFreeImgs = true;
-	return ret;
-}
-
 CDefHandler * CDefHandler::giveDef(const std::string & defName)
 {
 	ResourceID resID(std::string("SPRITES/") + defName, EResType::ANIMATION);
@@ -369,12 +353,4 @@ CDefHandler * CDefHandler::giveDef(const std::string & defName)
 	nh->openFromMemory(data.get(), defName);
 	return nh;
 }
-CDefEssential * CDefHandler::giveDefEss(const std::string & defName)
-{
-	CDefEssential * ret;
-	CDefHandler * temp = giveDef(defName);
-	ret = temp->essentialize();
-	delete temp;
-	return ret;
-}
 

+ 3 - 12
client/CDefHandler.h

@@ -66,12 +66,6 @@ struct SSpriteDef
 	ui32 TopMargin;
 } PACKED_STRUCT;
 
-class CDefEssential //DefHandler with images only
-{
-public:
-	std::vector<Cimage> ourImages;
-	~CDefEssential();
-};
 
 class CDefHandler
 {
@@ -84,20 +78,17 @@ private:
 		int group;
 	} ;
 	std::vector<SEntry> SEntries ;
-	
-	void openFromMemory(ui8 * table, const std::string & name);	
+
+	void openFromMemory(ui8 * table, const std::string & name);
 	SDL_Surface * getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const;
 public:
 	int width, height; //width and height
 	std::string defName;
 	std::vector<Cimage> ourImages;
-	bool notFreeImgs;
 
 	CDefHandler();
 	~CDefHandler();
-	
-	CDefEssential * essentialize();
+
 
 	static CDefHandler * giveDef(const std::string & defName);
-	static CDefEssential * giveDefEss(const std::string & defName);
 };

+ 6 - 17
client/Graphics.cpp

@@ -232,16 +232,10 @@ std::shared_ptr<CAnimation> Graphics::loadHeroFlagAnimation(const std::string &
 
 	for(const auto & rotation : rotations)
 	{
-        const int sourceGroup = rotation.first;
-        const int targetGroup = rotation.second;
+		const int sourceGroup = rotation.first;
+		const int targetGroup = rotation.second;
 
-        for(size_t frame = 0; frame < anim->size(sourceGroup); ++frame)
-		{
-			anim->duplicateImage(sourceGroup, frame, targetGroup);
-
-			IImage * image = anim->getImage(frame, targetGroup);
-			image->verticalFlip();
-		}
+		anim->createFlippedGroup(sourceGroup, targetGroup);
 	}
 
 	return anim;
@@ -262,15 +256,10 @@ std::shared_ptr<CAnimation> Graphics::loadHeroAnimation(const std::string &name)
 
 	for(const auto & rotation : rotations)
 	{
-        const int sourceGroup = rotation.first;
-        const int targetGroup = rotation.second;
+		const int sourceGroup = rotation.first;
+		const int targetGroup = rotation.second;
 
-        for(size_t frame = 0; frame < anim->size(sourceGroup); ++frame)
-		{
-			anim->duplicateImage(sourceGroup, frame, targetGroup);
-			IImage * image = anim->getImage(frame, targetGroup);
-			image->verticalFlip();
-		}
+		anim->createFlippedGroup(sourceGroup, targetGroup);
 	}
 
 	return anim;

+ 92 - 92
client/battle/CBattleAnimations.cpp

@@ -16,11 +16,11 @@
 #include "CBattleInterface.h"
 #include "CCreatureAnimation.h"
 
-#include "../CDefHandler.h"
 #include "../CGameInfo.h"
 #include "../CMusicHandler.h"
 #include "../CPlayerInterface.h"
 #include "../Graphics.h"
+#include "../gui/CAnimation.h"
 #include "../gui/CCursorHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Extensions.h"
@@ -58,13 +58,13 @@ bool CBattleAnimation::isEarliest(bool perStackConcurrency)
 {
 	int lowestMoveID = owner->animIDhelper + 5;
 	CBattleStackAnimation * thAnim = dynamic_cast<CBattleStackAnimation *>(this);
-	CSpellEffectAnimation * thSen = dynamic_cast<CSpellEffectAnimation *>(this);
+	CEffectAnimation * thSen = dynamic_cast<CEffectAnimation *>(this);
 
 	for(auto & elem : owner->pendingAnims)
 	{
 
 		CBattleStackAnimation * stAnim = dynamic_cast<CBattleStackAnimation *>(elem.first);
-		CSpellEffectAnimation * sen = dynamic_cast<CSpellEffectAnimation *>(elem.first);
+		CEffectAnimation * sen = dynamic_cast<CEffectAnimation *>(elem.first);
 		if(perStackConcurrency && stAnim && thAnim && stAnim->stack->ID != thAnim->stack->ID)
 			continue;
 
@@ -171,7 +171,7 @@ bool CDefenceAnimation::init()
 		if(attAnim && attAnim->stack->ID != stack->ID)
 			continue;
 
-		CSpellEffectAnimation * sen = dynamic_cast<CSpellEffectAnimation *>(elem.first);
+		CEffectAnimation * sen = dynamic_cast<CEffectAnimation *>(elem.first);
 		if (sen)
 			continue;
 
@@ -243,7 +243,7 @@ CCreatureAnim::EAnimType CDefenceAnimation::getMyAnimType()
 	if(killed)
 		return CCreatureAnim::DEATH;
 
-	if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM))
+	if(vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM))
 		return CCreatureAnim::DEFENCE;
 
 	return CCreatureAnim::HITTED;
@@ -270,10 +270,15 @@ void CDefenceAnimation::nextFrame()
 
 void CDefenceAnimation::endAnim()
 {
-	if (killed)
+	if(killed)
+	{
 		myAnim->setType(CCreatureAnim::DEAD);
+	}
 	else
+	{
 		myAnim->setType(CCreatureAnim::HOLDING);
+	}
+
 
 	CBattleAnimation::endAnim();
 
@@ -785,13 +790,13 @@ bool CShootingAnimation::init()
 		spi.dx = animSpeed;
 		spi.dy = 0;
 
-		SDL_Surface * img = owner->idToProjectile[spi.creID]->ourImages[0].bitmap;
+		IImage * img = owner->idToProjectile[spi.creID]->getImage(0);
 
 		// Add explosion anim
-		Point animPos(destPos.x - 126 + img->w / 2,
-		              destPos.y - 105 + img->h / 2);
+		Point animPos(destPos.x - 126 + img->width() / 2,
+		              destPos.y - 105 + img->height() / 2);
 
-		owner->addNewAnim( new CSpellEffectAnimation(owner, catapultDamage ? "SGEXPL.DEF" : "CSGRCK.DEF", animPos.x, animPos.y));
+		owner->addNewAnim( new CEffectAnimation(owner, catapultDamage ? "SGEXPL.DEF" : "CSGRCK.DEF", animPos.x, animPos.y));
 	}
 
 	auto & angles = shooterInfo->animation.missleFrameAngles;
@@ -801,7 +806,7 @@ bool CShootingAnimation::init()
 		owner->initStackProjectile(shooter);
 
 	// only frames below maxFrame are usable: anything  higher is either no present or we don't know when it should be used
-	size_t maxFrame = std::min<size_t>(angles.size(), owner->idToProjectile.at(spi.creID)->ourImages.size());
+	size_t maxFrame = std::min<size_t>(angles.size(), owner->idToProjectile.at(spi.creID)->size(0));
 
 	assert(maxFrame > 0);
 
@@ -872,35 +877,40 @@ void CShootingAnimation::endAnim()
 	delete this;
 }
 
-CSpellEffectAnimation::CSpellEffectAnimation(CBattleInterface * _owner, ui32 _effect, BattleHex _destTile, int _dx, int _dy, bool _Vflip, bool _alignToBottom)
-	:CBattleAnimation(_owner), effect(_effect), destTile(_destTile), customAnim(""), x(-1), y(-1), dx(_dx), dy(_dy), Vflip(_Vflip), alignToBottom(_alignToBottom)
+CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx, int _dy, bool _Vflip, bool _alignToBottom)
+	: CBattleAnimation(_owner),
+	destTile(BattleHex::INVALID),
+	customAnim(_customAnim),
+	x(_x),
+	y(_y),
+	dx(_dx),
+	dy(_dy),
+	Vflip(_Vflip),
+	alignToBottom(_alignToBottom)
 {
-	logAnim->debug("Created spell anim for effect #%d", effect);
+	logAnim->debug("Created effect animation %s", customAnim);
 }
 
-CSpellEffectAnimation::CSpellEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx, int _dy, bool _Vflip, bool _alignToBottom)
-	:CBattleAnimation(_owner), effect(-1), destTile(BattleHex::INVALID), customAnim(_customAnim), x(_x), y(_y), dx(_dx), dy(_dy), Vflip(_Vflip), alignToBottom(_alignToBottom)
+CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip, bool _alignToBottom)
+	: CBattleAnimation(_owner),
+	destTile(_destTile),
+	customAnim(_customAnim),
+	x(-1),
+	y(-1),
+	dx(0),
+	dy(0),
+	Vflip(_Vflip),
+	alignToBottom(_alignToBottom)
 {
-	logAnim->debug("Created spell anim for %s", customAnim);
-}
-
-CSpellEffectAnimation::CSpellEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip, bool _alignToBottom)
-	:CBattleAnimation(_owner), effect(-1), destTile(_destTile), customAnim(_customAnim), x(-1), y(-1), dx(0), dy(0), Vflip(_Vflip), alignToBottom(_alignToBottom)
-{
-	logAnim->debug("Created spell anim for %s", customAnim);
+	logAnim->debug("Created effect animation %s", customAnim);
 }
 
 
-bool CSpellEffectAnimation::init()
+bool CEffectAnimation::init()
 {
 	if(!isEarliest(true))
 		return false;
 
-	if(customAnim.empty() && effect != ui32(-1) && !graphics->battleACToDef[effect].empty())
-	{
-		customAnim = graphics->battleACToDef[effect][0];
-	}
-
 	if(customAnim.empty())
 	{
 		endAnim();
@@ -909,97 +919,88 @@ bool CSpellEffectAnimation::init()
 
 	const bool areaEffect = (!destTile.isValid() && x == -1 && y == -1);
 
-	if(areaEffect) //f.e. armageddon
+	std::shared_ptr<CAnimation> animation = std::make_shared<CAnimation>(customAnim);
+
+	animation->preload();
+	if(Vflip)
+		animation->verticalFlip();
+
+	IImage * first = animation->getImage(0, 0, true);
+	if(!first)
 	{
-		CDefHandler * anim = CDefHandler::giveDef(customAnim);
+		endAnim();
+		return false;
+	}
 
-		for(int i=0; i * anim->width < owner->pos.w ; ++i)
+	if(areaEffect) //f.e. armageddon
+	{
+		for(int i=0; i * first->width() < owner->pos.w ; ++i)
 		{
-			for(int j=0; j * anim->height < owner->pos.h ; ++j)
+			for(int j=0; j * first->height() < owner->pos.h ; ++j)
 			{
 				BattleEffect be;
 				be.effectID = ID;
-				be.anim = CDefHandler::giveDef(customAnim);
-				if (Vflip)
-				{
-					for (auto & elem : be.anim->ourImages)
-					{
-						CSDL_Ext::VflipSurf(elem.bitmap);
-					}
-				}
+				be.animation = animation;
 				be.currentFrame = 0;
-				be.maxFrame = be.anim->ourImages.size();
-				be.x = i * anim->width + owner->pos.x;
-				be.y = j * anim->height + owner->pos.y;
+
+				be.x = i * first->width() + owner->pos.x;
+				be.y = j * first->height() + owner->pos.y;
 				be.position = BattleHex::INVALID;
 
 				owner->battleEffects.push_back(be);
 			}
 		}
-
-		delete anim;
 	}
 	else // Effects targeted at a specific creature/hex.
 	{
+		const CStack * destStack = owner->getCurrentPlayerInterface()->cb->battleGetStackByPos(destTile, false);
+		Rect & tilePos = owner->bfield[destTile]->pos;
+		BattleEffect be;
+		be.effectID = ID;
+		be.animation = animation;
+		be.currentFrame = 0;
 
-			const CStack* destStack = owner->getCurrentPlayerInterface()->cb->battleGetStackByPos(destTile, false);
-			Rect & tilePos = owner->bfield[destTile]->pos;
-			BattleEffect be;
-			be.effectID = ID;
-			be.anim = CDefHandler::giveDef(customAnim);
 
-			if (Vflip)
-			{
-				for (auto & elem : be.anim->ourImages)
-				{
-					CSDL_Ext::VflipSurf(elem.bitmap);
-				}
-			}
-
-			be.currentFrame = 0;
-			be.maxFrame = be.anim->ourImages.size();
-
-			//todo: lightning anim frame count override
+		//todo: lightning anim frame count override
 
 //			if(effect == 1)
 //				be.maxFrame = 3;
 
-			if(x == -1)
-			{
-				be.x = tilePos.x + tilePos.w/2 - be.anim->width/2;
-			}
-			else
-			{
-				be.x = x;
-			}
+		if(x == -1)
+		{
+			be.x = tilePos.x + tilePos.w/2 - first->width()/2;
+		}
+		else
+		{
+			be.x = x;
+		}
 
-			if(y == -1)
-			{
-				if(alignToBottom)
-					be.y = tilePos.y + tilePos.h - be.anim->height;
-				else
-					be.y = tilePos.y - be.anim->height/2;
-			}
+		if(y == -1)
+		{
+			if(alignToBottom)
+				be.y = tilePos.y + tilePos.h - first->height();
 			else
-			{
-				be.y = y;
-			}
-
-			// Correction for 2-hex creatures.
-			if (destStack != nullptr && destStack->doubleWide())
-				be.x += (destStack->side == BattleSide::ATTACKER ? -1 : 1)*tilePos.w/2;
+				be.y = tilePos.y - first->height()/2;
+		}
+		else
+		{
+			be.y = y;
+		}
 
-			//Indicate if effect should be drawn on top of everything or just on top of the hex
-			be.position = destTile;
+		// Correction for 2-hex creatures.
+		if(destStack != nullptr && destStack->doubleWide())
+			be.x += (destStack->side == BattleSide::ATTACKER ? -1 : 1)*tilePos.w/2;
 
-			owner->battleEffects.push_back(be);
+		//Indicate if effect should be drawn on top of everything or just on top of the hex
+		be.position = destTile;
 
+		owner->battleEffects.push_back(be);
 	}
 	//battleEffects
 	return true;
 }
 
-void CSpellEffectAnimation::nextFrame()
+void CEffectAnimation::nextFrame()
 {
 	//notice: there may be more than one effect in owner->battleEffects correcponding to this animation (ie. armageddon)
 	for(auto & elem : owner->battleEffects)
@@ -1008,7 +1009,7 @@ void CSpellEffectAnimation::nextFrame()
 		{
 			elem.currentFrame += AnimationControls::getSpellEffectSpeed() * GH.mainFPSmng->getElapsedMilliseconds() / 1000;
 
-			if(elem.currentFrame >= elem.maxFrame)
+			if(elem.currentFrame >= elem.animation->size())
 			{
 				endAnim();
 				break;
@@ -1022,7 +1023,7 @@ void CSpellEffectAnimation::nextFrame()
 	}
 }
 
-void CSpellEffectAnimation::endAnim()
+void CEffectAnimation::endAnim()
 {
 	CBattleAnimation::endAnim();
 
@@ -1038,7 +1039,6 @@ void CSpellEffectAnimation::endAnim()
 
 	for(auto & elem : toDel)
 	{
-		delete elem->anim;
 		owner->battleEffects.erase(elem);
 	}
 

+ 7 - 9
client/battle/CBattleAnimations.h

@@ -208,16 +208,15 @@ public:
 	void endAnim() override;
 
 	//last two params only for catapult attacks
-	CShootingAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest, 
-		const CStack * _attacked, bool _catapult = false, int _catapultDmg = 0); 
+	CShootingAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest,
+		const CStack * _attacked, bool _catapult = false, int _catapultDmg = 0);
 	virtual ~CShootingAnimation(){};
 };
 
-/// This class manages a spell effect animation
-class CSpellEffectAnimation : public CBattleAnimation
+/// This class manages effect animation
+class CEffectAnimation : public CBattleAnimation
 {
 private:
-	ui32 effect;
 	BattleHex destTile;
 	std::string	customAnim;
 	int	x, y, dx, dy;
@@ -228,8 +227,7 @@ public:
 	void nextFrame() override;
 	void endAnim() override;
 
-	CSpellEffectAnimation(CBattleInterface * _owner, ui32 _effect, BattleHex _destTile, int _dx = 0, int _dy = 0, bool _Vflip = false, bool _alignToBottom = false);
-	CSpellEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx = 0, int _dy = 0, bool _Vflip = false, bool _alignToBottom = false);
-	CSpellEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip = false, bool _alignToBottom = false);
-	virtual ~CSpellEffectAnimation(){};
+	CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx = 0, int _dy = 0, bool _Vflip = false, bool _alignToBottom = false);
+	CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip = false, bool _alignToBottom = false);
+	virtual ~CEffectAnimation(){};
 };

+ 54 - 47
client/battle/CBattleInterface.cpp

@@ -23,6 +23,7 @@
 #include "../CPlayerInterface.h"
 #include "../CVideoHandler.h"
 #include "../Graphics.h"
+#include "../gui/CAnimation.h"
 #include "../gui/CCursorHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Extensions.h"
@@ -263,7 +264,10 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
 			battleImage = hero1->type->heroClass->imageBattleMale;
 
 		attackingHero = new CBattleHero(battleImage, false, hero1->tempOwner, hero1->tempOwner == curInt->playerID ? hero1 : nullptr, this);
-		attackingHero->pos = genRect(attackingHero->dh->ourImages[0].bitmap->h, attackingHero->dh->ourImages[0].bitmap->w, pos.x - 43, pos.y - 19);
+
+		IImage * img = attackingHero->animation->getImage(0, 0, true);
+		if(img)
+			attackingHero->pos = genRect(img->height(), img->width(), pos.x - 43, pos.y - 19);
 	}
 	else
 	{
@@ -278,7 +282,10 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
 			battleImage = hero2->type->heroClass->imageBattleMale;
 
 		defendingHero = new CBattleHero(battleImage, true, hero2->tempOwner, hero2->tempOwner == curInt->playerID ? hero2 : nullptr, this);
-		defendingHero->pos = genRect(defendingHero->dh->ourImages[0].bitmap->h, defendingHero->dh->ourImages[0].bitmap->w, pos.x + 693, pos.y - 19);
+
+		IImage * img = defendingHero->animation->getImage(0, 0, true);
+		if(img)
+			defendingHero->pos = genRect(img->height(), img->width(), pos.x + 693, pos.y - 19);
 	}
 	else
 	{
@@ -425,9 +432,6 @@ CBattleInterface::~CBattleInterface()
 	for (auto & elem : creAnims)
 		delete elem.second;
 
-	for (auto & elem : idToProjectile)
-		delete elem.second;
-
 	for (auto & elem : idToObstacle)
 		delete elem.second;
 
@@ -999,20 +1003,21 @@ void CBattleInterface::newStack(const CStack *stack)
 
 void CBattleInterface::initStackProjectile(const CStack * stack)
 {
-	CDefHandler *&projectile = idToProjectile[stack->getCreature()->idNumber];
-
-	const CCreature *creature;//creature whose shots should be loaded
-	if (stack->getCreature()->idNumber == CreatureID::ARROW_TOWERS)
+	const CCreature * creature;//creature whose shots should be loaded
+	if(stack->getCreature()->idNumber == CreatureID::ARROW_TOWERS)
 		creature = CGI->creh->creatures[siegeH->town->town->clientInfo.siegeShooter];
 	else
 		creature = stack->getCreature();
 
-	projectile = CDefHandler::giveDef(creature->animation.projectileImageName);
+	std::shared_ptr<CAnimation> projectile = std::make_shared<CAnimation>(creature->animation.projectileImageName);
+	projectile->preload();
 
-	for (auto & elem : projectile->ourImages) //alpha transforming
-	{
-		CSDL_Ext::alphaTransform(elem.bitmap);
-	}
+	if(projectile->size(1) != 0)
+		logAnim->error("Expected empty group 1 in stack projectile");
+	else
+		projectile->createFlippedGroup(0, 1);
+
+	idToProjectile[stack->getCreature()->idNumber] = projectile;
 }
 
 void CBattleInterface::stackRemoved(int stackID)
@@ -1216,7 +1221,7 @@ void CBattleInterface::stackIsCatapulting(const CatapultAttack & ca)
 		{
 			Point destPos = CClickableHex::getXYUnitAnim(attackInfo.destinationTile, nullptr, this) + Point(99, 120);
 
-			addNewAnim(new CSpellEffectAnimation(this, "SGEXPL.DEF", destPos.x, destPos.y));
+			addNewAnim(new CEffectAnimation(this, "SGEXPL.DEF", destPos.x, destPos.y));
 		}
 	}
 
@@ -1304,20 +1309,23 @@ void CBattleInterface::spellCast(const BattleSpellCast *sc)
 
 		std::string animToDisplay = spell.animationInfo.selectProjectile(angle);
 
-		if (!animToDisplay.empty())
+		if(!animToDisplay.empty())
 		{
+			//TODO: calculate inside CEffectAnimation
+			std::shared_ptr<CAnimation> tmp = std::make_shared<CAnimation>(animToDisplay);
+			tmp->load(0, 0);
+			IImage * first = tmp->getImage(0, 0);
+
 			//displaying animation
-			CDefEssential *animDef = CDefHandler::giveDefEss(animToDisplay);
 			double diffX = (destcoord.x - srccoord.x)*(destcoord.x - srccoord.x);
 			double diffY = (destcoord.y - srccoord.y)*(destcoord.y - srccoord.y);
 			double distance = sqrt(diffX + diffY);
 
 			int steps = distance / AnimationControls::getSpellEffectSpeed() + 1;
-			int dx = (destcoord.x - srccoord.x - animDef->ourImages[0].bitmap->w)/steps;
-			int dy = (destcoord.y - srccoord.y - animDef->ourImages[0].bitmap->h)/steps;
+			int dx = (destcoord.x - srccoord.x - first->width())/steps;
+			int dy = (destcoord.y - srccoord.y - first->height())/steps;
 
-			delete animDef;
-			addNewAnim(new CSpellEffectAnimation(this, animToDisplay, srccoord.x, srccoord.y, dx, dy, Vflip));
+			addNewAnim(new CEffectAnimation(this, animToDisplay, srccoord.x, srccoord.y, dx, dy, Vflip));
 		}
 	}
 	waitForAnims();
@@ -1349,8 +1357,8 @@ void CBattleInterface::spellCast(const BattleSpellCast *sc)
 	{
 		Point leftHero = Point(15, 30) + pos;
 		Point rightHero = Point(755, 30) + pos;
-		addNewAnim(new CSpellEffectAnimation(this, sc->side ? "SP07_A.DEF" : "SP07_B.DEF", leftHero.x, leftHero.y, 0, 0, false));
-		addNewAnim(new CSpellEffectAnimation(this, sc->side ? "SP07_B.DEF" : "SP07_A.DEF", rightHero.x, rightHero.y, 0, 0, false));
+		addNewAnim(new CEffectAnimation(this, sc->side ? "SP07_A.DEF" : "SP07_B.DEF", leftHero.x, leftHero.y, 0, 0, false));
+		addNewAnim(new CEffectAnimation(this, sc->side ? "SP07_B.DEF" : "SP07_A.DEF", rightHero.x, rightHero.y, 0, 0, false));
 	}
 }
 
@@ -1413,9 +1421,11 @@ void CBattleInterface::castThisSpell(SpellID spellID)
 	}
 }
 
-void CBattleInterface::displayEffect(ui32 effect, int destTile)
+void CBattleInterface::displayEffect(ui32 effect, BattleHex destTile)
 {
-	addNewAnim(new CSpellEffectAnimation(this, effect, destTile, 0, 0, false));
+	std::string customAnim = graphics->battleACToDef[effect][0];
+
+	addNewAnim(new CEffectAnimation(this, customAnim, destTile));
 }
 
 void CBattleInterface::displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile)
@@ -1426,7 +1436,7 @@ void CBattleInterface::displaySpellAnimation(const CSpell::TAnimation & animatio
 	}
 	else
 	{
-		addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM));
+		addNewAnim(new CEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM));
 	}
 }
 
@@ -2732,7 +2742,7 @@ void CBattleInterface::obstaclePlaced(const CObstacleInstance & oi)
 	//we assume here that effect graphics have the same size as the usual obstacle image
 	// -> if we know how to blit obstacle, let's blit the effect in the same place
 	Point whereTo = getObstaclePosition(getObstacleImage(oi), oi);
-	addNewAnim(new CSpellEffectAnimation(this, defname, whereTo.x, whereTo.y));
+	addNewAnim(new CEffectAnimation(this, defname, whereTo.x, whereTo.y));
 
 	//TODO we need to wait after playing sound till it's finished, otherwise it overlaps and sounds really bad
 	//CCS->soundh->playSound(sound);
@@ -3166,23 +3176,18 @@ void CBattleInterface::showProjectiles(SDL_Surface *to)
 				continue; // wait...
 		}
 
-		SDL_Surface *image = idToProjectile[it->creID]->ourImages[it->frameNum].bitmap;
-
-		SDL_Rect dst;
-		dst.h = image->h;
-		dst.w = image->w;
-		dst.x = it->x - dst.w / 2;
-		dst.y = it->y - dst.h / 2;
+		size_t group = it->reverse ? 1 : 0;
+		IImage * image = idToProjectile[it->creID]->getImage(it->frameNum, group, true);
 
-		if (it->reverse)
+		if(image)
 		{
-			SDL_Surface *rev = CSDL_Ext::verticalFlip(image);
-			CSDL_Ext::blit8bppAlphaTo24bpp(rev, nullptr, to, &dst);
-			SDL_FreeSurface(rev);
-		}
-		else
-		{
-			CSDL_Ext::blit8bppAlphaTo24bpp(image, nullptr, to, &dst);
+			SDL_Rect dst;
+			dst.h = image->height();
+			dst.w = image->width();
+			dst.x = it->x - dst.w / 2;
+			dst.y = it->y - dst.h / 2;
+
+			image->draw(to, &dst, nullptr);
 		}
 
 		// Update projectile
@@ -3200,7 +3205,7 @@ void CBattleInterface::showProjectiles(SDL_Surface *to)
 				it->y = it->catapultInfo->calculateY(it->x);
 
 				++(it->frameNum);
-				it->frameNum %= idToProjectile[it->creID]->ourImages.size();
+				it->frameNum %= idToProjectile[it->creID]->size(0);
 			}
 			else
 			{
@@ -3368,11 +3373,13 @@ void CBattleInterface::showBattleEffects(SDL_Surface *to, const std::vector<cons
 	for (auto & elem : battleEffects)
 	{
 		int currentFrame = floor(elem->currentFrame);
-		currentFrame %= elem->anim->ourImages.size();
+		currentFrame %= elem->animation->size();
+
+		IImage * img = elem->animation->getImage(currentFrame);
+
+		SDL_Rect temp_rect = genRect(img->height(), img->width(), elem->x, elem->y);
 
-		SDL_Surface *bitmapToBlit = elem->anim->ourImages[currentFrame].bitmap;
-		SDL_Rect temp_rect = genRect(bitmapToBlit->h, bitmapToBlit->w, elem->x, elem->y);
-		SDL_BlitSurface(bitmapToBlit, nullptr, to, &temp_rect);
+		img->draw(to, &temp_rect, nullptr);
 	}
 }
 

+ 8 - 5
client/battle/CBattleInterface.h

@@ -48,6 +48,7 @@ struct BattleHex;
 struct InfoAboutHero;
 struct BattleAction;
 class CBattleGameInterface;
+class CAnimation;
 
 /// Small struct which contains information about the id of the attacked stack, the damage dealt,...
 struct StackAttackedInfo
@@ -67,8 +68,7 @@ struct BattleEffect
 {
 	int x, y; //position on the screen
 	float currentFrame;
-	int maxFrame;
-	CDefHandler *anim; //animation to display
+	std::shared_ptr<CAnimation> animation;
 	int effectID; //uniqueID equal ot ID of appropriate CSpellEffectAnim
 	BattleHex position; //Indicates if effect which hex the effect is drawn on
 };
@@ -120,6 +120,7 @@ class CBattleInterface : public CIntObject
 	};
 private:
 	SDL_Surface *background, *menu, *amountNormal, *amountNegative, *amountPositive, *amountEffNeutral, *cellBorders, *backgroundWithHexes;
+
 	CButton *bOptions, *bSurrender, *bFlee, *bAutofight, *bSpell,
 		* bWait, *bDefence, *bConsoleUp, *bConsoleDown, *btactNext, *btactEnd;
 	CBattleConsole *console;
@@ -128,7 +129,9 @@ private:
 	const CCreatureSet *army1, *army2; //copy of initial armies (for result window)
 	const CGHeroInstance *attackingHeroInstance, *defendingHeroInstance;
 	std::map<int, CCreatureAnimation *> creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
-	std::map<int, CDefHandler *> idToProjectile; //projectiles of creatures (creatureID, defhandler)
+
+	std::map<int, std::shared_ptr<CAnimation>> idToProjectile;
+
 	std::map<int, CDefHandler *> idToObstacle; //obstacles located on the battlefield
 	std::map<int, SDL_Surface *> idToAbsoluteObstacle; //obstacles located on the battlefield
 
@@ -340,7 +343,7 @@ public:
 	void spellCast(const BattleSpellCast *sc); //called when a hero casts a spell
 	void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
 	void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook
-	void displayEffect(ui32 effect, int destTile); //displays custom effect on the battlefield
+	void displayEffect(ui32 effect, BattleHex destTile); //displays custom effect on the battlefield
 
 	void displaySpellCast(SpellID spellID, BattleHex destinationTile); //displays spell`s cast animation
 	void displaySpellEffect(SpellID spellID, BattleHex destinationTile); //displays spell`s affected animation
@@ -376,7 +379,7 @@ public:
 
 	friend class CBattleResultWindow;
 	friend class CBattleHero;
-	friend class CSpellEffectAnimation;
+	friend class CEffectAnimation;
 	friend class CBattleStackAnimation;
 	friend class CReverseAnimation;
 	friend class CDefenceAnimation;

+ 45 - 48
client/battle/CBattleInterfaceClasses.cpp

@@ -13,13 +13,13 @@
 #include "CBattleInterface.h"
 
 #include "../CBitmapHandler.h"
-#include "../CDefHandler.h"
 #include "../CGameInfo.h"
 #include "../CMessage.h"
 #include "../CMusicHandler.h"
 #include "../CPlayerInterface.h"
 #include "../CVideoHandler.h"
 #include "../Graphics.h"
+#include "../gui/CAnimation.h"
 #include "../gui/CCursorHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/SDL_Extensions.h"
@@ -129,13 +129,18 @@ CBattleConsole::CBattleConsole() : lastShown(-1), alterTxt(""), whoSetAlter(0)
 
 void CBattleHero::show(SDL_Surface * to)
 {
+	IImage * flagFrame = flagAnimation->getImage(flagAnim, 0, true);
+
+	if(!flagFrame)
+		return;
+
 	//animation of flag
 	SDL_Rect temp_rect;
 	if(flip)
 	{
 		temp_rect = genRect(
-			flag->ourImages[flagAnim].bitmap->h,
-			flag->ourImages[flagAnim].bitmap->w,
+			flagFrame->height(),
+			flagFrame->width(),
 			pos.x + 61,
 			pos.y + 39);
 
@@ -143,28 +148,30 @@ void CBattleHero::show(SDL_Surface * to)
 	else
 	{
 		temp_rect = genRect(
-			flag->ourImages[flagAnim].bitmap->h,
-			flag->ourImages[flagAnim].bitmap->w,
+			flagFrame->height(),
+			flagFrame->width(),
 			pos.x + 72,
 			pos.y + 39);
 	}
-	CSDL_Ext::blit8bppAlphaTo24bpp(
-		flag->ourImages[flagAnim].bitmap,
-		nullptr,
-		screen,
-		&temp_rect);
+
+	flagFrame->draw(screen, &temp_rect, nullptr); //FIXME: why screen?
 
 	//animation of hero
 	SDL_Rect rect = pos;
-	CSDL_Ext::blit8bppAlphaTo24bpp(dh->ourImages[currentFrame].bitmap, nullptr, to, &rect);
 
-	if ( ++animCount == 4 )
+	IImage * heroFrame = animation->getImage(currentFrame, phase, true);
+	if(!heroFrame)
+		return;
+
+	heroFrame->draw(to, &rect, nullptr);
+
+	if(++animCount >= 4)
 	{
 		animCount = 0;
-		if ( ++flagAnim >= flag->ourImages.size())
+		if(++flagAnim >= flagAnimation->size(0))
 			flagAnim = 0;
 
-		if ( ++currentFrame >= lastFrame)
+		if(++currentFrame >= lastFrame)
 			switchToNextPhase();
 	}
 }
@@ -190,7 +197,13 @@ void CBattleHero::clickLeft(tribool down, bool previousState)
 	if(myOwner->spellDestSelectMode) //we are casting a spell
 		return;
 
-	if(myHero != nullptr && !down &&  myOwner->myTurn && myOwner->getCurrentPlayerInterface()->cb->battleCanCastSpell(myHero, ECastingMode::HERO_CASTING) == ESpellCastProblem::OK) //check conditions
+	if(boost::logic::indeterminate(down))
+		return;
+
+	if(!myHero || down || !myOwner->myTurn)
+		return;
+
+	if(myOwner->getCurrentPlayerInterface()->cb->battleCanCastSpell(myHero, ECastingMode::HERO_CASTING) == ESpellCastProblem::OK) //check conditions
 	{
 		for(int it=0; it<GameConstants::BFIELD_SIZE; ++it) //do nothing when any hex is hovered - hero's animation overlaps battlefield
 		{
@@ -205,6 +218,9 @@ void CBattleHero::clickLeft(tribool down, bool previousState)
 
 void CBattleHero::clickRight(tribool down, bool previousState)
 {
+	if(boost::logic::indeterminate(down))
+		return;
+
 	Point windowPosition;
 	windowPosition.x = (!flip) ? myOwner->pos.topLeft().x + 1 : myOwner->pos.topRight().x - 79;
 	windowPosition.y = myOwner->pos.y + 135;
@@ -220,24 +236,19 @@ void CBattleHero::clickRight(tribool down, bool previousState)
 
 void CBattleHero::switchToNextPhase()
 {
-	if (phase != nextPhase)
+	if(phase != nextPhase)
 	{
 		phase = nextPhase;
 
-		//find first and last frames of our animation
-		for (firstFrame = 0;
-		     firstFrame < dh->ourImages.size() && dh->ourImages[firstFrame].groupNumber != phase;
-		     firstFrame++);
+		firstFrame = 0;
 
-		for (lastFrame = firstFrame;
-			 lastFrame < dh->ourImages.size() && dh->ourImages[lastFrame].groupNumber == phase;
-			 lastFrame++);
+		lastFrame = animation->size(phase);
 	}
 
 	currentFrame = firstFrame;
 }
 
-CBattleHero::CBattleHero(const std::string & defName, bool flipG, PlayerColor player, const CGHeroInstance * hero, const CBattleInterface * owner):
+CBattleHero::CBattleHero(const std::string & animationPath, bool flipG, PlayerColor player, const CGHeroInstance * hero, const CBattleInterface * owner):
     flip(flipG),
     myHero(hero),
     myOwner(owner),
@@ -246,39 +257,25 @@ CBattleHero::CBattleHero(const std::string & defName, bool flipG, PlayerColor pl
     flagAnim(0),
     animCount(0)
 {
-	dh = CDefHandler::giveDef( defName );
-	for(auto & elem : dh->ourImages) //transforming images
-	{
-		if(flip)
-		{
-			SDL_Surface * hlp = CSDL_Ext::verticalFlip(elem.bitmap);
-			SDL_FreeSurface(elem.bitmap);
-			elem.bitmap = hlp;
-		}
-		CSDL_Ext::alphaTransform(elem.bitmap);
-	}
+	animation = std::make_shared<CAnimation>(animationPath);
+	animation->preload();
+	if(flipG)
+		animation->verticalFlip();
 
 	if(flip)
-		flag = CDefHandler::giveDef("CMFLAGR.DEF");
+		flagAnimation = std::make_shared<CAnimation>("CMFLAGR");
 	else
-		flag = CDefHandler::giveDef("CMFLAGL.DEF");
+		flagAnimation = std::make_shared<CAnimation>("CMFLAGL");
+
+	flagAnimation->preload();
+	flagAnimation->playerColored(player);
 
-	//coloring flag and adding transparency
-	for(auto & elem : flag->ourImages)
-	{
-		CSDL_Ext::alphaTransform(elem.bitmap);
-		graphics->blueToPlayersAdv(elem.bitmap, player);
-	}
 	addUsedEvents(LCLICK | RCLICK | HOVER);
 
 	switchToNextPhase();
 }
 
-CBattleHero::~CBattleHero()
-{
-	delete dh;
-	delete flag;
-}
+CBattleHero::~CBattleHero() = default;
 
 CBattleOptionsWindow::CBattleOptionsWindow(const SDL_Rect & position, CBattleInterface *owner)
 {

+ 8 - 4
client/battle/CBattleInterfaceClasses.h

@@ -14,7 +14,6 @@
 #include "../windows/CWindowObject.h"
 
 struct SDL_Surface;
-class CDefHandler;
 class CGHeroInstance;
 class CBattleInterface;
 class CPicture;
@@ -53,19 +52,24 @@ class CBattleHero : public CIntObject
 	void switchToNextPhase();
 public:
 	bool flip; //false if it's attacking hero, true otherwise
-	CDefHandler *dh, *flag; //animation and flag
+
+	std::shared_ptr<CAnimation> animation;
+	std::shared_ptr<CAnimation> flagAnimation;
+
 	const CGHeroInstance * myHero; //this animation's hero instance
 	const CBattleInterface * myOwner; //battle interface to which this animation is assigned
 	int phase; //stage of animation
 	int nextPhase; //stage of animation to be set after current phase is fully displayed
 	int currentFrame, firstFrame, lastFrame; //frame of animation
-	ui8 flagAnim, animCount; //for flag animation
+
+	size_t flagAnim;
+	ui8 animCount; //for flag animation
 	void show(SDL_Surface * to) override; //prints next frame of animation to to
 	void setPhase(int newPhase); //sets phase of hero animation
 	void hover(bool on) override;
 	void clickLeft(tribool down, bool previousState) override; //call-in
 	void clickRight(tribool down, bool previousState) override; //call-in
-	CBattleHero(const std::string &defName, bool filpG, PlayerColor player, const CGHeroInstance *hero, const CBattleInterface *owner);
+	CBattleHero(const std::string & animationPath, bool filpG, PlayerColor player, const CGHeroInstance * hero, const CBattleInterface * owner);
 	~CBattleHero();
 };
 

+ 45 - 172
client/battle/CCreatureAnimation.cpp

@@ -10,14 +10,10 @@
 #include "StdInc.h"
 #include "CCreatureAnimation.h"
 
-#include "../../lib/vcmi_endian.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CCreatureHandler.h"
-#include "../../lib/filesystem/Filesystem.h"
-#include "../../lib/filesystem/CBinaryReader.h"
-#include "../../lib/filesystem/CMemoryStream.h"
 
-#include "../gui/SDL_Pixels.h"
+#include "../gui/SDL_Extensions.h"
 
 static const SDL_Color creatureBlueBorder = { 0, 255, 255, 255 };
 static const SDL_Color creatureGoldBorder = { 255, 255, 0, 255 };
@@ -142,60 +138,43 @@ void CCreatureAnimation::setType(CCreatureAnim::EAnimType type)
 	play();
 }
 
-CCreatureAnimation::CCreatureAnimation(std::string name, TSpeedController controller)
-    : defName(name),
-      speed(0.1),
-      currentFrame(0),
-      elapsedTime(0),
+CCreatureAnimation::CCreatureAnimation(const std::string & name_, TSpeedController controller)
+	: name(name_),
+	  speed(0.1),
+	  currentFrame(0),
+	  elapsedTime(0),
 	  type(CCreatureAnim::HOLDING),
 	  border(CSDL_Ext::makeColor(0, 0, 0, 0)),
-      speedController(controller),
-      once(false)
+	  speedController(controller),
+	  once(false)
 {
-	// separate block to avoid accidental use of "data" after it was moved into "pixelData"
-	{
-		ResourceID resID(std::string("SPRITES/") + name, EResType::ANIMATION);
-
-		auto data = CResourceHandler::get()->load(resID)->readAll();
-
-		pixelData = std::move(data.first);
-		pixelDataSize = data.second;
-	}
-
-	CMemoryStream stm(pixelData.get(), pixelDataSize);
-
-	CBinaryReader reader(&stm);
+	forward = std::make_shared<CAnimation>(name_);
+	reverse = std::make_shared<CAnimation>(name_);
 
-	reader.readInt32(); // def type, unused
+	//todo: optimize
+	forward->preload();
+	reverse->preload();
 
-	fullWidth  = reader.readInt32();
-	fullHeight = reader.readInt32();
-
-	int totalBlocks = reader.readInt32();
-
-	for (auto & elem : palette)
+	// if necessary, add one frame into vcmi-only group DEAD
+	if(forward->size(CCreatureAnim::DEAD) == 0)
 	{
-		elem.r = reader.readUInt8();
-		elem.g = reader.readUInt8();
-		elem.b = reader.readUInt8();
-		elem.a = SDL_ALPHA_OPAQUE;
+		forward->duplicateImage(CCreatureAnim::DEATH, forward->size(CCreatureAnim::DEATH)-1, CCreatureAnim::DEAD);
+		reverse->duplicateImage(CCreatureAnim::DEATH, reverse->size(CCreatureAnim::DEATH)-1, CCreatureAnim::DEAD);
 	}
 
-	for (int i=0; i<totalBlocks; i++)
-	{
-		int groupID = reader.readInt32();
-
-		int totalInBlock = reader.readInt32();
-
-		reader.skip(4 + 4 + 13 * totalInBlock); // some unused data
+	//TODO: get dimensions form CAnimation
+	IImage * first = forward->getImage(0, type, true);
 
-		for (int j=0; j<totalInBlock; j++)
-			dataOffsets[groupID].push_back(reader.readUInt32());
+	if(!first)
+	{
+		fullWidth = 0;
+		fullHeight = 0;
+		return;
 	}
+	fullWidth = first->width();
+	fullHeight = first->height();
 
-	// if necessary, add one frame into vcmi-only group DEAD
-	if (dataOffsets.count(CCreatureAnim::DEAD) == 0)
-		dataOffsets[CCreatureAnim::DEAD].push_back(dataOffsets[CCreatureAnim::DEATH].back());
+	reverse->verticalFlip();
 
 	play();
 }
@@ -285,142 +264,35 @@ static SDL_Color addColors(const SDL_Color & base, const SDL_Color & over)
 			);
 }
 
-std::array<SDL_Color, 8> CCreatureAnimation::genSpecialPalette()
+void CCreatureAnimation::genBorderPalette(IImage::BorderPallete & target)
 {
-	std::array<SDL_Color, 8> ret;
-
-	ret[0] = genShadow(0);
-	ret[1] = genShadow(64);
-	ret[2] = genShadow(128);//unused
-	ret[3] = genShadow(128);//unused
-	ret[4] = genShadow(128);
-	ret[5] = genBorderColor(getBorderStrength(elapsedTime), border);
-	ret[6] = addColors(genShadow(128), genBorderColor(getBorderStrength(elapsedTime), border));
-	ret[7] = addColors(genShadow(64),  genBorderColor(getBorderStrength(elapsedTime), border));
-
-	return ret;
+	target[0] = genBorderColor(getBorderStrength(elapsedTime), border);
+	target[1] = addColors(genShadow(128), genBorderColor(getBorderStrength(elapsedTime), border));
+	target[2] = addColors(genShadow(64),  genBorderColor(getBorderStrength(elapsedTime), border));
 }
 
-template<int bpp>
-void CCreatureAnimation::nextFrameT(SDL_Surface * dest, bool rotate)
+void CCreatureAnimation::nextFrame(SDL_Surface *dest, bool attacker)
 {
-	assert(dataOffsets.count(type) && dataOffsets.at(type).size() > size_t(currentFrame));
-
-	ui32 offset = dataOffsets.at(type).at(floor(currentFrame));
+	size_t frame = floor(currentFrame);
 
-	CMemoryStream stm(pixelData.get(), pixelDataSize);
+	IImage * image = nullptr;
 
-	CBinaryReader reader(&stm);
-
-	reader.getStream()->seek(offset);
-
-	reader.readUInt32(); // unused, size of pixel data for this frame
-	const ui32 defType2 = reader.readUInt32();
-	const ui32 fullWidth = reader.readUInt32();
-	/*const ui32 fullHeight =*/ reader.readUInt32();
-	const ui32 spriteWidth = reader.readUInt32();
-	const ui32 spriteHeight = reader.readUInt32();
-	const int leftMargin = reader.readInt32();
-	const int topMargin = reader.readInt32();
-
-	const int rightMargin = fullWidth - spriteWidth - leftMargin;
-	//const int bottomMargin = fullHeight - spriteHeight - topMargin;
-
-	const size_t baseOffset = reader.getStream()->tell();
+	if(attacker)
+		image = forward->getImage(frame, type);
+	else
+		image = reverse->getImage(frame, type);
 
-	assert(defType2 == 1);
-	UNUSED(defType2);
+	IImage::BorderPallete borderPallete;
+	genBorderPalette(borderPallete);
 
-	auto specialPalette = genSpecialPalette();
+	image->setBorderPallete(borderPallete);
 
-	for (ui32 i=0; i<spriteHeight; i++)
-	{
-		//NOTE: if this loop will be optimized to skip empty lines - recheck this read access
-		ui8 * lineData = pixelData.get() + baseOffset + reader.readUInt32();
-
-		size_t destX = pos.x;
-		if (rotate)
-			destX += rightMargin + spriteWidth - 1;
-		else
-			destX += leftMargin;
-
-		size_t destY = pos.y + topMargin + i;
-		size_t currentOffset = 0;
-		size_t totalRowLength = 0;
-
-		while (totalRowLength < spriteWidth)
-		{
-			ui8 type = lineData[currentOffset++];
-			ui32 length = lineData[currentOffset++] + 1;
-
-			if (type==0xFF)//Raw data
-			{
-				for (size_t j=0; j<length; j++)
-					putPixelAt<bpp>(dest, destX + (rotate?(-j):(j)), destY, lineData[currentOffset + j], specialPalette);
-
-				currentOffset += length;
-			}
-			else// RLE
-			{
-				if (type != 0) // transparency row, handle it here for speed
-				{
-					for (size_t j=0; j<length; j++)
-						putPixelAt<bpp>(dest, destX + (rotate?(-j):(j)), destY, type, specialPalette);
-				}
-			}
-
-			destX += rotate ? (-length) : (length);
-			totalRowLength += length;
-		}
-	}
-}
-
-void CCreatureAnimation::nextFrame(SDL_Surface *dest, bool attacker)
-{
-	// Note: please notice that attacker value is inversed when passed further.
-	// This is intended behavior because "attacker" actually does not needs rotation
-	switch(dest->format->BytesPerPixel)
-	{
-	case 2: return nextFrameT<2>(dest, !attacker);
-	case 3: return nextFrameT<3>(dest, !attacker);
-	case 4: return nextFrameT<4>(dest, !attacker);
-	default:
-		logGlobal->error("%d bpp is not supported!", (int)dest->format->BitsPerPixel);
-	}
+	image->draw(dest, pos.x, pos.y);
 }
 
 int CCreatureAnimation::framesInGroup(CCreatureAnim::EAnimType group) const
 {
-	if(dataOffsets.count(group) == 0)
-		return 0;
-
-	return dataOffsets.at(group).size();
-}
-
-ui8 * CCreatureAnimation::getPixelAddr(SDL_Surface * dest, int X, int Y) const
-{
-	return (ui8*)dest->pixels + X * dest->format->BytesPerPixel + Y * dest->pitch;
-}
-
-template<int bpp>
-inline void CCreatureAnimation::putPixelAt(SDL_Surface * dest, int X, int Y, size_t index, const std::array<SDL_Color, 8> & special) const
-{
-	if ( X < pos.x + pos.w && Y < pos.y + pos.h && X >= 0 && Y >= 0)
-		putPixel<bpp>(getPixelAddr(dest, X, Y), palette[index], index, special);
-}
-
-template<int bpp>
-inline void CCreatureAnimation::putPixel(ui8 * dest, const SDL_Color & color, size_t index, const std::array<SDL_Color, 8> & special) const
-{
-	if((index <= 1) || (index >=4 && index < 8))
-	{
-		const SDL_Color & pal = special[index];
-		ColorPutter<bpp, 0>::PutColor(dest, pal.r, pal.g, pal.b, pal.a);
-	}
-	else
-	{
-		ColorPutter<bpp, 0>::PutColor(dest, color.r, color.g, color.b);
-	}
+	return forward->size(group);
 }
 
 bool CCreatureAnimation::isDead() const
@@ -456,7 +328,8 @@ void CCreatureAnimation::pause()
 
 void CCreatureAnimation::play()
 {
+	//logAnim->trace("Play %s group %d at %d:%d", name, static_cast<int>(getType()), pos.x, pos.y);
     speed = 0;
-    if (speedController(this, type) != 0)
+    if(speedController(this, type) != 0)
         speed = 1 / speedController(this, type);
 }

+ 10 - 30
client/battle/CCreatureAnimation.h

@@ -10,8 +10,8 @@
 #pragma once
 
 #include "../../lib/FunctionList.h"
-#include "../gui/SDL_Extensions.h"
 #include "../widgets/Images.h"
+#include "../gui/CAnimation.h"
 
 class CIntObject;
 class CCreatureAnimation;
@@ -52,21 +52,12 @@ public:
 	typedef std::function<float(CCreatureAnimation *, size_t)> TSpeedController;
 
 private:
-	std::string defName;
+	std::string name;
+	std::shared_ptr<CAnimation> forward;
+	std::shared_ptr<CAnimation> reverse;
 
-	int fullWidth, fullHeight;
-
-	// palette, as read from def file
-	std::array<SDL_Color, 256> palette;
-
-	//key = id of group (note that some groups may be missing)
-	//value = offset of pixel data for each frame, vector size = number of frames in group
-	std::map<int, std::vector<unsigned int>> dataOffsets;
-
-	//animation raw data
-	//TODO: use vector instead?
-	std::unique_ptr<ui8[]> pixelData;
-	size_t pixelDataSize;
+	int fullWidth;
+	int fullHeight;
 
 	// speed of animation, measure in frames per second
 	float speed;
@@ -85,21 +76,10 @@ private:
 
 	bool once; // animation will be played once and the reset to idling
 
-	ui8 * getPixelAddr(SDL_Surface * dest, int ftcpX, int ftcpY) const;
-
-	template<int bpp>
-	void putPixelAt(SDL_Surface * dest, int X, int Y, size_t index, const std::array<SDL_Color, 8> & special) const;
-
-	template<int bpp>
-	void putPixel( ui8 * dest, const SDL_Color & color, size_t index, const std::array<SDL_Color, 8> & special) const;
-
-	template<int bpp>
-	void nextFrameT(SDL_Surface * dest, bool rotate);
-
 	void endAnimation();
 
-	/// creates 8 special colors for current frame
-	std::array<SDL_Color, 8> genSpecialPalette();
+
+	void genBorderPalette(IImage::BorderPallete & target);
 public:
 
 	// function(s) that will be called when animation ends, after reset to 1st frame
@@ -113,12 +93,12 @@ public:
 	/// name - path to .def file, relative to SPRITES/ directory
 	/// controller - function that will return for how long *each* frame
 	/// in specified group of animation should be played, measured in seconds
-	CCreatureAnimation(std::string name, TSpeedController speedController);
+	CCreatureAnimation(const std::string & name_, TSpeedController speedController);
 
 	void setType(CCreatureAnim::EAnimType type); //sets type of animation and cleares framecount
 	CCreatureAnim::EAnimType getType() const; //returns type of animation
 
-	void nextFrame(SDL_Surface * dest, bool rotate);
+	void nextFrame(SDL_Surface * dest, bool attacker);
 
 	// should be called every frame, return true when animation was reset to beginning
 	bool incrementFrame(float timePassed);

+ 55 - 6
client/gui/CAnimation.cpp

@@ -29,7 +29,7 @@ typedef std::map <size_t, std::vector <JsonNode> > source_map;
 typedef std::map<size_t, IImage* > image_map;
 typedef std::map<size_t, image_map > group_map;
 
- /// Class for def loading, methods are based on CDefHandler
+/// Class for def loading
 /// After loading will store general info (palette and frame offsets) and pointer to file itself
 class CDefFile
 {
@@ -100,6 +100,8 @@ public:
 
 	void shiftPalette(int from, int howMany) override;
 
+	void setBorderPallete(const BorderPallete & borderPallete) override;
+
 	friend class SDLImageLoader;
 };
 
@@ -157,6 +159,7 @@ public:
 	void verticalFlip() override;
 
 	void shiftPalette(int from, int howMany) override;
+	void setBorderPallete(const BorderPallete & borderPallete) override;
 
 	friend class CompImageLoader;
 };
@@ -985,6 +988,15 @@ void SDLImage::shiftPalette(int from, int howMany)
 	}
 }
 
+void SDLImage::setBorderPallete(const IImage::BorderPallete & borderPallete)
+{
+	if(surf->format->palette)
+	{
+		SDL_SetColors(surf, const_cast<SDL_Color *>(borderPallete.data()), 5, 3);
+	}
+}
+
+
 SDLImage::~SDLImage()
 {
 	SDL_FreeSurface(surf);
@@ -1238,22 +1250,27 @@ CompImage::~CompImage()
 
 void CompImage::horizontalFlip()
 {
-	logAnim->error("CompImage::horizontalFlip is not implemented");
+	logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
 }
 
 void CompImage::verticalFlip()
 {
-	logAnim->error("CompImage::verticalFlip is not implemented");
+	logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
 }
 
 void CompImage::shiftPalette(int from, int howMany)
 {
-	logAnim->error("CompImage::shiftPalette is not implemented");
+	logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
+}
+
+void CompImage::setBorderPallete(const IImage::BorderPallete & borderPallete)
+{
+	logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
 }
 
-void CompImage::exportBitmap(const boost::filesystem::path& path) const
+void CompImage::exportBitmap(const boost::filesystem::path & path) const
 {
-	logAnim->error("CompImage::exportBitmap is not implemented");
+	logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
 }
 
 
@@ -1605,6 +1622,38 @@ size_t CAnimation::size(size_t group) const
 	return 0;
 }
 
+void CAnimation::horizontalFlip()
+{
+	for(auto & group : images)
+		for(auto & image : group.second)
+			image.second->horizontalFlip();
+}
+
+void CAnimation::verticalFlip()
+{
+	for(auto & group : images)
+		for(auto & image : group.second)
+			image.second->verticalFlip();
+}
+
+void CAnimation::playerColored(PlayerColor player)
+{
+	for(auto & group : images)
+		for(auto & image : group.second)
+			image.second->playerColored(player);
+}
+
+void CAnimation::createFlippedGroup(const size_t sourceGroup, const size_t targetGroup)
+{
+	for(size_t frame = 0; frame < size(sourceGroup); ++frame)
+	{
+		duplicateImage(sourceGroup, frame, targetGroup);
+
+		IImage * image = getImage(frame, targetGroup);
+		image->verticalFlip();
+	}
+}
+
 float CFadeAnimation::initialCounter() const
 {
 	if (fadingMode == EMode::OUT)

+ 10 - 0
client/gui/CAnimation.h

@@ -24,6 +24,7 @@ class IImage
 {
 	int refCount;
 public:
+	using BorderPallete = std::array<SDL_Color, 3>;
 
 	//draws image on surface "where" at position
 	virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, Rect * src = nullptr, ui8 alpha = 255) const=0;
@@ -49,6 +50,9 @@ public:
 	//only indexed bitmaps, 16 colors maximum
 	virtual void shiftPalette(int from, int howMany) = 0;
 
+	//only indexed bitmaps, colors 5,6,7 must be special
+	virtual void setBorderPallete(const BorderPallete & borderPallete) = 0;
+
 	virtual void horizontalFlip() = 0;
 	virtual void verticalFlip() = 0;
 
@@ -127,6 +131,12 @@ public:
 
 	//total count of frames in group (including not loaded)
 	size_t size(size_t group=0) const;
+
+	void horizontalFlip();
+	void verticalFlip();
+	void playerColored(PlayerColor player);
+
+	void createFlippedGroup(const size_t sourceGroup, const size_t targetGroup);
 };
 
 const float DEFAULT_DELTA = 0.05f;

+ 19 - 28
client/gui/CCursorHandler.cpp

@@ -23,11 +23,20 @@ void CCursorHandler::initCursor()
 	xpos = ypos = 0;
 	type = ECursor::DEFAULT;
 	dndObject = nullptr;
-	currentCursor = nullptr;
+
+	cursors =
+	{
+		make_unique<CAnimImage>("CRADVNTR", 0),
+		make_unique<CAnimImage>("CRCOMBAT", 0),
+		make_unique<CAnimImage>("CRDEFLT",  0),
+		make_unique<CAnimImage>("CRSPELL",  0)
+	};
+
+	currentCursor = cursors.at(int(ECursor::DEFAULT)).get();
 
 	help = CSDL_Ext::newSurface(40,40);
 	//No blending. Ensure, that we are copying pixels during "screen restore draw"
-	SDL_SetSurfaceBlendMode(help,SDL_BLENDMODE_NONE);	
+	SDL_SetSurfaceBlendMode(help,SDL_BLENDMODE_NONE);
 	SDL_ShowCursor(SDL_DISABLE);
 
 	changeGraphic(ECursor::ADVENTURE, 0);
@@ -35,32 +44,23 @@ void CCursorHandler::initCursor()
 
 void CCursorHandler::changeGraphic(ECursor::ECursorTypes type, int index)
 {
-	std::string cursorDefs[4] = { "CRADVNTR.DEF", "CRCOMBAT.DEF", "CRDEFLT.DEF", "CRSPELL.DEF" };
-
-	if (type != this->type)
+	if(type != this->type)
 	{
-		BLOCK_CAPTURING; // not used here
-
 		this->type = type;
 		this->frame = index;
-
-		delete currentCursor;
-		currentCursor = new CAnimImage(cursorDefs[int(type)], index);
+		currentCursor = cursors.at(int(type)).get();
+		currentCursor->setFrame(index);
 	}
-
-	if (frame != index)
+	else if(index != this->frame)
 	{
-		frame = index;
+		this->frame = index;
 		currentCursor->setFrame(index);
 	}
 }
 
-void CCursorHandler::dragAndDropCursor(CAnimImage * object)
+void CCursorHandler::dragAndDropCursor(std::unique_ptr<CAnimImage> object)
 {
-	if (dndObject)
-		delete dndObject;
-
-	dndObject = object;
+	dndObject = std::move(object);
 }
 
 void CCursorHandler::cursorMove(const int & x, const int & y)
@@ -101,13 +101,6 @@ void CCursorHandler::drawRestored()
 
 	SDL_Rect temp_rect = genRect(40, 40, x, y);
 	SDL_BlitSurface(help, nullptr, screen, &temp_rect);
-	//blitAt(help,x,y);
-}
-
-void CCursorHandler::draw(SDL_Surface *to)
-{
-	currentCursor->moveTo(Point(xpos, ypos));
-	currentCursor->showAll(screen);
 }
 
 void CCursorHandler::shiftPos( int &x, int &y )
@@ -233,12 +226,10 @@ void CCursorHandler::render()
 	drawRestored();
 }
 
+CCursorHandler::CCursorHandler() = default;
 
 CCursorHandler::~CCursorHandler()
 {
 	if(help)
 		SDL_FreeSurface(help);
-
-	delete currentCursor;
-	delete dndObject;
 }

+ 10 - 8
client/gui/CCursorHandler.h

@@ -17,27 +17,28 @@ namespace ECursor
 	enum ECursorTypes { ADVENTURE, COMBAT, DEFAULT, SPELLBOOK };
 
 	enum EBattleCursors { COMBAT_BLOCKED, COMBAT_MOVE, COMBAT_FLY, COMBAT_SHOOT,
-						COMBAT_HERO, COMBAT_QUERY, COMBAT_POINTER, 
+						COMBAT_HERO, COMBAT_QUERY, COMBAT_POINTER,
 						//various attack frames
 						COMBAT_SHOOT_PENALTY = 15, COMBAT_SHOOT_CATAPULT, COMBAT_HEAL,
 						COMBAT_SACRIFICE, COMBAT_TELEPORT};
 }
 
 /// handles mouse cursor
-class CCursorHandler 
+class CCursorHandler final
 {
 	SDL_Surface * help;
 	CAnimImage * currentCursor;
-	CAnimImage * dndObject; //if set, overrides currentCursor
+
+	std::unique_ptr<CAnimImage> dndObject; //if set, overrides currentCursor
+
+	std::array<std::unique_ptr<CAnimImage>, 4> cursors;
+
 	bool showing;
 
 	/// Draw cursor preserving original image below cursor
 	void drawWithScreenRestore();
 	/// Restore original image below cursor
 	void drawRestored();
-	/// Simple draw cursor
-	void draw(SDL_Surface *to);
-	
 public:
 	/// position of cursor
 	int xpos, ypos;
@@ -58,8 +59,8 @@ public:
 	 * @param image Image to replace cursor with or nullptr to use the normal
 	 * cursor. CursorHandler takes ownership of object
 	 */
-	void dragAndDropCursor (CAnimImage * image);
-	
+	void dragAndDropCursor (std::unique_ptr<CAnimImage> image);
+
 	void render();
 
 	void shiftPos( int &x, int &y );
@@ -71,5 +72,6 @@ public:
 	/// Move cursor to screen center
 	void centerCursor();
 
+	CCursorHandler();
 	~CCursorHandler();
 };

+ 1 - 2
client/gui/CGuiHandler.cpp

@@ -459,8 +459,7 @@ void CGuiHandler::renderFrame()
 		// draw the mouse cursor and update the screen
 		CCS->curh->render();
 
-		if(0 != SDL_RenderCopy(mainRenderer, screenTexture, nullptr, nullptr))
-			logGlobal->error("%s SDL_RenderCopy %s", __FUNCTION__, SDL_GetError());
+		SDL_RenderCopy(mainRenderer, screenTexture, nullptr, nullptr);
 
 		SDL_RenderPresent(mainRenderer);
 	}

+ 2 - 2
client/widgets/CArtifactHolder.cpp

@@ -290,7 +290,7 @@ void CHeroArtPlace::select ()
 		}
 	}
 
-	CCS->curh->dragAndDropCursor(new CAnimImage("artifact", ourArt->artType->iconIndex));
+	CCS->curh->dragAndDropCursor(make_unique<CAnimImage>("artifact", ourArt->artType->iconIndex));
 	ourOwner->commonInfo->src.setTo(this, false);
 	ourOwner->markPossibleSlots(ourArt);
 
@@ -766,7 +766,7 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact
 			commonInfo->src.art = dst.getArt();
 			commonInfo->src.slotID = dst.slot;
 			assert(commonInfo->src.AOH);
-			CCS->curh->dragAndDropCursor(new CAnimImage("artifact", dst.getArt()->artType->iconIndex));
+			CCS->curh->dragAndDropCursor(make_unique<CAnimImage>("artifact", dst.getArt()->artType->iconIndex));
 			markPossibleSlots(dst.getArt());
 		}
 	}

+ 12 - 13
client/windows/CHeroWindow.cpp

@@ -24,6 +24,7 @@
 
 #include "../gui/SDL_Extensions.h"
 #include "../gui/CGuiHandler.h"
+#include "../gui/CAnimation.h"
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/CComponent.h"
 
@@ -107,7 +108,7 @@ CHeroWindow::CHeroWindow(const CGHeroInstance *hero):
 	quitButton = new CButton(Point(609, 516), "hsbtns.def", CButton::tooltip(heroscrn[17]), [&](){ close(); }, SDLK_RETURN);
 	quitButton->assignedKeys.insert(SDLK_ESCAPE);
 	dismissButton = new CButton(Point(454, 429), "hsbtns2.def", CButton::tooltip(heroscrn[28]), [&](){ dismissCurrent(); }, SDLK_d);
-	questlogButton = new CButton(Point(314, 429), "hsbtns4.def", CButton::tooltip(heroscrn[0]), [&](){ questlog(); }, SDLK_q);
+	questlogButton = new CButton(Point(314, 429), "hsbtns4.def", CButton::tooltip(heroscrn[0]), [=](){ LOCPLINT->showQuestLog(); }, SDLK_q);
 
 	formations = new CToggleGroup(0);
 	formations->addToggle(0, new CToggleButton(Point(481, 483), "hsbtns6.def", std::make_pair(heroscrn[23], heroscrn[29]), 0, SDLK_t));
@@ -144,11 +145,12 @@ CHeroWindow::CHeroWindow(const CGHeroInstance *hero):
 	luck = new MoraleLuckBox(false, Rect(233,179,53,45));
 	spellPointsArea = new LRClickableAreaWText(Rect(162,228, 136, 42), CGI->generaltexth->heroscrn[22]);
 
+	auto secSkills = std::make_shared<CAnimation>("SECSKILL");
 	for(int i = 0; i < std::min<size_t>(hero->secSkills.size(), 8u); ++i)
 	{
 		Rect r = Rect(i%2 == 0  ?  18  :  162,  276 + 48 * (i/2),  136,  42);
 		secSkillAreas.push_back(new LRClickableAreaWTextComp(r, CComponent::secskill));
-		secSkillImages.push_back(new CAnimImage("SECSKILL", 0, 0, r.x, r.y));
+		secSkillImages.push_back(new CAnimImage(secSkills, 0, 0, r.x, r.y));
 	}
 
 	//dismiss / quest log
@@ -158,12 +160,14 @@ CHeroWindow::CHeroWindow(const CGHeroInstance *hero):
 	//////////////////////////////////////////////////////////////////////////???????????????
 
 	//primary skills & exp and mana
-	new CAnimImage("PSKIL42", 0, 0, 32, 111, false);
-	new CAnimImage("PSKIL42", 1, 0, 102, 111, false);
-	new CAnimImage("PSKIL42", 2, 0, 172, 111, false);
-	new CAnimImage("PSKIL42", 3, 0, 162, 230, false);
-	new CAnimImage("PSKIL42", 4, 0, 20, 230, false);
-	new CAnimImage("PSKIL42", 5, 0, 242, 111, false);
+	auto primSkills = std::make_shared<CAnimation>("PSKIL42");
+	primSkills->preload();
+	new CAnimImage(primSkills, 0, 0, 32, 111);
+	new CAnimImage(primSkills, 1, 0, 102, 111);
+	new CAnimImage(primSkills, 2, 0, 172, 111);
+	new CAnimImage(primSkills, 3, 0, 162, 230);
+	new CAnimImage(primSkills, 4, 0, 20, 230);
+	new CAnimImage(primSkills, 5, 0, 242, 111);
 
 	// various texts
 	new CLabel( 52, 99, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->jktexts[1]);
@@ -306,11 +310,6 @@ void CHeroWindow::dismissCurrent()
 	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[22], ony, 0, false);
 }
 
-void CHeroWindow::questlog()
-{
-	LOCPLINT->showQuestLog();
-}
-
 void CHeroWindow::commanderWindow()
 {
 	//TODO: allow equipping commander artifacts by drag / drop

+ 0 - 1
client/windows/CHeroWindow.h

@@ -86,7 +86,6 @@ public:
 	void showAll(SDL_Surface * to) override;
 
 	void dismissCurrent(); //dissmissed currently displayed hero (curHero)
-	void questlog(); //show quest log in hero window
 	void commanderWindow();
 	void switchHero(); //changes displayed hero
 	virtual void updateGarrisons() override;  //updates the morale widget and calls the parent

+ 1 - 1
client/windows/CTradeWindow.cpp

@@ -187,7 +187,7 @@ void CTradeWindow::CTradeableItem::clickLeft(tribool down, bool previousState)
 				aw->arts->markPossibleSlots(art);
 
 				//aw->arts->commonInfo->dst.AOH = aw->arts;
-				CCS->curh->dragAndDropCursor(new CAnimImage("artifact", art->artType->iconIndex));
+				CCS->curh->dragAndDropCursor(make_unique<CAnimImage>("artifact", art->artType->iconIndex));
 
 				aw->arts->artifactsOnAltar.erase(art);
 				setID(-1);

+ 0 - 1
lib/CGameState.h

@@ -37,7 +37,6 @@ class CGObjectInstance;
 class CCreature;
 class CMap;
 struct StartInfo;
-struct SDL_Surface;
 class CMapHandler;
 struct SetObjectProperty;
 struct MetaString;