1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00

Implemented group attack animations for dragons/Hydras/etc

This commit is contained in:
Ivan Savenko 2022-12-14 14:21:58 +02:00
parent 814f6ed684
commit 7857668158
6 changed files with 115 additions and 101 deletions

View File

@ -92,6 +92,18 @@ BattleStackAnimation::BattleStackAnimation(BattleInterface & owner, const CStack
assert(myAnim);
}
ECreatureAnimType::Type AttackAnimation::findValidGroup( const std::vector<ECreatureAnimType::Type> candidates ) const
{
for ( auto group : candidates)
{
if(myAnim->framesInGroup(group) > 0)
return group;
}
assert(0);
return ECreatureAnimType::HOLDING;
}
void AttackAnimation::nextFrame()
{
if(myAnim->getType() != group)
@ -210,6 +222,42 @@ void DummyAnimation::nextFrame()
delete this;
}
ECreatureAnimType::Type MeleeAttackAnimation::getUpwardsGroup() const
{
if (!multiAttack)
return ECreatureAnimType::ATTACK_UP;
return findValidGroup({
ECreatureAnimType::GROUP_ATTACK_UP,
ECreatureAnimType::SPECIAL_UP,
ECreatureAnimType::ATTACK_UP
});
}
ECreatureAnimType::Type MeleeAttackAnimation::getForwardGroup() const
{
if (!multiAttack)
return ECreatureAnimType::ATTACK_FRONT;
return findValidGroup({
ECreatureAnimType::GROUP_ATTACK_FRONT,
ECreatureAnimType::SPECIAL_FRONT,
ECreatureAnimType::ATTACK_FRONT
});
}
ECreatureAnimType::Type MeleeAttackAnimation::getDownwardsGroup() const
{
if (!multiAttack)
return ECreatureAnimType::ATTACK_DOWN;
return findValidGroup({
ECreatureAnimType::GROUP_ATTACK_DOWN,
ECreatureAnimType::SPECIAL_DOWN,
ECreatureAnimType::ATTACK_DOWN
});
}
bool MeleeAttackAnimation::init()
{
assert(attackingStack);
@ -223,24 +271,14 @@ bool MeleeAttackAnimation::init()
logAnim->info("CMeleeAttackAnimation::init: stack %s -> stack %s", stack->getName(), defendingStack->getName());
static const ECreatureAnimType::Type mutPosToGroup[] =
const ECreatureAnimType::Type mutPosToGroup[] =
{
ECreatureAnimType::ATTACK_UP,
ECreatureAnimType::ATTACK_UP,
ECreatureAnimType::ATTACK_FRONT,
ECreatureAnimType::ATTACK_DOWN,
ECreatureAnimType::ATTACK_DOWN,
ECreatureAnimType::ATTACK_FRONT
};
static const ECreatureAnimType::Type mutPosToGroup2H[] =
{
ECreatureAnimType::VCMI_2HEX_UP,
ECreatureAnimType::VCMI_2HEX_UP,
ECreatureAnimType::VCMI_2HEX_FRONT,
ECreatureAnimType::VCMI_2HEX_DOWN,
ECreatureAnimType::VCMI_2HEX_DOWN,
ECreatureAnimType::VCMI_2HEX_FRONT
getUpwardsGroup(),
getUpwardsGroup(),
getForwardGroup(),
getDownwardsGroup(),
getDownwardsGroup(),
getForwardGroup()
};
int revShiftattacker = (attackingStack->side == BattleSide::ATTACKER ? -1 : 1);
@ -259,30 +297,9 @@ bool MeleeAttackAnimation::init()
mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn + revShiftattacker, defendingStack->occupiedHex());
}
assert(mutPos >= 0 && mutPos <=5);
switch(mutPos) //attack direction
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
group = mutPosToGroup[mutPos];
if(attackingStack->hasBonusOfType(Bonus::TWO_HEX_ATTACK_BREATH))
{
ECreatureAnimType::Type group2H = mutPosToGroup2H[mutPos];
if(myAnim->framesInGroup(group2H)>0)
group = group2H;
}
break;
default:
logGlobal->error("Critical Error! Wrong dest in stackAttacking! dest: %d; attacking stack pos: %d; mutual pos: %d", dest.hex, attackingStackPosBeforeReturn, mutPos);
group = ECreatureAnimType::ATTACK_FRONT;
break;
}
group = mutPosToGroup[mutPos];
return true;
}
@ -304,8 +321,9 @@ void MeleeAttackAnimation::playSound()
CCS->soundh->playSound(battle_sound(getCreature(), attack));
}
MeleeAttackAnimation::MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked)
: AttackAnimation(owner, attacker, _dest, _attacked)
MeleeAttackAnimation::MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack)
: AttackAnimation(owner, attacker, _dest, _attacked),
multiAttack(multiAttack)
{
logAnim->debug("Created melee attack anim for %s", attacker->getName());
}
@ -603,17 +621,17 @@ void FadingAnimation::nextFrame()
float delta = elapsed / fullTime;
progress += delta;
if (progress > 1.0f)
progress = 1.0f;
uint8_t factor = stack->cloned ? 128 : 255;
uint8_t blue = stack->cloned ? 128 : 0;
uint8_t alpha = CSDL_Ext::lerp(from, dest, progress);
ColorShifterMultiplyAndAdd shifterFade ({factor, factor, factor, alpha}, {0, 0, blue, 0});
stackAnimation(stack)->shiftColor(&shifterFade);
if (progress == 1.0f)
if (progress > 1.0f)
progress = 1.0f;
uint8_t factor = stack->cloned ? 128 : 255;
uint8_t blue = stack->cloned ? 128 : 0;
uint8_t alpha = CSDL_Ext::lerp(from, dest, progress);
ColorShifterMultiplyAndAdd shifterFade ({factor, factor, factor, alpha}, {0, 0, blue, 0});
stackAnimation(stack)->shiftColor(&shifterFade);
if (progress == 1.0f)
delete this;
}
@ -831,23 +849,11 @@ CastAnimation::CastAnimation(BattleInterface & owner_, const CStack * attacker,
dest = defender->getPosition();
}
ECreatureAnimType::Type CastAnimation::findValidGroup( const std::vector<ECreatureAnimType::Type> candidates ) const
{
for ( auto group : candidates)
{
if(myAnim->framesInGroup(group) > 0)
return group;
}
assert(0);
return ECreatureAnimType::HOLDING;
}
ECreatureAnimType::Type CastAnimation::getUpwardsGroup() const
{
return findValidGroup({
ECreatureAnimType::VCMI_CAST_UP,
ECreatureAnimType::CAST_UP,
ECreatureAnimType::SPECIAL_UP,
ECreatureAnimType::SHOOT_UP,
ECreatureAnimType::ATTACK_UP
});
@ -856,8 +862,8 @@ ECreatureAnimType::Type CastAnimation::getUpwardsGroup() const
ECreatureAnimType::Type CastAnimation::getForwardGroup() const
{
return findValidGroup({
ECreatureAnimType::VCMI_CAST_FRONT,
ECreatureAnimType::CAST_FRONT,
ECreatureAnimType::SPECIAL_FRONT,
ECreatureAnimType::SHOOT_FRONT,
ECreatureAnimType::ATTACK_FRONT
});
@ -866,8 +872,8 @@ ECreatureAnimType::Type CastAnimation::getForwardGroup() const
ECreatureAnimType::Type CastAnimation::getDownwardsGroup() const
{
return findValidGroup({
ECreatureAnimType::VCMI_CAST_DOWN,
ECreatureAnimType::CAST_DOWN,
ECreatureAnimType::SPECIAL_DOWN,
ECreatureAnimType::SHOOT_DOWN,
ECreatureAnimType::ATTACK_DOWN
});

View File

@ -76,6 +76,8 @@ protected:
int attackingStackPosBeforeReturn; //for stacks with return_after_strike feature
const CCreature * getCreature() const;
ECreatureAnimType::Type findValidGroup( const std::vector<ECreatureAnimType::Type> candidates ) const;
public:
virtual void playSound() = 0;
@ -127,6 +129,11 @@ public:
class MeleeAttackAnimation : public AttackAnimation
{
bool multiAttack;
ECreatureAnimType::Type getUpwardsGroup() const;
ECreatureAnimType::Type getForwardGroup() const;
ECreatureAnimType::Type getDownwardsGroup() const;
public:
bool init() override;
void nextFrame() override;
@ -278,7 +285,6 @@ class CastAnimation : public RangedAttackAnimation
{
const CSpell * spell;
ECreatureAnimType::Type findValidGroup( const std::vector<ECreatureAnimType::Type> candidates ) const;
ECreatureAnimType::Type getUpwardsGroup() const override;
ECreatureAnimType::Type getForwardGroup() const override;
ECreatureAnimType::Type getDownwardsGroup() const override;

View File

@ -73,9 +73,9 @@ enum Type // list of creature animations, numbers were taken from def files
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
SPECIAL_UP = 17, // If empty, fallback to SPECIAL_FRONT
SPECIAL_FRONT = 18, // Used for any special moves - dragon breath, spellcasting, (possibly - Pit Lord/Ogre Mage ability)
SPECIAL_DOWN = 19, // If empty, fallback to SPECIAL_FRONT
MOVE_START = 20, // small animation to be played before MOVING
MOVE_END = 21, // small animation to be played after MOVING
@ -83,11 +83,12 @@ enum Type // list of creature animations, numbers were taken from def files
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
RESURRECTION = 24, // new group, used for animating resurrection, if empty - reversed "DEATH" animation will be copiend here
VCMI_CAST_UP = 30,
VCMI_CAST_FRONT = 31,
VCMI_CAST_DOWN = 32,
VCMI_2HEX_UP = 40,
VCMI_2HEX_FRONT = 41,
VCMI_2HEX_DOWN = 42
CAST_UP = 30,
CAST_FRONT = 31,
CAST_DOWN = 32,
GROUP_ATTACK_UP = 40,
GROUP_ATTACK_FRONT = 41,
GROUP_ATTACK_DOWN = 42
};
}

View File

@ -513,6 +513,7 @@ void BattleStacksController::stackAttacking( const StackAttackInfo & info )
auto defender = info.defender;
auto tile = info.tile;
auto spellEffect = info.spellEffect;
auto multiAttack = !info.secondaryDefender.empty();
if (needsReverse)
{
@ -561,7 +562,7 @@ void BattleStacksController::stackAttacking( const StackAttackInfo & info )
}
else
{
addNewAnim(new MeleeAttackAnimation(owner, attacker, tile, defender));
addNewAnim(new MeleeAttackAnimation(owner, attacker, tile, defender, multiAttack));
}
});

View File

@ -74,12 +74,12 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
case ECreatureAnimType::SHOOT_UP:
case ECreatureAnimType::SHOOT_FRONT:
case ECreatureAnimType::SHOOT_DOWN:
case ECreatureAnimType::CAST_UP:
case ECreatureAnimType::CAST_FRONT:
case ECreatureAnimType::SPECIAL_UP:
case ECreatureAnimType::SPECIAL_FRONT:
case ECreatureAnimType::SPECIAL_DOWN:
case ECreatureAnimType::CAST_DOWN:
case ECreatureAnimType::VCMI_CAST_DOWN:
case ECreatureAnimType::VCMI_CAST_FRONT:
case ECreatureAnimType::VCMI_CAST_UP:
case ECreatureAnimType::CAST_FRONT:
case ECreatureAnimType::CAST_UP:
return static_cast<float>(speed * 4 * creature->animation.attackAnimationTime / anim->framesInGroup(type));
// as strange as it looks like "attackAnimationTime" does not affects melee attacks
@ -92,9 +92,9 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
case ECreatureAnimType::DEATH:
case ECreatureAnimType::DEATH_RANGED:
case ECreatureAnimType::RESURRECTION:
case ECreatureAnimType::VCMI_2HEX_DOWN:
case ECreatureAnimType::VCMI_2HEX_FRONT:
case ECreatureAnimType::VCMI_2HEX_UP:
case ECreatureAnimType::GROUP_ATTACK_DOWN:
case ECreatureAnimType::GROUP_ATTACK_FRONT:
case ECreatureAnimType::GROUP_ATTACK_UP:
return speed * 3 / anim->framesInGroup(type);
case ECreatureAnimType::TURN_L:
@ -315,19 +315,19 @@ static SDL_Color addColors(const SDL_Color & base, const SDL_Color & over)
ui8(over.a + base.a * (255 - over.a) / 256)
);
}
void CreatureAnimation::genSpecialPalette(IImage::SpecialPalette & target)
{
target[0] = genShadow(shadowAlpha / 2);
target[1] = genShadow(shadowAlpha / 2);
target[2] = genShadow(shadowAlpha);
target[3] = genShadow(shadowAlpha);
target[4] = genBorderColor(getBorderStrength(elapsedTime), border);
target[5] = addColors(genShadow(shadowAlpha), genBorderColor(getBorderStrength(elapsedTime), border));
target[6] = addColors(genShadow(shadowAlpha / 2), genBorderColor(getBorderStrength(elapsedTime), border));
}
void CreatureAnimation::nextFrame(Canvas & canvas, bool facingRight)
void CreatureAnimation::genSpecialPalette(IImage::SpecialPalette & target)
{
target[0] = genShadow(shadowAlpha / 2);
target[1] = genShadow(shadowAlpha / 2);
target[2] = genShadow(shadowAlpha);
target[3] = genShadow(shadowAlpha);
target[4] = genBorderColor(getBorderStrength(elapsedTime), border);
target[5] = addColors(genShadow(shadowAlpha), genBorderColor(getBorderStrength(elapsedTime), border));
target[6] = addColors(genShadow(shadowAlpha / 2), genBorderColor(getBorderStrength(elapsedTime), border));
}
void CreatureAnimation::nextFrame(Canvas & canvas, bool facingRight)
{
size_t frame = static_cast<size_t>(floor(currentFrame));

View File

@ -482,7 +482,7 @@ void CCreatureAnim::loopPreview(bool warMachine)
ECreatureAnimType::HITTED,
ECreatureAnimType::DEFENCE,
ECreatureAnimType::ATTACK_FRONT,
ECreatureAnimType::CAST_FRONT
ECreatureAnimType::SPECIAL_FRONT
};
static const ECreatureAnimType::Type machPreviewList[] = {
ECreatureAnimType::HOLDING,