浏览代码

Added fading animation for fade-in effect for summons

Ivan Savenko 3 年之前
父节点
当前提交
fb3a08e0a6

+ 39 - 4
client/battle/BattleAnimationClasses.cpp

@@ -589,6 +589,42 @@ ResurrectionAnimation::ResurrectionAnimation(BattleInterface & owner, const CSta
 
 }
 
+bool FadingAnimation::init()
+{
+	logAnim->info("FadingAnimation::init: stack %s", stack->getName());
+	//TODO: pause animation?
+	return true;
+}
+
+void FadingAnimation::nextFrame()
+{
+	float elapsed  = GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
+	float fullTime = AnimationControls::getFadeInDuration();
+	float delta    = elapsed / fullTime;
+	progress += delta;
+
+	if (progress > 1.0f)
+		progress = 1.0f;
+
+	uint8_t blueFactor = stack->cloned ? 0 : 255;
+	uint8_t blueAdded  = stack->cloned ? 255 : 0;
+	uint8_t alpha = CSDL_Ext::lerp(from, dest, progress);
+
+	ColorShifterMultiplyAndAdd shifterFade ({255, 255, blueFactor, alpha}, {0, 0, blueAdded, 0});
+	stackAnimation(stack)->shiftColor(&shifterFade);
+
+	if (progress == 1.0f)
+		delete this;
+}
+
+FadingAnimation::FadingAnimation(BattleInterface & owner, const CStack * _stack, uint8_t alphaFrom, uint8_t alphaDest):
+	BattleStackAnimation(owner, _stack),
+	from(alphaFrom),
+	dest(alphaDest)
+{
+}
+
+
 RangedAttackAnimation::RangedAttackAnimation(BattleInterface & owner_, const CStack * attacker, BattleHex dest_, const CStack * defender)
 	: AttackAnimation(owner_, attacker, dest_, defender),
 	  projectileEmitted(false)
