mirror of
https://github.com/vcmi/vcmi.git
synced 2025-09-16 09:26:28 +02:00
Spell animation will now wait for hero animation before playing
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
#include "BattleAnimationClasses.h"
|
||||
|
||||
#include "BattleInterface.h"
|
||||
#include "BattleInterfaceClasses.h"
|
||||
#include "BattleProjectileController.h"
|
||||
#include "BattleSiegeController.h"
|
||||
#include "BattleFieldController.h"
|
||||
@@ -1055,35 +1056,81 @@ PointEffectAnimation::~PointEffectAnimation()
|
||||
assert(soundFinished);
|
||||
}
|
||||
|
||||
void WaitingProjectileAnimation::nextFrame()
|
||||
HeroCastAnimation::HeroCastAnimation(BattleInterface & owner, std::shared_ptr<BattleHero> hero, BattleHex dest, const CStack * defender, const CSpell * spell):
|
||||
BattleAnimation(owner),
|
||||
projectileEmitted(false),
|
||||
hero(hero),
|
||||
target(defender),
|
||||
tile(dest),
|
||||
spell(spell)
|
||||
{
|
||||
// initialization conditions fulfilled, delay is over
|
||||
if(owner.getAnimationCondition(EAnimationEvents::HIT) == false)
|
||||
owner.setAnimationCondition(EAnimationEvents::HIT, true);
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
WaitingProjectileAnimation::WaitingProjectileAnimation(BattleInterface & owner_, const CStack * shooter):
|
||||
BattleAnimation(owner_),
|
||||
shooter(shooter)
|
||||
{}
|
||||
|
||||
bool WaitingProjectileAnimation::init()
|
||||
bool HeroCastAnimation::init()
|
||||
{
|
||||
for(auto & elem : pendingAnimations())
|
||||
{
|
||||
auto * attackAnim = dynamic_cast<RangedAttackAnimation *>(elem);
|
||||
hero->setPhase(EHeroAnimType::CAST_SPELL);
|
||||
|
||||
if( attackAnim && shooter && attackAnim->stack->ID == shooter->ID && !attackAnim->isInitialized() )
|
||||
{
|
||||
// there is ongoing ranged attack that involves our stack, but projectile was not created yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
hero->onPhaseFinished([&](){
|
||||
assert(owner.getAnimationCondition(EAnimationEvents::HIT) == true);
|
||||
delete this;
|
||||
});
|
||||
|
||||
if(owner.projectilesController->hasActiveProjectile(shooter))
|
||||
return false;
|
||||
initializeProjectile();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HeroCastAnimation::initializeProjectile()
|
||||
{
|
||||
//spell has no projectile to play, ignore this step
|
||||
if (spell->animationInfo.projectile.empty())
|
||||
return;
|
||||
|
||||
Point srccoord = hero->pos.center();
|
||||
Point destcoord = owner.stacksController->getStackPositionAtHex(tile, target); //position attacked by projectile
|
||||
|
||||
destcoord += Point(222, 265); // FIXME: what are these constants?
|
||||
owner.projectilesController->createSpellProjectile( nullptr, srccoord, destcoord, spell);
|
||||
}
|
||||
|
||||
void HeroCastAnimation::emitProjectile()
|
||||
{
|
||||
if (projectileEmitted)
|
||||
return;
|
||||
|
||||
//spell has no projectile to play, skip this step and immediately play hit animations
|
||||
if (spell->animationInfo.projectile.empty())
|
||||
emitAnimationEvent();
|
||||
else
|
||||
owner.projectilesController->emitStackProjectile( nullptr );
|
||||
|
||||
projectileEmitted = true;
|
||||
}
|
||||
|
||||
void HeroCastAnimation::emitAnimationEvent()
|
||||
{
|
||||
if(owner.getAnimationCondition(EAnimationEvents::HIT) == false)
|
||||
owner.setAnimationCondition(EAnimationEvents::HIT, true);
|
||||
}
|
||||
|
||||
void HeroCastAnimation::nextFrame()
|
||||
{
|
||||
float frame = hero->getFrame();
|
||||
|
||||
if (frame < 4.0f)
|
||||
return;
|
||||
|
||||
if (!projectileEmitted)
|
||||
{
|
||||
emitProjectile();
|
||||
hero->pause();
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
hero->play();
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ class CSpell;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class BattleHero;
|
||||
class CAnimation;
|
||||
class BattleInterface;
|
||||
class CreatureAnimation;
|
||||
@@ -328,18 +329,20 @@ public:
|
||||
void nextFrame() override;
|
||||
};
|
||||
|
||||
class HeroAnimation : public BattleAnimation
|
||||
class HeroCastAnimation : public BattleAnimation
|
||||
{
|
||||
public:
|
||||
HeroAnimation(BattleInterface * owner_, const CStack * shooter);
|
||||
};
|
||||
std::shared_ptr<BattleHero> hero;
|
||||
const CStack * target;
|
||||
const CSpell * spell;
|
||||
BattleHex tile;
|
||||
bool projectileEmitted;
|
||||
|
||||
void initializeProjectile();
|
||||
void emitProjectile();
|
||||
void emitAnimationEvent();
|
||||
|
||||
/// Class that waits till projectile of certain shooter hits a target
|
||||
class WaitingProjectileAnimation : public BattleAnimation
|
||||
{
|
||||
const CStack * shooter;
|
||||
public:
|
||||
WaitingProjectileAnimation(BattleInterface & owner, const CStack * shooter);
|
||||
HeroCastAnimation(BattleInterface & owner, std::shared_ptr<BattleHero> hero, BattleHex dest, const CStack * defender, const CSpell * spell);
|
||||
|
||||
void nextFrame() override;
|
||||
bool init() override;
|
||||
|
@@ -510,21 +510,14 @@ void BattleInterface::spellCast(const BattleSpellCast * sc)
|
||||
});
|
||||
}
|
||||
else
|
||||
if (targetedTile.isValid() && !spell->animationInfo.projectile.empty())
|
||||
if (targetedTile.isValid())
|
||||
{
|
||||
// this is spell cast by hero with valid destination & valid projectile -> play animation
|
||||
auto hero = sc->side ? defendingHero : attackingHero;
|
||||
assert(hero);
|
||||
|
||||
const CStack * target = curInt->cb->battleGetStackByPos(targetedTile);
|
||||
Point srccoord = (sc->side ? Point(770, 60) : Point(30, 60)) + pos; //hero position
|
||||
Point destcoord = stacksController->getStackPositionAtHex(targetedTile, target); //position attacked by projectile
|
||||
destcoord += Point(250, 240); // FIXME: what are these constants?
|
||||
|
||||
//FIXME: should be replaced with new hero cast animation type
|
||||
executeOnAnimationCondition(EAnimationEvents::BEFORE_HIT, true, [=]()
|
||||
{
|
||||
projectilesController->createSpellProjectile( nullptr, srccoord, destcoord, spell);
|
||||
projectilesController->emitStackProjectile( nullptr );
|
||||
stacksController->addNewAnim(new WaitingProjectileAnimation(*this, nullptr));
|
||||
stacksController->addNewAnim(new HeroCastAnimation(*this, hero, targetedTile, curInt->cb->battleGetStackByPos(targetedTile), spell));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -770,10 +763,7 @@ void BattleInterface::startAction(const BattleAction* action)
|
||||
redraw(); // redraw after deactivation, including proper handling of hovered hexes
|
||||
|
||||
if(action->actionType == EActionType::HERO_SPELL) //when hero casts spell
|
||||
{
|
||||
setHeroAnimation(action->side, EHeroAnimType::CAST_SPELL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stack)
|
||||
{
|
||||
|
@@ -176,9 +176,8 @@ void BattleHero::render(Canvas & canvas)
|
||||
canvas.draw(flagFrame, flagPosition);
|
||||
canvas.draw(heroFrame, heroPosition);
|
||||
|
||||
//FIXME: un-hardcode speed
|
||||
flagCurrentFrame += 0.25f;
|
||||
currentFrame += 0.25f;
|
||||
flagCurrentFrame += currentSpeed;
|
||||
currentFrame += currentSpeed;
|
||||
|
||||
if(flagCurrentFrame >= flagAnimation->size(0))
|
||||
flagCurrentFrame -= flagAnimation->size(0);
|
||||
@@ -186,14 +185,21 @@ void BattleHero::render(Canvas & canvas)
|
||||
if(currentFrame >= animation->size(groupIndex))
|
||||
{
|
||||
currentFrame -= animation->size(groupIndex);
|
||||
if (phaseFinishedCallback)
|
||||
{
|
||||
phaseFinishedCallback();
|
||||
phaseFinishedCallback = std::function<void()>();
|
||||
}
|
||||
switchToNextPhase();
|
||||
}
|
||||
}
|
||||
|
||||
void BattleHero::pause()
|
||||
{
|
||||
currentSpeed = 0.f;
|
||||
}
|
||||
|
||||
void BattleHero::play()
|
||||
{
|
||||
//FIXME: un-hardcode speed
|
||||
currentSpeed = 0.25f;
|
||||
}
|
||||
|
||||
float BattleHero::getFrame() const
|
||||
{
|
||||
return currentFrame;
|
||||
@@ -266,11 +272,10 @@ void BattleHero::switchToNextPhase()
|
||||
{
|
||||
phase = nextPhase;
|
||||
currentFrame = 0.f;
|
||||
if (phaseFinishedCallback)
|
||||
{
|
||||
phaseFinishedCallback();
|
||||
phaseFinishedCallback = std::function<void()>();
|
||||
}
|
||||
|
||||
auto copy = phaseFinishedCallback;
|
||||
phaseFinishedCallback.clear();
|
||||
copy();
|
||||
}
|
||||
|
||||
BattleHero::BattleHero(const std::string & animationPath, bool flipG, PlayerColor player, const CGHeroInstance * hero, const BattleInterface & owner):
|
||||
@@ -279,6 +284,7 @@ BattleHero::BattleHero(const std::string & animationPath, bool flipG, PlayerColo
|
||||
owner(owner),
|
||||
phase(EHeroAnimType::HOLDING),
|
||||
nextPhase(EHeroAnimType::HOLDING),
|
||||
currentSpeed(0.f),
|
||||
currentFrame(0.f),
|
||||
flagCurrentFrame(0.f)
|
||||
{
|
||||
@@ -304,6 +310,7 @@ BattleHero::BattleHero(const std::string & animationPath, bool flipG, PlayerColo
|
||||
addUsedEvents(LCLICK | RCLICK | HOVER);
|
||||
|
||||
switchToNextPhase();
|
||||
play();
|
||||
}
|
||||
|
||||
HeroInfoWindow::HeroInfoWindow(const InfoAboutHero & hero, Point * position)
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "BattleConstants.h"
|
||||
#include "../gui/CIntObject.h"
|
||||
#include "../../lib/FunctionList.h"
|
||||
#include "../../lib/battle/BattleHex.h"
|
||||
#include "../windows/CWindowObject.h"
|
||||
|
||||
@@ -80,7 +81,8 @@ class BattleHero : public CIntObject
|
||||
{
|
||||
bool flip; //false if it's attacking hero, true otherwise
|
||||
|
||||
std::function<void()> phaseFinishedCallback;
|
||||
CFunctionList<void()> phaseFinishedCallback;
|
||||
|
||||
std::shared_ptr<CAnimation> animation;
|
||||
std::shared_ptr<CAnimation> flagAnimation;
|
||||
|
||||
@@ -90,6 +92,7 @@ class BattleHero : public CIntObject
|
||||
EHeroAnimType phase; //stage of animation
|
||||
EHeroAnimType nextPhase; //stage of animation to be set after current phase is fully displayed
|
||||
|
||||
float currentSpeed;
|
||||
float currentFrame; //frame of animation
|
||||
float flagCurrentFrame;
|
||||
|
||||
@@ -102,6 +105,9 @@ public:
|
||||
float getFrame() const;
|
||||
void onPhaseFinished(const std::function<void()> &);
|
||||
|
||||
void pause();
|
||||
void play();
|
||||
|
||||
void hover(bool on) override;
|
||||
void clickLeft(tribool down, bool previousState) override; //call-in
|
||||
void clickRight(tribool down, bool previousState) override; //call-in
|
||||
|
Reference in New Issue
Block a user