1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-26 22:57:00 +02:00
vcmi/client/battle/BattleAnimationClasses.h

373 lines
11 KiB
C
Raw Normal View History

/*
* BattleAnimations.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../../lib/battle/BattleHex.h"
#include "BattleConstants.h"
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
class CCreature;
class CSpell;
2023-01-18 17:32:57 +02:00
class Point;
VCMI_LIB_NAMESPACE_END
struct SDL_Color;
class ColorFilter;
class BattleHero;
class CAnimation;
class BattleInterface;
class CreatureAnimation;
struct StackAttackedInfo;
/// Base class of battle animations
2022-12-12 22:04:25 +02:00
class BattleAnimation
{
protected:
2022-12-13 13:58:16 +02:00
BattleInterface & owner;
bool initialized;
2022-12-12 22:04:25 +02:00
std::vector<BattleAnimation *> & pendingAnimations();
std::shared_ptr<CreatureAnimation> stackAnimation(const CStack * stack) const;
bool stackFacingRight(const CStack * stack);
void setStackFacingRight(const CStack * stack, bool facingRight);
virtual bool init() = 0; //to be called - if returned false, call again until returns true
public:
ui32 ID; //unique identifier
bool isInitialized();
bool tryInitialize();
virtual void nextFrame() {} //call every new frame
2022-12-12 22:04:25 +02:00
virtual ~BattleAnimation();
2022-12-12 22:04:25 +02:00
BattleAnimation(BattleInterface & owner);
};
/// Sub-class which is responsible for managing the battle stack animation.
2022-12-12 22:04:25 +02:00
class BattleStackAnimation : public BattleAnimation
{
public:
std::shared_ptr<CreatureAnimation> myAnim; //animation for our stack, managed by BattleInterface
const CStack * stack; //id of stack whose animation it is
2022-12-12 22:04:25 +02:00
BattleStackAnimation(BattleInterface & owner, const CStack * _stack);
void rotateStack(BattleHex hex);
};
2022-12-16 18:34:35 +02:00
class StackActionAnimation : public BattleStackAnimation
{
2022-12-22 01:04:58 +02:00
ECreatureAnimType nextGroup;
ECreatureAnimType currGroup;
2022-12-16 18:34:35 +02:00
std::string sound;
public:
2022-12-22 01:04:58 +02:00
void setNextGroup( ECreatureAnimType group );
void setGroup( ECreatureAnimType group );
2022-12-16 18:34:35 +02:00
void setSound( std::string sound );
2022-11-27 20:01:52 +02:00
2022-12-22 01:04:58 +02:00
ECreatureAnimType getGroup() const;
2022-12-16 18:34:35 +02:00
StackActionAnimation(BattleInterface & owner, const CStack * _stack);
~StackActionAnimation();
2022-12-16 18:34:35 +02:00
bool init() override;
};
/// Animation of a defending unit
2022-12-16 18:34:35 +02:00
class DefenceAnimation : public StackActionAnimation
{
2022-12-12 22:04:25 +02:00
public:
DefenceAnimation(BattleInterface & owner, const CStack * stack);
};
2022-12-12 22:04:25 +02:00
/// Animation of a hit unit
2022-12-16 18:34:35 +02:00
class HittedAnimation : public StackActionAnimation
2022-12-12 22:04:25 +02:00
{
public:
2022-12-12 22:04:25 +02:00
HittedAnimation(BattleInterface & owner, const CStack * stack);
};
2022-12-12 22:04:25 +02:00
/// Animation of a dying unit
2022-12-16 18:34:35 +02:00
class DeathAnimation : public StackActionAnimation
2022-12-12 22:04:25 +02:00
{
public:
DeathAnimation(BattleInterface & owner, const CStack * stack, bool ranged);
};
2022-12-16 18:34:35 +02:00
/// Resurrects stack from dead state
class ResurrectionAnimation : public StackActionAnimation
{
public:
2022-12-16 18:34:35 +02:00
ResurrectionAnimation(BattleInterface & owner, const CStack * _stack);
};
class ColorTransformAnimation : public BattleStackAnimation
{
2022-12-16 18:34:35 +02:00
std::vector<ColorFilter> steps;
std::vector<float> timePoints;
const CSpell * spell;
float totalProgress;
bool init() override;
2022-12-16 18:34:35 +02:00
void nextFrame() override;
public:
ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const std::string & colorFilterName, const CSpell * spell);
};
/// Base class for all animations that play during stack movement
2022-12-12 22:04:25 +02:00
class StackMoveAnimation : public BattleStackAnimation
2022-11-28 22:35:38 +02:00
{
public:
BattleHex nextHex;
BattleHex prevHex;
2022-11-28 22:35:38 +02:00
protected:
StackMoveAnimation(BattleInterface & owner, const CStack * _stack, BattleHex prevHex, BattleHex nextHex);
2022-11-28 22:35:38 +02:00
};
/// Move animation of a creature
2022-12-12 22:04:25 +02:00
class MovementAnimation : public StackMoveAnimation
{
private:
2023-03-20 19:43:37 +02:00
int moveSoundHander; // sound handler used when moving a unit
std::vector<BattleHex> destTiles; //full path, includes already passed hexes
ui32 curentMoveIndex; // index of nextHex in destTiles
double begX, begY; // starting position
double distanceX, distanceY; // full movement distance, may be negative if creture moves topleft
/// progress gain per second
double progressPerSecond;
/// range 0 -> 1, indicates move progrees. 0 = movement starts, 1 = move ends
double progress;
public:
bool init() override;
void nextFrame() override;
2022-12-12 22:04:25 +02:00
MovementAnimation(BattleInterface & owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance);
~MovementAnimation();
};
/// Move end animation of a creature
2022-12-12 22:04:25 +02:00
class MovementEndAnimation : public StackMoveAnimation
{
public:
bool init() override;
2022-12-12 22:04:25 +02:00
MovementEndAnimation(BattleInterface & owner, const CStack * _stack, BattleHex destTile);
~MovementEndAnimation();
};
/// Move start animation of a creature
2022-12-12 22:04:25 +02:00
class MovementStartAnimation : public StackMoveAnimation
{
public:
bool init() override;
2022-12-12 22:04:25 +02:00
MovementStartAnimation(BattleInterface & owner, const CStack * _stack);
};
/// Class responsible for animation of stack chaning direction (left <-> right)
2022-12-12 22:04:25 +02:00
class ReverseAnimation : public StackMoveAnimation
{
void setupSecondPart();
public:
bool init() override;
2022-12-12 22:04:25 +02:00
ReverseAnimation(BattleInterface & owner, const CStack * stack, BattleHex dest);
};
2022-12-16 18:34:35 +02:00
/// This class is responsible for managing the battle attack animation
class AttackAnimation : public StackActionAnimation
{
2022-12-16 18:34:35 +02:00
protected:
BattleHex dest; //attacked hex
const CStack *defendingStack;
const CStack *attackingStack;
int attackingStackPosBeforeReturn; //for stacks with return_after_strike feature
2022-12-16 18:34:35 +02:00
const CCreature * getCreature() const;
2022-12-22 01:04:58 +02:00
ECreatureAnimType findValidGroup( const std::vector<ECreatureAnimType> candidates ) const;
2022-12-16 18:34:35 +02:00
public:
AttackAnimation(BattleInterface & owner, const CStack *attacker, BattleHex _dest, const CStack *defender);
};
2022-12-16 18:34:35 +02:00
/// Hand-to-hand attack
class MeleeAttackAnimation : public AttackAnimation
{
2022-12-22 01:04:58 +02:00
ECreatureAnimType getUpwardsGroup(bool multiAttack) const;
ECreatureAnimType getForwardGroup(bool multiAttack) const;
ECreatureAnimType getDownwardsGroup(bool multiAttack) const;
2022-12-22 01:04:58 +02:00
ECreatureAnimType selectGroup(bool multiAttack);
public:
2022-12-16 18:34:35 +02:00
MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack);
2022-12-16 18:34:35 +02:00
void nextFrame() override;
};
2022-12-16 18:34:35 +02:00
class RangedAttackAnimation : public AttackAnimation
{
void setAnimationGroup();
void initializeProjectile();
void emitProjectile();
void emitExplosion();
protected:
bool projectileEmitted;
2022-12-22 01:04:58 +02:00
virtual ECreatureAnimType getUpwardsGroup() const = 0;
virtual ECreatureAnimType getForwardGroup() const = 0;
virtual ECreatureAnimType getDownwardsGroup() const = 0;
virtual void createProjectile(const Point & from, const Point & dest) const = 0;
2022-12-06 14:12:13 +02:00
virtual uint32_t getAttackClimaxFrame() const = 0;
public:
2022-12-12 22:04:25 +02:00
RangedAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest, const CStack * defender);
~RangedAttackAnimation();
bool init() override;
void nextFrame() override;
};
/// Shooting attack
2022-12-12 22:04:25 +02:00
class ShootingAnimation : public RangedAttackAnimation
{
2022-12-22 01:04:58 +02:00
ECreatureAnimType getUpwardsGroup() const override;
ECreatureAnimType getForwardGroup() const override;
ECreatureAnimType getDownwardsGroup() const override;
void createProjectile(const Point & from, const Point & dest) const override;
2022-12-06 14:12:13 +02:00
uint32_t getAttackClimaxFrame() const override;
public:
2022-12-12 22:04:25 +02:00
ShootingAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest, const CStack * defender);
};
/// Catapult attack
2022-12-12 22:04:25 +02:00
class CatapultAnimation : public ShootingAnimation
{
private:
bool explosionEmitted;
int catapultDamage;
public:
2022-12-12 22:04:25 +02:00
CatapultAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest, const CStack * defender, int _catapultDmg = 0);
void createProjectile(const Point & from, const Point & dest) const override;
void nextFrame() override;
};
2022-12-12 22:04:25 +02:00
class CastAnimation : public RangedAttackAnimation
{
const CSpell * spell;
2022-12-22 01:04:58 +02:00
ECreatureAnimType getUpwardsGroup() const override;
ECreatureAnimType getForwardGroup() const override;
ECreatureAnimType getDownwardsGroup() const override;
void createProjectile(const Point & from, const Point & dest) const override;
2022-12-06 14:12:13 +02:00
uint32_t getAttackClimaxFrame() const override;
public:
2022-12-12 22:04:25 +02:00
CastAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest_, const CStack * defender, const CSpell * spell);
};
2022-12-16 18:34:35 +02:00
class DummyAnimation : public BattleAnimation
{
private:
int counter;
int howMany;
public:
bool init() override;
void nextFrame() override;
DummyAnimation(BattleInterface & owner, int howManyFrames);
};
/// Class that plays effect at one or more positions along with (single) sound effect
class EffectAnimation : public BattleAnimation
{
std::string soundName;
bool effectFinished;
bool reversed;
int effectFlags;
std::shared_ptr<CAnimation> animation;
std::vector<Point> positions;
std::vector<BattleHex> battlehexes;
bool alignToBottom() const;
bool waitForSound() const;
bool forceOnTop() const;
bool screenFill() const;
void onEffectFinished();
void clearEffect();
void playEffect();
public:
enum EEffectFlags
{
ALIGN_TO_BOTTOM = 1,
FORCE_ON_TOP = 2,
SCREEN_FILL = 4,
};
/// Create animation with screen-wide effect
EffectAnimation(BattleInterface & owner, std::string animationName, int effects = 0, bool reversed = false);
/// Create animation positioned at point(s). Note that positions must be are absolute, including battleint position offset
EffectAnimation(BattleInterface & owner, std::string animationName, Point pos , int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, std::string animationName, std::vector<Point> pos , int effects = 0, bool reversed = false);
/// Create animation positioned at certain hex(es)
EffectAnimation(BattleInterface & owner, std::string animationName, BattleHex hex , int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, std::string animationName, std::vector<BattleHex> hex, int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, std::string animationName, Point pos, BattleHex hex, int effects = 0, bool reversed = false);
~EffectAnimation();
bool init() override;
void nextFrame() override;
};
class HeroCastAnimation : public BattleAnimation
2022-12-12 23:31:18 +02:00
{
std::shared_ptr<BattleHero> hero;
const CStack * target;
const CSpell * spell;
BattleHex tile;
bool projectileEmitted;
void initializeProjectile();
void emitProjectile();
void emitAnimationEvent();
2022-12-12 23:31:18 +02:00
public:
HeroCastAnimation(BattleInterface & owner, std::shared_ptr<BattleHero> hero, BattleHex dest, const CStack * defender, const CSpell * spell);
Spells configuration version 2 (effect-based) * Indirect spell effects loading * Json serializer improvements * spell->canBeCastAt do not allow useless cast for any spell * Added proxy caster class for spell-created obstacles * Handle damage from spell-created obstacles inside mechanics * Experimental GameState integration/regression tests * Ignore mod settings and load only "vcmi" mod when running tests * fixed https://bugs.vcmi.eu/view.php?id=2765 (with tests) * Huge improvements of BattleAI regarding spell casts * AI can cast almost any combat spell except TELEPORT, SACRIFICE and obstacle placement spells. * Possible fix for https://bugs.vcmi.eu/view.php?id=1811 * CStack factored out to several classes * [Battle] Allowed RETURN_AFTER_STRIKE effect on server side to be optional * [Battle] Allowed BattleAction have multiple destinations * [Spells] Converted limit|immunity to target condition * [Spells] Use partial configuration reload for backward compatibility handling * [Tests] Started tests for CUnitState * Partial fixes of fire shield effect * [Battle] Do HP calculations in 64 bits * [BattleAI] Use threading for spell cast evaluation * [BattleAI] Made AI be able to evaluate modified turn order (on hypothetical battle state) * Implemented https://bugs.vcmi.eu/view.php?id=2811 * plug rare freeze when hypnotized unit shots vertically * Correctly apply ONLY_MELEE_FIGHT / ONLY_DISTANCE_FIGHT for unit damage, attack & defense * [BattleAI] Try to not waste a cast if battle is actually won already * Extended JsonSerializeFormat API * fixed https://bugs.vcmi.eu/view.php?id=2847 * Any unit effect can be now chained (not only damage like Chain Lightning) ** only damage effect for now actually uses "chainFactor" * Possible quick fix for https://bugs.vcmi.eu/view.php?id=2860
2017-07-20 06:08:49 +02:00
2022-12-12 22:04:25 +02:00
void nextFrame() override;
bool init() override;
};