@@ -707,7 +743,6 @@ void RangedAttackAnimation::nextFrame()
 
 RangedAttackAnimation::~RangedAttackAnimation()
 {
-	//FIXME: this assert triggers under some unclear, rare conditions. Possibly - if game window is inactive and/or in foreground/minimized?
 	assert(!owner.projectilesController->hasActiveProjectile(attackingStack));
 	assert(projectileEmitted);
 
@@ -846,7 +881,7 @@ void CastAnimation::createProjectile(const Point & from, const Point & dest) con
 
 uint32_t CastAnimation::getAttackClimaxFrame() const
 {
-	//FIXME: allow defining this parameter in config file, separately from attackClimaxFrame of missile attacks
+	//TODO: allow defining this parameter in config file, separately from attackClimaxFrame of missile attacks
 	uint32_t maxFrames = stackAnimation(attackingStack)->framesInGroup(group);
 
 	if (maxFrames > 2)
@@ -1117,7 +1152,7 @@ void HeroCastAnimation::nextFrame()
 {
 	float frame = hero->getFrame();
 
-	if (frame < 4.0f)
+	if (frame < 4.0f) // middle point of animation //TODO: un-hardcode
 		return;
 
 	if (!projectileEmitted)
@@ -1130,7 +1165,7 @@ void HeroCastAnimation::nextFrame()
 	if (!owner.projectilesController->hasActiveProjectile(nullptr))
 	{
 		emitAnimationEvent();
-		//FIXME: check H3 - it is possible that hero animation should be paused until hit effect is over, not just projectile
+		//TODO: check H3 - it is possible that hero animation should be paused until hit effect is over, not just projectile
 		hero->play();
 	}
 }

+ 13 - 1
client/battle/BattleAnimationClasses.h

@@ -205,9 +205,21 @@ public:
 	ResurrectionAnimation(BattleInterface & owner, const CStack * _stack);
 };
 
-class RangedAttackAnimation : public AttackAnimation
+/// Performs fade-in or fade-out animation on stack
+class FadingAnimation : public BattleStackAnimation
 {
+	float progress;
+	uint8_t from;
+	uint8_t dest;
+public:
+	bool init() override;
+	void nextFrame() override;
 
+	FadingAnimation(BattleInterface & owner, const CStack * _stack, uint8_t alphaFrom, uint8_t alphaDest);
+};
+
+class RangedAttackAnimation : public AttackAnimation
+{
 	void setAnimationGroup();
 	void initializeProjectile();
 	void emitProjectile();

+ 9 - 9
client/battle/BattleConstants.h

@@ -62,7 +62,7 @@ enum Type // list of creature animations, numbers were taken from def files
 	HITTED          = 3,  // base animation for when stack is taking damage
 	DEFENCE         = 4,  // alternative animation for defending in melee if stack spent its action on defending
 	DEATH           = 5,
-	DEATH_RANGED    = 6,  // alternative animation for when stack is killed by ranged attack
+	DEATH_RANGED    = 6,  // Optional, alternative animation for when stack is killed by ranged attack
 	TURN_L          = 7,
 	TURN_R          = 8,
 	//TURN_L2       = 9,  //unused - identical to TURN_L
@@ -70,14 +70,14 @@ enum Type // list of creature animations, numbers were taken from def files
 	ATTACK_UP       = 11,
 	ATTACK_FRONT    = 12,
 	ATTACK_DOWN     = 13,
-	SHOOT_UP        = 14,
-	SHOOT_FRONT     = 15,
-	SHOOT_DOWN      = 16,
-	CAST_UP         = 17,
-	CAST_FRONT      = 18,
-	CAST_DOWN       = 19,
-	MOVE_START      = 20,  // small animation to be played before MOVING
-	MOVE_END        = 21,  // small animation to be played after MOVING
+	SHOOT_UP        = 14, // Shooters only
+	SHOOT_FRONT     = 15, // Shooters only
+	SHOOT_DOWN      = 16, // Shooters only
+	CAST_UP         = 17, // If empty, fallback to CAST_FRONT
+	CAST_FRONT      = 18, // Used for any special moves - dragon breath, spellcasting, (possibly - Pit Lord/Ogre Mage ability)
+	CAST_DOWN       = 19, // If empty, fallback to CAST_FRONT
+	MOVE_START      = 20, // small animation to be played before MOVING
+	MOVE_END        = 21, // small animation to be played after MOVING
 
 	DEAD            = 22, // new group, used to show dead stacks. If empty - last frame from "DEATH" will be copied here
 	DEAD_RANGED     = 23, // new group, used to show dead stacks (if DEATH_RANGED was used). If empty - last frame from "DEATH_RANGED" will be copied here

+ 0 - 1
client/battle/BattleEffectsController.cpp

@@ -73,7 +73,6 @@ void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bt
 	//don't show animation when no HP is regenerated
 	switch(bte.effect)
 	{
-		//TODO: move to bonus type handler
 		case Bonus::HP_REGENERATION:
 			displayEffect(EBattleEffect::REGENERATION, soundBase::REGENER, stack->getPosition());
 			break;

+ 2 - 2
client/battle/BattleInterface.cpp

@@ -318,7 +318,7 @@ void BattleInterface::stackReset(const CStack * stack)
 
 void BattleInterface::stackAdded(const CStack * stack)
 {
-	stacksController->stackAdded(stack);
+	stacksController->stackAdded(stack, false);
 }
 
 void BattleInterface::stackRemoved(uint32_t stackID)
@@ -328,7 +328,7 @@ void BattleInterface::stackRemoved(uint32_t stackID)
 	queue->update();
 }
 
-void BattleInterface::stackActivated(const CStack *stack) //TODO: check it all before game state is changed due to abilities
+void BattleInterface::stackActivated(const CStack *stack)
 {
 	stacksController->stackActivated(stack);
 }

+ 18 - 9
client/battle/BattleStacksController.cpp

@@ -89,7 +89,7 @@ BattleStacksController::BattleStacksController(BattleInterface & owner):
 	std::vector<const CStack*> stacks = owner.curInt->cb->battleGetAllStacks(true);
 	for(const CStack * s : stacks)
 	{
-		stackAdded(s);
+		stackAdded(s, true);
 	}
 }
 
@@ -172,17 +172,15 @@ void BattleStacksController::stackReset(const CStack * stack)
 		});
 	}
 
-	static const ColorShifterMultiplyAndAdd shifterClone ({255, 255, 0, 255}, {0, 0, 255, 0});
-
-	if (stack->isClone())
-	{
-		animation->shiftColor(&shifterClone);
-	}
-
+	//static const ColorShifterMultiplyAndAdd shifterClone ({255, 255, 0, 255}, {0, 0, 255, 0});
+	//if (stack->isClone())
+	//{
+	//	animation->shiftColor(&shifterClone);
+	//}
 	//owner.waitForAnimationCondition(EAnimationEvents::ACTION, false);
 }
 
