mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-12 02:28:11 +02:00
Moved all animation ordering logic to callers
Previously, CBattleAnimation & inheritors were controlling animation ordering - e.g. which animations should play after which. Now, this is controlled by caller, e.g. BattleInterface & its controllers. H3 animations are fairly linear and can be split in stages which are already somewhat implemented via waitForAnims
This commit is contained in:
parent
e750bd2713
commit
c79634b6a7
@ -80,7 +80,7 @@ public:
|
||||
//void actionFinished(const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero
|
||||
//void actionStarted(const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero
|
||||
//void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack
|
||||
//void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) override; //called when stack receives damage (after battleAttack())
|
||||
//void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged) override; //called when stack receives damage (after battleAttack())
|
||||
//void battleEnd(const BattleResult *br) override;
|
||||
//void battleResultsApplied() override; //called when all effects of last battle are applied
|
||||
//void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied;
|
||||
|
@ -177,7 +177,7 @@ void CStupidAI::battleAttack(const BattleAttack *ba)
|
||||
print("battleAttack called");
|
||||
}
|
||||
|
||||
void CStupidAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa)
|
||||
void CStupidAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged)
|
||||
{
|
||||
print("battleStacksAttacked called");
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ public:
|
||||
BattleAction activeStack(const CStack * stack) override; //called when it's turn of that stack
|
||||
|
||||
void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack
|
||||
void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) override; //called when stack receives damage (after battleAttack())
|
||||
void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged) override; //called when stack receives damage (after battleAttack())
|
||||
void battleEnd(const BattleResult *br) override;
|
||||
//void battleResultsApplied() override; //called when all effects of last battle are applied
|
||||
void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied;
|
||||
|
@ -944,7 +944,7 @@ void CPlayerInterface::battleTriggerEffect (const BattleTriggerEffect & bte)
|
||||
RETURN_IF_QUICK_COMBAT;
|
||||
battleInt->effectsController->battleTriggerEffect(bte);
|
||||
}
|
||||
void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa)
|
||||
void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
BATTLE_EVENT_POSSIBLE_RETURN;
|
||||
@ -954,24 +954,28 @@ void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacke
|
||||
{
|
||||
const CStack * defender = cb->battleGetStackByID(elem.stackAttacked, false);
|
||||
const CStack * attacker = cb->battleGetStackByID(elem.attackerID, false);
|
||||
if(elem.isEffect())
|
||||
{
|
||||
if(defender && !elem.isSecondary())
|
||||
battleInt->effectsController->displayEffect(EBattleEffect::EBattleEffect(elem.effect), defender->getPosition());
|
||||
}
|
||||
if(elem.isSpell())
|
||||
{
|
||||
if(defender)
|
||||
battleInt->displaySpellEffect(elem.spellID, defender->getPosition());
|
||||
}
|
||||
//FIXME: why action is deleted during enchanter cast?
|
||||
bool remoteAttack = false;
|
||||
|
||||
if(LOCPLINT->curAction)
|
||||
remoteAttack |= LOCPLINT->curAction->actionType != EActionType::WALK_AND_ATTACK;
|
||||
assert(defender);
|
||||
|
||||
StackAttackedInfo to_put = {defender, elem.damageAmount, elem.killedAmount, attacker, remoteAttack, elem.killed(), elem.willRebirth(), elem.cloneKilled()};
|
||||
arg.push_back(to_put);
|
||||
StackAttackedInfo info;
|
||||
info.defender = defender;
|
||||
info.attacker = attacker;
|
||||
info.damageDealt = elem.damageAmount;
|
||||
info.amountKilled = elem.killedAmount;
|
||||
info.battleEffect = EBattleEffect::INVALID;
|
||||
info.spellEffect = SpellID::NONE;
|
||||
info.indirectAttack = ranged;
|
||||
info.killed = elem.killed();
|
||||
info.rebirth = elem.willRebirth();
|
||||
info.cloneKilled = elem.cloneKilled();
|
||||
|
||||
if(elem.isEffect() && !elem.isSecondary())
|
||||
info.battleEffect = EBattleEffect::EBattleEffect(elem.effect);
|
||||
|
||||
if (elem.isSpell())
|
||||
info.spellEffect = elem.spellID;
|
||||
|
||||
arg.push_back(info);
|
||||
}
|
||||
battleInt->stacksAreAttacked(arg);
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ public:
|
||||
void battleSpellCast(const BattleSpellCast *sc) override;
|
||||
void battleStacksEffectsSet(const SetStackEffect & sse) override; //called when a specific effect is set to stacks
|
||||
void battleTriggerEffect(const BattleTriggerEffect & bte) override; //various one-shot effect
|
||||
void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) override;
|
||||
void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged) override;
|
||||
void battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) override; //called by engine just before battle starts; side=0 - left, side=1 - right
|
||||
void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right
|
||||
void battleUnitsChanged(const std::vector<UnitChanges> & units, const std::vector<CustomEffectInfo> & customEffects) override;
|
||||
|
@ -748,7 +748,7 @@ void BattleAttack::applyFirstCl(CClient *cl)
|
||||
|
||||
void BattleAttack::applyCl(CClient *cl)
|
||||
{
|
||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, bsa);
|
||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, bsa, shot());
|
||||
}
|
||||
|
||||
void StartAction::applyFirstCl(CClient *cl)
|
||||
@ -770,7 +770,7 @@ void SetStackEffect::applyCl(CClient *cl)
|
||||
|
||||
void StacksInjured::applyCl(CClient *cl)
|
||||
{
|
||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, stacks);
|
||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, stacks, false);
|
||||
}
|
||||
|
||||
void BattleResultsApplied::applyCl(CClient *cl)
|
||||
|
@ -90,32 +90,6 @@ void CBattleAnimation::setStackFacingRight(const CStack * stack, bool facingRigh
|
||||
owner.stacksController->stackFacingRight[stack->ID] = facingRight;
|
||||
}
|
||||
|
||||
bool CBattleAnimation::checkInitialConditions()
|
||||
{
|
||||
int lowestMoveID = ID;
|
||||
auto * thAnim = dynamic_cast<CBattleStackAnimation *>(this);
|
||||
auto * thSen = dynamic_cast<CPointEffectAnimation *>(this);
|
||||
|
||||
for(auto & elem : pendingAnimations())
|
||||
{
|
||||
auto * sen = dynamic_cast<CPointEffectAnimation *>(elem);
|
||||
|
||||
// all effect animations can play concurrently with each other
|
||||
if(sen && thSen && sen != thSen)
|
||||
continue;
|
||||
|
||||
auto * revAnim = dynamic_cast<CReverseAnimation *>(elem);
|
||||
|
||||
// if there is high-priority reverse animation affecting our stack then this animation will wait
|
||||
if(revAnim && thAnim && revAnim && revAnim->stack->ID == thAnim->stack->ID && revAnim->priority)
|
||||
return false;
|
||||
|
||||
if(elem)
|
||||
vstd::amin(lowestMoveID, elem->ID);
|
||||
}
|
||||
return ID == lowestMoveID;
|
||||
}
|
||||
|
||||
CBattleStackAnimation::CBattleStackAnimation(BattleInterface & owner, const CStack * stack)
|
||||
: CBattleAnimation(owner),
|
||||
myAnim(stackAnimation(stack)),
|
||||
@ -153,22 +127,6 @@ CAttackAnimation::~CAttackAnimation()
|
||||
myAnim->setType(ECreatureAnimType::HOLDING);
|
||||
}
|
||||
|
||||
bool CAttackAnimation::checkInitialConditions()
|
||||
{
|
||||
for(auto & elem : pendingAnimations())
|
||||
{
|
||||
CBattleStackAnimation * stAnim = dynamic_cast<CBattleStackAnimation *>(elem);
|
||||
CReverseAnimation * revAnim = dynamic_cast<CReverseAnimation *>(stAnim);
|
||||
|
||||
if(revAnim && attackedStack) // enemy must be fully reversed
|
||||
{
|
||||
if (revAnim->stack->ID == attackedStack->ID)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return CBattleAnimation::checkInitialConditions();
|
||||
}
|
||||
|
||||
const CCreature * CAttackAnimation::getCreature() const
|
||||
{
|
||||
if (attackingStack->getCreature()->idNumber == CreatureID::ARROW_TOWERS)
|
||||
@ -202,46 +160,6 @@ CDefenceAnimation::CDefenceAnimation(StackAttackedInfo _attackedInfo, BattleInte
|
||||
|
||||
bool CDefenceAnimation::init()
|
||||
{
|
||||
ui32 lowestMoveID = ID;
|
||||
for(auto & elem : pendingAnimations())
|
||||
{
|
||||
|
||||
auto * defAnim = dynamic_cast<CDefenceAnimation *>(elem);
|
||||
if(defAnim && defAnim->stack->ID != stack->ID)
|
||||
continue;
|
||||
|
||||
auto * attAnim = dynamic_cast<CAttackAnimation *>(elem);
|
||||
if(attAnim && attAnim->stack->ID != stack->ID)
|
||||
continue;
|
||||
|
||||
auto * sen = dynamic_cast<CPointEffectAnimation *>(elem);
|
||||
if (sen && attacker == nullptr)
|
||||
return false;
|
||||
|
||||
if (sen)
|
||||
continue;
|
||||
|
||||
CReverseAnimation * animAsRev = dynamic_cast<CReverseAnimation *>(elem);
|
||||
|
||||
if(animAsRev)
|
||||
return false;
|
||||
|
||||
if(elem)
|
||||
vstd::amin(lowestMoveID, elem->ID);
|
||||
}
|
||||
|
||||
if(ID > lowestMoveID)
|
||||
return false;
|
||||
|
||||
|
||||
//reverse unit if necessary
|
||||
if(attacker && owner.getCurrentPlayerInterface()->cb->isToReverse(stack->getPosition(), attacker->getPosition(), stackFacingRight(stack), attacker->doubleWide(), stackFacingRight(attacker)))
|
||||
{
|
||||
owner.stacksController->addNewAnim(new CReverseAnimation(owner, stack, stack->getPosition(), true));
|
||||
return false;
|
||||
}
|
||||
//unit reversed
|
||||
|
||||
if(rangedAttack && attacker != nullptr && owner.projectilesController->hasActiveProjectile(attacker)) //delay hit animation
|
||||
{
|
||||
return false;
|
||||
@ -256,6 +174,7 @@ bool CDefenceAnimation::init()
|
||||
|
||||
timeToWait = myAnim->framesInGroup(getMyAnimType()) * frameLength / 2;
|
||||
|
||||
//FIXME: perhaps this should be pause instead?
|
||||
myAnim->setType(ECreatureAnimType::HOLDING);
|
||||
}
|
||||
else
|
||||
@ -349,9 +268,6 @@ void CDummyAnimation::nextFrame()
|
||||
|
||||
bool CMeleeAttackAnimation::init()
|
||||
{
|
||||
if(!CAttackAnimation::checkInitialConditions())
|
||||
return false;
|
||||
|
||||
if(!attackingStack || myAnim->isDeadOrDying())
|
||||
{
|
||||
delete this;
|
||||
@ -362,7 +278,7 @@ bool CMeleeAttackAnimation::init()
|
||||
|
||||
if(toReverse)
|
||||
{
|
||||
owner.stacksController->addNewAnim(new CReverseAnimation(owner, stack, attackingStackPosBeforeReturn, true));
|
||||
owner.stacksController->addNewAnim(new CReverseAnimation(owner, stack, attackingStackPosBeforeReturn));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -453,8 +369,8 @@ CStackMoveAnimation::CStackMoveAnimation(BattleInterface & owner, const CStack *
|
||||
|
||||
bool CMovementAnimation::init()
|
||||
{
|
||||
if( !CBattleAnimation::checkInitialConditions() )
|
||||
return false;
|
||||
assert(stack);
|
||||
assert(!myAnim->isDeadOrDying());
|
||||
|
||||
if(!stack || myAnim->isDeadOrDying())
|
||||
{
|
||||
@ -473,17 +389,9 @@ bool CMovementAnimation::init()
|
||||
//reverse unit if necessary
|
||||
if(owner.stacksController->shouldRotate(stack, oldPos, currentHex))
|
||||
{
|
||||
// it seems that H3 does NOT plays full rotation animation here in most situations
|
||||
// it seems that H3 does NOT plays full rotation animation during movement
|
||||
// Logical since it takes quite a lot of time
|
||||
if (curentMoveIndex == 0) // full rotation only for moving towards first tile.
|
||||
{
|
||||
owner.stacksController->addNewAnim(new CReverseAnimation(owner, stack, oldPos, true));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
rotateStack(oldPos);
|
||||
}
|
||||
rotateStack(oldPos);
|
||||
}
|
||||
|
||||
if(myAnim->getType() != ECreatureAnimType::MOVING)
|
||||
@ -554,7 +462,6 @@ CMovementAnimation::~CMovementAnimation()
|
||||
assert(stack);
|
||||
|
||||
myAnim->pos = owner.stacksController->getStackPositionAtHex(currentHex, stack);
|
||||
owner.stacksController->addNewAnim(new CMovementEndAnimation(owner, stack, currentHex));
|
||||
|
||||
if(owner.moveSoundHander != -1)
|
||||
{
|
||||
@ -584,11 +491,10 @@ CMovementEndAnimation::CMovementEndAnimation(BattleInterface & owner, const CSta
|
||||
|
||||
bool CMovementEndAnimation::init()
|
||||
{
|
||||
//if( !isEarliest(true) )
|
||||
// return false;
|
||||
assert(stack);
|
||||
assert(!myAnim->isDeadOrDying());
|
||||
|
||||
if(!stack || myAnim->framesInGroup(ECreatureAnimType::MOVE_END) == 0 ||
|
||||
myAnim->isDeadOrDying())
|
||||
if(!stack || myAnim->isDeadOrDying())
|
||||
{
|
||||
delete this;
|
||||
return false;
|
||||
@ -596,8 +502,13 @@ bool CMovementEndAnimation::init()
|
||||
|
||||
CCS->soundh->playSound(battle_sound(stack->getCreature(), endMoving));
|
||||
|
||||
myAnim->setType(ECreatureAnimType::MOVE_END);
|
||||
if(!myAnim->framesInGroup(ECreatureAnimType::MOVE_END))
|
||||
{
|
||||
delete this;
|
||||
return false;
|
||||
}
|
||||
|
||||
myAnim->setType(ECreatureAnimType::MOVE_END);
|
||||
myAnim->onAnimationReset += [&](){ delete this; };
|
||||
|
||||
return true;
|
||||
@ -619,8 +530,8 @@ CMovementStartAnimation::CMovementStartAnimation(BattleInterface & owner, const
|
||||
|
||||
bool CMovementStartAnimation::init()
|
||||
{
|
||||
if( !CBattleAnimation::checkInitialConditions() )
|
||||
return false;
|
||||
assert(stack);
|
||||
assert(!myAnim->isDeadOrDying());
|
||||
|
||||
if(!stack || myAnim->isDeadOrDying())
|
||||
{
|
||||
@ -629,15 +540,20 @@ bool CMovementStartAnimation::init()
|
||||
}
|
||||
|
||||
CCS->soundh->playSound(battle_sound(stack->getCreature(), startMoving));
|
||||
|
||||
if(!myAnim->framesInGroup(ECreatureAnimType::MOVE_START))
|
||||
{
|
||||
delete this;
|
||||
return false;
|
||||
}
|
||||
|
||||
myAnim->setType(ECreatureAnimType::MOVE_START);
|
||||
myAnim->onAnimationReset += [&](){ delete this; };
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CReverseAnimation::CReverseAnimation(BattleInterface & owner, const CStack * stack, BattleHex dest, bool _priority)
|
||||
: CStackMoveAnimation(owner, stack, dest),
|
||||
priority(_priority)
|
||||
CReverseAnimation::CReverseAnimation(BattleInterface & owner, const CStack * stack, BattleHex dest)
|
||||
: CStackMoveAnimation(owner, stack, dest)
|
||||
{
|
||||
logAnim->debug("Created reverse anim for %s", stack->getName());
|
||||
}
|
||||
@ -650,9 +566,6 @@ bool CReverseAnimation::init()
|
||||
return false; //there is no such creature
|
||||
}
|
||||
|
||||
if(!priority && !CBattleAnimation::checkInitialConditions())
|
||||
return false;
|
||||
|
||||
if(myAnim->framesInGroup(ECreatureAnimType::TURN_L))
|
||||
{
|
||||
myAnim->setType(ECreatureAnimType::TURN_L);
|
||||
@ -699,9 +612,6 @@ void CReverseAnimation::setupSecondPart()
|
||||
|
||||
bool CResurrectionAnimation::init()
|
||||
{
|
||||
if( !CBattleAnimation::checkInitialConditions() )
|
||||
return false;
|
||||
|
||||
if(!stack)
|
||||
{
|
||||
delete this;
|
||||
@ -734,9 +644,6 @@ CRangedAttackAnimation::CRangedAttackAnimation(BattleInterface & owner, const CS
|
||||
|
||||
bool CRangedAttackAnimation::init()
|
||||
{
|
||||
if( !CAttackAnimation::checkInitialConditions() )
|
||||
return false;
|
||||
|
||||
assert(attackingStack);
|
||||
assert(!myAnim->isDeadOrDying());
|
||||
|
||||
@ -748,13 +655,6 @@ bool CRangedAttackAnimation::init()
|
||||
return false;
|
||||
}
|
||||
|
||||
//reverse unit if necessary
|
||||
if (attackingStack && attackedStack && owner.getCurrentPlayerInterface()->cb->isToReverse(attackingStack->getPosition(), attackedStack->getPosition(), stackFacingRight(attackingStack), attackingStack->doubleWide(), stackFacingRight(attackedStack)))
|
||||
{
|
||||
owner.stacksController->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->getPosition(), true));
|
||||
return false;
|
||||
}
|
||||
|
||||
logAnim->info("Ranged attack animation initialized");
|
||||
setAnimationGroup();
|
||||
initializeProjectile();
|
||||
@ -820,17 +720,6 @@ void CRangedAttackAnimation::emitProjectile()
|
||||
|
||||
void CRangedAttackAnimation::nextFrame()
|
||||
{
|
||||
for(auto & it : pendingAnimations())
|
||||
{
|
||||
CMovementStartAnimation * anim = dynamic_cast<CMovementStartAnimation *>(it);
|
||||
CReverseAnimation * anim2 = dynamic_cast<CReverseAnimation *>(it);
|
||||
if( (anim && anim->stack->ID == stack->ID) || (anim2 && anim2->stack->ID == stack->ID && anim2->priority ) )
|
||||
{
|
||||
assert(0); // FIXME: our stack started to move even though we are playing shooting animation? How?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// animation should be paused if there is an active projectile
|
||||
if (projectileEmitted)
|
||||
{
|
||||
@ -1056,9 +945,6 @@ CPointEffectAnimation::CPointEffectAnimation(BattleInterface & owner, soundBase:
|
||||
|
||||
bool CPointEffectAnimation::init()
|
||||
{
|
||||
if(!CBattleAnimation::checkInitialConditions())
|
||||
return false;
|
||||
|
||||
animation->preload();
|
||||
|
||||
auto first = animation->getImage(0, 0, true);
|
||||
|
@ -44,7 +44,6 @@ protected:
|
||||
void setStackFacingRight(const CStack * stack, bool facingRight);
|
||||
|
||||
virtual bool init() = 0; //to be called - if returned false, call again until returns true
|
||||
bool checkInitialConditions(); //determines if this animation is earliest of all
|
||||
|
||||
public:
|
||||
ui32 ID; //unique identifier
|
||||
@ -61,7 +60,7 @@ public:
|
||||
class CBattleStackAnimation : public CBattleAnimation
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<CreatureAnimation> myAnim; //animation for our stack, managed by CBattleInterface
|
||||
std::shared_ptr<CreatureAnimation> myAnim; //animation for our stack, managed by BattleInterface
|
||||
const CStack * stack; //id of stack whose animation it is
|
||||
|
||||
CBattleStackAnimation(BattleInterface & owner, const CStack * _stack);
|
||||
@ -86,7 +85,6 @@ protected:
|
||||
const CCreature * getCreature() const;
|
||||
public:
|
||||
void nextFrame() override;
|
||||
bool checkInitialConditions();
|
||||
|
||||
CAttackAnimation(BattleInterface & owner, const CStack *attacker, BattleHex _dest, const CStack *defender);
|
||||
~CAttackAnimation();
|
||||
@ -190,12 +188,11 @@ public:
|
||||
class CReverseAnimation : public CStackMoveAnimation
|
||||
{
|
||||
public:
|
||||
bool priority; //true - high, false - low
|
||||
bool init() override;
|
||||
|
||||
void setupSecondPart();
|
||||
|
||||
CReverseAnimation(BattleInterface & owner, const CStack * stack, BattleHex dest, bool _priority);
|
||||
CReverseAnimation(BattleInterface & owner, const CStack * stack, BattleHex dest);
|
||||
~CReverseAnimation();
|
||||
};
|
||||
|
||||
|
@ -9,6 +9,27 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace EBattleEffect
|
||||
{
|
||||
enum EBattleEffect
|
||||
{
|
||||
// list of battle effects that have hardcoded triggers
|
||||
FEAR = 15,
|
||||
GOOD_LUCK = 18,
|
||||
GOOD_MORALE = 20,
|
||||
BAD_MORALE = 30,
|
||||
BAD_LUCK = 48,
|
||||
RESURRECT = 50,
|
||||
DRAIN_LIFE = 52, // hardcoded constant in CGameHandler
|
||||
POISON = 67,
|
||||
DEATH_BLOW = 73,
|
||||
REGENERATION = 74,
|
||||
MANA_DRAIN = 77,
|
||||
|
||||
INVALID = -1,
|
||||
};
|
||||
}
|
||||
|
||||
namespace EHeroAnimType
|
||||
{
|
||||
enum Type
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../lib/battle/BattleHex.h"
|
||||
#include "BattleConstants.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -25,27 +26,6 @@ class BattleInterface;
|
||||
class BattleRenderer;
|
||||
class CPointEffectAnimation;
|
||||
|
||||
namespace EBattleEffect
|
||||
{
|
||||
enum EBattleEffect
|
||||
{
|
||||
// list of battle effects that have hardcoded triggers
|
||||
FEAR = 15,
|
||||
GOOD_LUCK = 18,
|
||||
GOOD_MORALE = 20,
|
||||
BAD_MORALE = 30,
|
||||
BAD_LUCK = 48,
|
||||
RESURRECT = 50,
|
||||
DRAIN_LIFE = 52, // hardcoded constant in CGameHandler
|
||||
POISON = 67,
|
||||
DEATH_BLOW = 73,
|
||||
REGENERATION = 74,
|
||||
MANA_DRAIN = 77,
|
||||
|
||||
INVALID = -1,
|
||||
};
|
||||
}
|
||||
|
||||
/// Struct for battle effect animation e.g. morale, prayer, armageddon, bless,...
|
||||
struct BattleEffect
|
||||
{
|
||||
@ -74,7 +54,6 @@ public:
|
||||
//displays custom effect on the battlefield
|
||||
void displayEffect(EBattleEffect::EBattleEffect effect, const BattleHex & destTile);
|
||||
void displayEffect(EBattleEffect::EBattleEffect effect, uint32_t soundID, const BattleHex & destTile);
|
||||
//void displayEffects(EBattleEffect::EBattleEffect effect, uint32_t soundID, const std::vector<BattleHex> & destTiles);
|
||||
|
||||
void battleTriggerEffect(const BattleTriggerEffect & bte);
|
||||
|
||||
|
@ -51,7 +51,7 @@ BattleInterface::BattleInterface(const CCreatureSet *army1, const CCreatureSet *
|
||||
std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt)
|
||||
: attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
|
||||
attackerInt(att), defenderInt(defen), curInt(att),
|
||||
myTurn(false), moveStarted(false), moveSoundHander(-1), bresult(nullptr), battleActionsStarted(false)
|
||||
myTurn(false), moveSoundHander(-1), bresult(nullptr), battleActionsStarted(false)
|
||||
{
|
||||
OBJ_CONSTRUCTION;
|
||||
|
||||
@ -548,6 +548,7 @@ void BattleInterface::spellCast(const BattleSpellCast * sc)
|
||||
stacksController->addNewAnim(new CPointEffectAnimation(*this, soundBase::invalid, sc->side ? "SP07_A.DEF" : "SP07_B.DEF", leftHero));
|
||||
stacksController->addNewAnim(new CPointEffectAnimation(*this, soundBase::invalid, sc->side ? "SP07_B.DEF" : "SP07_A.DEF", rightHero));
|
||||
}
|
||||
waitForAnims();
|
||||
}
|
||||
|
||||
void BattleInterface::battleStacksEffectsSet(const SetStackEffect & sse)
|
||||
@ -908,10 +909,6 @@ void BattleInterface::show(SDL_Surface *to)
|
||||
SDL_SetClipRect(to, &buf); //restoring previous clip_rect
|
||||
|
||||
showInterface(to);
|
||||
|
||||
//activation of next stack, if any
|
||||
//TODO: should be moved to the very start of this method?
|
||||
//activateStack();
|
||||
}
|
||||
|
||||
void BattleInterface::collectRenderableObjects(BattleRenderer & renderer)
|
||||
|
@ -9,6 +9,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "BattleConstants.h"
|
||||
#include "../gui/CIntObject.h"
|
||||
#include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation
|
||||
|
||||
@ -56,10 +57,15 @@ class BattleEffectsController;
|
||||
/// Small struct which contains information about the id of the attacked stack, the damage dealt,...
|
||||
struct StackAttackedInfo
|
||||
{
|
||||
const CStack *defender; //attacked stack
|
||||
int64_t dmg; //damage dealt
|
||||
unsigned int amountKilled; //how many creatures in stack has been killed
|
||||
const CStack *attacker; //attacking stack
|
||||
const CStack *defender;
|
||||
const CStack *attacker;
|
||||
|
||||
int64_t damageDealt;
|
||||
uint32_t amountKilled;
|
||||
|
||||
EBattleEffect::EBattleEffect battleEffect;
|
||||
SpellID spellEffect;
|
||||
|
||||
bool indirectAttack; //if true, stack was attacked indirectly - spell or ranged attack
|
||||
bool killed; //if true, stack has been killed
|
||||
bool rebirth; //if true, play rebirth animation after all
|
||||
@ -114,7 +120,6 @@ public:
|
||||
static CondSh<BattleAction *> givenCommand; //data != nullptr if we have i.e. moved current unit
|
||||
|
||||
bool myTurn; //if true, interface is active (commands can be ordered)
|
||||
bool moveStarted; //if true, the creature that is already moving is going to make its first step
|
||||
int moveSoundHander; // sound handler used when moving a unit
|
||||
|
||||
const BattleResult *bresult; //result of a battle; if non-zero then display when all animations end
|
||||
|
@ -147,6 +147,10 @@ void BattleStacksController::collectRenderableObjects(BattleRenderer & renderer)
|
||||
|
||||
void BattleStacksController::stackReset(const CStack * stack)
|
||||
{
|
||||
//FIXME: there should be no more ongoing animations. If not - then some other method created animations but did not wait for them to end
|
||||
assert(!owner.animsAreDisplayed.get());
|
||||
owner.waitForAnims();
|
||||
|
||||
auto iter = stackAnimation.find(stack->ID);
|
||||
|
||||
if(iter == stackAnimation.end())
|
||||
@ -170,8 +174,6 @@ void BattleStacksController::stackReset(const CStack * stack)
|
||||
}
|
||||
|
||||
owner.waitForAnims();
|
||||
|
||||
//TODO: handle more cases
|
||||
}
|
||||
|
||||
void BattleStacksController::stackAdded(const CStack * stack)
|
||||
@ -395,20 +397,41 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at
|
||||
{
|
||||
for(auto & attackedInfo : attackedInfos)
|
||||
{
|
||||
//if (!attackedInfo.cloneKilled) //FIXME: play dead animation for cloned creature before it vanishes
|
||||
addNewAnim(new CDefenceAnimation(attackedInfo, owner));
|
||||
if (!attackedInfo.attacker)
|
||||
continue;
|
||||
|
||||
if(attackedInfo.rebirth)
|
||||
{
|
||||
owner.effectsController->displayEffect(EBattleEffect::RESURRECT, soundBase::RESURECT, attackedInfo.defender->getPosition());
|
||||
}
|
||||
bool needsReverse =
|
||||
owner.curInt->cb->isToReverse(
|
||||
attackedInfo.defender->getPosition(),
|
||||
attackedInfo.attacker->getPosition(),
|
||||
facingRight(attackedInfo.defender),
|
||||
attackedInfo.attacker->doubleWide(),
|
||||
facingRight(attackedInfo.attacker));
|
||||
|
||||
if (needsReverse)
|
||||
addNewAnim(new CReverseAnimation(owner, attackedInfo.defender, attackedInfo.defender->getPosition()));
|
||||
}
|
||||
|
||||
for(auto & attackedInfo : attackedInfos)
|
||||
{
|
||||
addNewAnim(new CDefenceAnimation(attackedInfo, owner));
|
||||
|
||||
if (attackedInfo.battleEffect != EBattleEffect::INVALID)
|
||||
owner.effectsController->displayEffect(EBattleEffect::EBattleEffect(attackedInfo.battleEffect), attackedInfo.defender->getPosition());
|
||||
|
||||
if (attackedInfo.spellEffect != SpellID::NONE)
|
||||
owner.displaySpellEffect(attackedInfo.spellEffect, attackedInfo.defender->getPosition());
|
||||
}
|
||||
owner.waitForAnims();
|
||||
|
||||
for (auto & attackedInfo : attackedInfos)
|
||||
{
|
||||
if (attackedInfo.rebirth)
|
||||
{
|
||||
owner.effectsController->displayEffect(EBattleEffect::RESURRECT, soundBase::RESURECT, attackedInfo.defender->getPosition());
|
||||
addNewAnim(new CResurrectionAnimation(owner, attackedInfo.defender));
|
||||
}
|
||||
|
||||
if (attackedInfo.cloneKilled)
|
||||
stackRemoved(attackedInfo.defender->ID);
|
||||
}
|
||||
@ -417,21 +440,50 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at
|
||||
|
||||
void BattleStacksController::stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance)
|
||||
{
|
||||
assert(destHex.size() > 0);
|
||||
|
||||
//FIXME: there should be no more ongoing animations. If not - then some other method created animations but did not wait for them to end
|
||||
assert(!owner.animsAreDisplayed.get());
|
||||
|
||||
if(shouldRotate(stack, stack->getPosition(), destHex[0]))
|
||||
addNewAnim(new CReverseAnimation(owner, stack, destHex[0]));
|
||||
|
||||
addNewAnim(new CMovementStartAnimation(owner, stack));
|
||||
owner.waitForAnims();
|
||||
|
||||
addNewAnim(new CMovementAnimation(owner, stack, destHex, distance));
|
||||
owner.waitForAnims();
|
||||
|
||||
addNewAnim(new CMovementEndAnimation(owner, stack, destHex.back()));
|
||||
owner.waitForAnims();
|
||||
}
|
||||
|
||||
void BattleStacksController::stackAttacking( const CStack *attacker, BattleHex dest, const CStack *attacked, bool shooting )
|
||||
void BattleStacksController::stackAttacking( const CStack *attacker, BattleHex dest, const CStack *defender, bool shooting )
|
||||
{
|
||||
bool needsReverse =
|
||||
owner.curInt->cb->isToReverse(
|
||||
attacker->getPosition(),
|
||||
defender->getPosition(),
|
||||
facingRight(attacker),
|
||||
attacker->doubleWide(),
|
||||
facingRight(defender));
|
||||
|
||||
if (needsReverse)
|
||||
addNewAnim(new CReverseAnimation(owner, attacker, attacker->getPosition()));
|
||||
|
||||
owner.waitForAnims();
|
||||
|
||||
if (shooting)
|
||||
{
|
||||
addNewAnim(new CShootingAnimation(owner, attacker, dest, attacked));
|
||||
addNewAnim(new CShootingAnimation(owner, attacker, dest, defender));
|
||||
}
|
||||
else
|
||||
{
|
||||
addNewAnim(new CMeleeAttackAnimation(owner, attacker, dest, attacked));
|
||||
addNewAnim(new CMeleeAttackAnimation(owner, attacker, dest, defender));
|
||||
}
|
||||
//waitForAnims();
|
||||
|
||||
// do not wait - waiting will be done at stacksAreAttacked
|
||||
// waitForAnims();
|
||||
}
|
||||
|
||||
bool BattleStacksController::shouldRotate(const CStack * stack, const BattleHex & oldPos, const BattleHex & nextHex) const
|
||||
@ -450,8 +502,11 @@ bool BattleStacksController::shouldRotate(const CStack * stack, const BattleHex
|
||||
|
||||
void BattleStacksController::endAction(const BattleAction* action)
|
||||
{
|
||||
//FIXME: there should be no more ongoing animations. If not - then some other method created animations but did not wait for them to end
|
||||
assert(!owner.animsAreDisplayed.get());
|
||||
owner.waitForAnims();
|
||||
|
||||
//check if we should reverse stacks
|
||||
//for some strange reason, it's not enough
|
||||
TStacks stacks = owner.curInt->cb->battleGetStacks(CBattleCallback::MINE_AND_ENEMY);
|
||||
|
||||
for (const CStack *s : stacks)
|
||||
@ -460,29 +515,15 @@ void BattleStacksController::endAction(const BattleAction* action)
|
||||
|
||||
if (s && facingRight(s) != shouldFaceRight && s->alive() && stackAnimation[s->ID]->isIdle())
|
||||
{
|
||||
addNewAnim(new CReverseAnimation(owner, s, s->getPosition(), false));
|
||||
addNewAnim(new CReverseAnimation(owner, s, s->getPosition()));
|
||||
}
|
||||
}
|
||||
owner.waitForAnims();
|
||||
}
|
||||
|
||||
void BattleStacksController::startAction(const BattleAction* action)
|
||||
{
|
||||
const CStack *stack = owner.curInt->cb->battleGetStackByID(action->stackNumber);
|
||||
setHoveredStack(nullptr);
|
||||
|
||||
auto actionTarget = action->getTarget(owner.curInt->cb.get());
|
||||
|
||||
if(action->actionType == EActionType::WALK
|
||||
|| (action->actionType == EActionType::WALK_AND_ATTACK && actionTarget.at(0).hexValue != stack->getPosition()))
|
||||
{
|
||||
assert(stack);
|
||||
owner.moveStarted = true;
|
||||
if (stackAnimation[action->stackNumber]->framesInGroup(ECreatureAnimType::MOVE_START))
|
||||
addNewAnim(new CMovementStartAnimation(owner, stack));
|
||||
|
||||
//if(shouldRotate(stack, stack->getPosition(), actionTarget.at(0).hexValue))
|
||||
// addNewAnim(new CReverseAnimation(owner, stack, stack->getPosition(), true));
|
||||
}
|
||||
}
|
||||
|
||||
void BattleStacksController::activateStack()
|
||||
|
@ -238,7 +238,7 @@ void CGuiHandler::handleCurrentEvent()
|
||||
break;
|
||||
|
||||
case SDLK_F9:
|
||||
//not working yet since CClient::run remain locked after CBattleInterface removal
|
||||
//not working yet since CClient::run remain locked after BattleInterface removal
|
||||
// if(LOCPLINT->battleInt)
|
||||
// {
|
||||
// GH.popInts(1);
|
||||
|
@ -174,9 +174,9 @@ void CAdventureAI::battleStart(const CCreatureSet * army1, const CCreatureSet *
|
||||
battleAI->battleStart(army1, army2, tile, hero1, hero2, side);
|
||||
}
|
||||
|
||||
void CAdventureAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa)
|
||||
void CAdventureAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged)
|
||||
{
|
||||
battleAI->battleStacksAttacked(bsa);
|
||||
battleAI->battleStacksAttacked(bsa, ranged);
|
||||
}
|
||||
|
||||
void CAdventureAI::actionStarted(const BattleAction & action)
|
||||
|
@ -154,7 +154,7 @@ public:
|
||||
virtual void battleNewRound(int round) override;
|
||||
virtual void battleCatapultAttacked(const CatapultAttack & ca) override;
|
||||
virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override;
|
||||
virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) override;
|
||||
virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged) override;
|
||||
virtual void actionStarted(const BattleAction &action) override;
|
||||
virtual void battleNewRoundFirst(int round) override;
|
||||
virtual void actionFinished(const BattleAction &action) override;
|
||||
|
@ -911,7 +911,8 @@ enum class EActionType : int32_t
|
||||
INVALID = -1,
|
||||
NO_ACTION = 0,
|
||||
HERO_SPELL,
|
||||
WALK, DEFEND,
|
||||
WALK,
|
||||
DEFEND,
|
||||
RETREAT,
|
||||
SURRENDER,
|
||||
WALK_AND_ATTACK,
|
||||
|
@ -59,7 +59,7 @@ public:
|
||||
virtual void actionFinished(const BattleAction &action){};//occurs AFTER every action taken by any stack or by the hero
|
||||
virtual void actionStarted(const BattleAction &action){};//occurs BEFORE every action taken by any stack or by the hero
|
||||
virtual void battleAttack(const BattleAttack *ba){}; //called when stack is performing attack
|
||||
virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa){}; //called when stack receives damage (after battleAttack())
|
||||
virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged){}; //called when stack receives damage (after battleAttack())
|
||||
virtual void battleEnd(const BattleResult *br){};
|
||||
virtual void battleNewRoundFirst(int round){}; //called at the beginning of each turn before changes are applied;
|
||||
virtual void battleNewRound(int round){}; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
||||
|
@ -1569,10 +1569,14 @@ struct BattleUnitsChanged : public CPackForClient
|
||||
struct BattleStackAttacked
|
||||
{
|
||||
BattleStackAttacked():
|
||||
stackAttacked(0), attackerID(0),
|
||||
killedAmount(0), damageAmount(0),
|
||||
stackAttacked(0),
|
||||
attackerID(0),
|
||||
killedAmount(0),
|
||||
damageAmount(0),
|
||||
newState(),
|
||||
flags(0), effect(0), spellID(SpellID::NONE)
|
||||
flags(0),
|
||||
effect(0),
|
||||
spellID(SpellID::NONE)
|
||||
{};
|
||||
|
||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||
|
@ -1221,6 +1221,7 @@ int64_t CGameHandler::applyBattleEffects(BattleAttack & bat, std::shared_ptr<bat
|
||||
BattleStackAttacked bsa;
|
||||
if(secondary)
|
||||
bsa.flags |= BattleStackAttacked::SECONDARY; //all other targets do not suffer from spells & spell-like abilities
|
||||
|
||||
bsa.attackerID = attackerState->unitId();
|
||||
bsa.stackAttacked = def->unitId();
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user