-void BattleStacksController::stackAdded(const CStack * stack)
+void BattleStacksController::stackAdded(const CStack * stack, bool instant)
 {
 	// Tower shooters have only their upper half visible
 	static const int turretCreatureAnimationHeight = 235;
@@ -212,6 +210,17 @@ void BattleStacksController::stackAdded(const CStack * stack)
 	stackAnimation[stack->ID]->pos.y = coords.y;
 	stackAnimation[stack->ID]->pos.w = stackAnimation[stack->ID]->getWidth();
 	stackAnimation[stack->ID]->setType(ECreatureAnimType::HOLDING);
+
+	if (!instant)
+	{
+		ColorShifterMultiplyAndAdd shifterFade ({255, 255, 255, 0}, {0, 0, 0, 0});
+		stackAnimation[stack->ID]->shiftColor(&shifterFade);
+
+		owner.executeOnAnimationCondition(EAnimationEvents::HIT, true, [=]()
+		{
+			addNewAnim(new FadingAnimation(owner, stack, 0, 255));
+		});
+	}
 }
 
 void BattleStacksController::setActiveStack(const CStack *stack)

+ 1 - 1
client/battle/BattleStacksController.h

@@ -86,7 +86,7 @@ public:
 	bool facingRight(const CStack * stack) const;
 
 	void stackReset(const CStack * stack);
-	void stackAdded(const CStack * stack); //new stack appeared on battlefield
+	void stackAdded(const CStack * stack, bool instant); //new stack appeared on battlefield
 	void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled
 	void stackActivated(const CStack *stack); //active stack has been changed
 	void stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance); //stack with id number moved to destHex

+ 27 - 11
client/battle/CreatureAnimation.cpp

@@ -19,6 +19,11 @@ static const SDL_Color creatureBlueBorder = { 0, 255, 255, 255 };
 static const SDL_Color creatureGoldBorder = { 255, 255, 0, 255 };
 static const SDL_Color creatureNoBorder  =  { 0, 0, 0, 0 };
 
+static SDL_Color genShadow(ui8 alpha)
+{
+	return CSDL_Ext::makeColor(0, 0, 0, alpha);
+}
+
 SDL_Color AnimationControls::getBlueBorder()
 {
 	return creatureBlueBorder;
@@ -134,6 +139,11 @@ float AnimationControls::getFlightDistance(const CCreature * creature)
 	return static_cast<float>(creature->animation.flightAnimationDistance * 200);
 }
 
+float AnimationControls::getFadeInDuration()
+{
+	return 1.0f / settings["battle"]["animationSpeed"].Float();
+}
+
 ECreatureAnimType::Type CreatureAnimation::getType() const
 {
 	return type;
@@ -150,6 +160,10 @@ void CreatureAnimation::setType(ECreatureAnimType::Type type)
 
 void CreatureAnimation::shiftColor(const ColorShifter* shifter)
 {
+	SDL_Color shadowTest = shifter->shiftColor(genShadow(128));
+
+	shadowAlpha = shadowTest.a;
+
 	if(forward)
 		forward->shiftColor(shifter);
 
@@ -160,6 +174,7 @@ void CreatureAnimation::shiftColor(const ColorShifter* shifter)
 CreatureAnimation::CreatureAnimation(const std::string & name_, TSpeedController controller)
 	: name(name_),
 	  speed(0.1f),
+	  shadowAlpha(128),
 	  currentFrame(0),
 	  elapsedTime(0),
 	  type(ECreatureAnimType::HOLDING),
@@ -281,11 +296,6 @@ inline int getBorderStrength(float time)
 	return static_cast<int>(borderStrength * 155 + 100); // scale to 0-255
 }
 
-static SDL_Color genShadow(ui8 alpha)
-{
-	return CSDL_Ext::makeColor(0, 0, 0, alpha);
-}
-
 static SDL_Color genBorderColor(ui8 alpha, const SDL_Color & base)
 {
 	return CSDL_Ext::makeColor(base.r, base.g, base.b, ui8(base.a * alpha / 256));
@@ -306,11 +316,16 @@ static SDL_Color addColors(const SDL_Color & base, const SDL_Color & over)
 			);
 }
 
-void CreatureAnimation::genBorderPalette(IImage::BorderPallete & target)
+void CreatureAnimation::genSpecialPalette(IImage::SpecialPalette & target)
 {
 	target[0] = genBorderColor(getBorderStrength(elapsedTime), border);
-	target[1] = addColors(genShadow(128), genBorderColor(getBorderStrength(elapsedTime), border));
-	target[2] = addColors(genShadow(64),  genBorderColor(getBorderStrength(elapsedTime), border));
+	target[1] = genShadow(shadowAlpha / 2);
+	target[2] = genShadow(shadowAlpha / 2);
+	target[3] = genShadow(shadowAlpha);
+	target[4] = genShadow(shadowAlpha);
+	target[5] = genBorderColor(getBorderStrength(elapsedTime), border);
+	target[6] = addColors(genShadow(shadowAlpha),     genBorderColor(getBorderStrength(elapsedTime), border));
+	target[7] = addColors(genShadow(shadowAlpha / 2), genBorderColor(getBorderStrength(elapsedTime), border));
 }
 
 void CreatureAnimation::nextFrame(Canvas & canvas, bool facingRight)
@@ -326,12 +341,13 @@ void CreatureAnimation::nextFrame(Canvas & canvas, bool facingRight)
 
 	if(image)
 	{
-		IImage::BorderPallete borderPallete;
-		genBorderPalette(borderPallete);
+		IImage::SpecialPalette SpecialPalette;
+		genSpecialPalette(SpecialPalette);
 
-		image->setBorderPallete(borderPallete);
+		image->setSpecialPallete(SpecialPalette);
 
 		canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h));
+
 	}
 }
 

+ 7 - 1
client/battle/CreatureAnimation.h

@@ -46,6 +46,9 @@ namespace AnimationControls
 
 	/// Returns distance on which flying creatures should during one animation loop
 	float getFlightDistance(const CCreature * creature);
+
+	/// Returns total time for full fade-in effect on newly summoned creatures, in seconds
+	float getFadeInDuration();
 }
 
 /// Class which manages animations of creatures/units inside battles
@@ -80,6 +83,9 @@ private:
 	///type of animation being displayed
 	ECreatureAnimType::Type type;
 
+	/// current value of shadow transparency
+	uint8_t shadowAlpha;
+
 	/// border color, disabled if alpha = 0
 	SDL_Color border;
 
@@ -90,7 +96,7 @@ private:
 
 	void endAnimation();
 
-	void genBorderPalette(IImage::BorderPallete & target);
+	void genSpecialPalette(IImage::SpecialPalette & target);
 public:
 
 	/// function(s) that will be called when animation ends, after reset to 1st frame

+ 11 - 26
client/gui/CAnimation.cpp

@@ -108,7 +108,7 @@ public:
 	void adjustPalette(const ColorShifter * shifter) override;
 	void resetPalette() override;
 
-	void setBorderPallete(const BorderPallete & borderPallete) override;
+	void setSpecialPallete(const SpecialPalette & SpecialPalette) override;
 
 	friend class SDLImageLoader;
 
@@ -212,32 +212,17 @@ CDefFile::CDefFile(std::string Name):
 	data(nullptr),
 	palette(nullptr)
 {
-
-	#if 0
-	static SDL_Color H3_ORIG_PALETTE[8] =
-	{
-	   {  0, 255, 255, SDL_ALPHA_OPAQUE},
-	   {255, 150, 255, SDL_ALPHA_OPAQUE},
-	   {255, 100, 255, SDL_ALPHA_OPAQUE},
-	   {255,  50, 255, SDL_ALPHA_OPAQUE},
-	   {255,   0, 255, SDL_ALPHA_OPAQUE},
-	   {255, 255, 0,   SDL_ALPHA_OPAQUE},
-	   {180,   0, 255, SDL_ALPHA_OPAQUE},
-	   {  0, 255, 0,   SDL_ALPHA_OPAQUE}
-	};
-	#endif // 0
-
 	//First 8 colors in def palette used for transparency
 	static SDL_Color H3Palette[8] =
 	{
-		{   0,   0,   0,   0},// 100% - transparency
-		{   0,   0,   0,  32},//  75% - shadow border,
-		{   0,   0,   0,  64},// TODO: find exact value
-		{   0,   0,   0, 128},// TODO: for transparency
-		{   0,   0,   0, 128},//  50% - shadow body
-		{   0,   0,   0,   0},// 100% - selection highlight
-		{   0,   0,   0, 128},//  50% - shadow body   below selection
-		{   0,   0,   0,  64} // 75% - shadow border below selection
+		{   0,   0,   0,   0},// transparency                  ( used in most images )
+		{   0,   0,   0,  64},// shadow border                 ( used in battle, adventure map def's )
+		{   0,   0,   0,  64},// shadow border                 ( used in fog-of-war def's )
+		{   0,   0,   0, 128},// shadow body                   ( used in fog-of-war def's )
+		{   0,   0,   0, 128},// shadow body                   ( used in battle, adventure map def's )
+		{   0,   0,   0,   0},// selection                     ( used in battle def's )
+		{   0,   0,   0, 128},// shadow body   below selection ( used in battle def's )
+		{   0,   0,   0,  64} // shadow border below selection ( used in battle def's )
 	};
 	data = animationCache.getCachedFile(ResourceID(std::string("SPRITES/") + Name, EResType::ANIMATION));
 
@@ -827,11 +812,11 @@ void SDLImage::resetPalette()
 	SDL_SetPaletteColors(surf->format->palette, originalPalette->colors, 0, originalPalette->ncolors);
 }
 
-void SDLImage::setBorderPallete(const IImage::BorderPallete & borderPallete)
+void SDLImage::setSpecialPallete(const IImage::SpecialPalette & SpecialPalette)
 {
 	if(surf->format->palette)
 	{
-		SDL_SetColors(surf, const_cast<SDL_Color *>(borderPallete.data()), 5, 3);
+		SDL_SetColors(surf, const_cast<SDL_Color *>(SpecialPalette.data()), 1, 7);
 	}
 }
 

+ 3 - 3
client/gui/CAnimation.h

@@ -37,7 +37,7 @@ class ColorShifter;
 class IImage
 {
 public:
-	using BorderPallete = std::array<SDL_Color, 3>;
+	using SpecialPalette = std::array<SDL_Color, 7>;
 
 	//draws image on surface "where" at position
 	virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr, ui8 alpha = 255) const=0;
@@ -65,8 +65,8 @@ public:
 	virtual void adjustPalette(const ColorShifter * shifter) = 0;
 	virtual void resetPalette() = 0;
 
-	//only indexed bitmaps, colors 5,6,7 must be special
-	virtual void setBorderPallete(const BorderPallete & borderPallete) = 0;
+	//only indexed bitmaps with 7 special colors
+	virtual void setSpecialPallete(const SpecialPalette & SpecialPalette) = 0;
 
 	virtual void horizontalFlip() = 0;
 	virtual void verticalFlip() = 0;

+ 7 - 0
client/gui/Canvas.cpp

@@ -52,6 +52,13 @@ void Canvas::draw(std::shared_ptr<IImage> image, const Point & pos, const Rect &
 		image->draw(surface, pos.x, pos.y, &sourceRect);
 }
 
+void Canvas::draw(std::shared_ptr<IImage> image, const Point & pos, const Rect & sourceRect, uint8_t alpha)
+{
+	assert(image);
+	if (image)
+		image->draw(surface, pos.x, pos.y, &sourceRect, alpha);
+}
+
 void Canvas::draw(Canvas & image, const Point & pos)
 {
 	blitAt(image.surface, pos.x, pos.y, surface);

+ 4 - 0
client/gui/Canvas.h

@@ -41,6 +41,10 @@ public:
 	/// renders section of image bounded by sourceRect at specified position
 	void draw(std::shared_ptr<IImage> image, const Point & pos, const Rect & sourceRect);
 
+	/// renders section of image bounded by sourceRect at specified position at specific transparency value
+	void draw(std::shared_ptr<IImage> image, const Point & pos, const Rect & sourceRect, uint8_t alpha);
+
+
 	/// renders another canvas onto this canvas
 	void draw(Canvas & image, const Point & pos);
 

+ 0 - 5
config/spells/other.json

@@ -592,7 +592,6 @@
 		"index" : 65,
 		"targetType" : "CREATURE",
 		"animation":{
-			"cast":[2]
 		},
 		"sounds": {
 			"cast": "CLONE"
@@ -636,7 +635,6 @@
 		"index" : 66,
 		"targetType" : "NO_TARGET",
 		"animation":{
-			"cast":[2]
 		},
 		"sounds": {
 			"cast": "SUMNELM"
@@ -662,7 +660,6 @@
 		"index" : 67,
 		"targetType" : "NO_TARGET",
 		"animation":{
-			"cast":[2]
 		},
 		"sounds": {
 			"cast": "SUMNELM"
@@ -688,7 +685,6 @@
 		"index" : 68,
 		"targetType" : "NO_TARGET",
 		"animation":{
-			"cast":[2]
 		},
 		"sounds": {
 			"cast": "SUMNELM"
@@ -714,7 +710,6 @@
 		"index" : 69,
 		"targetType" : "NO_TARGET",
 		"animation":{
-			"cast":[2]
 		},
 		"sounds": {
 			"cast": "SUMNELM"