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

Petrify will freeze stack animations

This commit is contained in:
Ivan Savenko 2022-12-16 18:34:35 +02:00
parent 8665f36778
commit 733f21f8dc
8 changed files with 187 additions and 221 deletions

View File

@ -93,6 +93,58 @@ BattleStackAnimation::BattleStackAnimation(BattleInterface & owner, const CStack
assert(myAnim);
}
StackActionAnimation::StackActionAnimation(BattleInterface & owner, const CStack * stack)
: BattleStackAnimation(owner, stack)
, nextGroup(ECreatureAnimType::HOLDING)
, currGroup(ECreatureAnimType::HOLDING)
{
}
ECreatureAnimType::Type StackActionAnimation::getGroup() const
{
return currGroup;
}
void StackActionAnimation::setNextGroup( ECreatureAnimType::Type group )
{
nextGroup = group;
}
void StackActionAnimation::setGroup( ECreatureAnimType::Type group )
{
currGroup = group;
}
void StackActionAnimation::setSound( std::string sound )
{
this->sound = sound;
}
bool StackActionAnimation::init()
{
if (!sound.empty())
CCS->soundh->playSound(sound);
if (myAnim->framesInGroup(currGroup) > 0)
{
myAnim->playOnce(currGroup);
myAnim->onAnimationReset += [&](){ delete this; };
}
else
delete this;
return true;
}
StackActionAnimation::~StackActionAnimation()
{
if (stack->isFrozen())
myAnim->setType(ECreatureAnimType::HOLDING);
else
myAnim->setType(nextGroup);
}
ECreatureAnimType::Type AttackAnimation::findValidGroup( const std::vector<ECreatureAnimType::Type> candidates ) const
{
for ( auto group : candidates)
@ -105,26 +157,6 @@ ECreatureAnimType::Type AttackAnimation::findValidGroup( const std::vector<ECrea
return ECreatureAnimType::HOLDING;
}
void AttackAnimation::nextFrame()
{
if(myAnim->getType() != group)
{
myAnim->setType(group);
myAnim->onAnimationReset += [&](){ delete this; };
}
if(!soundPlayed)
{
playSound();
soundPlayed = true;
}
}
AttackAnimation::~AttackAnimation()
{
myAnim->setType(ECreatureAnimType::HOLDING);
}
const CCreature * AttackAnimation::getCreature() const
{
if (attackingStack->getCreature()->idNumber == CreatureID::ARROW_TOWERS)
@ -135,9 +167,7 @@ const CCreature * AttackAnimation::getCreature() const
AttackAnimation::AttackAnimation(BattleInterface & owner, const CStack *attacker, BattleHex _dest, const CStack *defender)
: BattleStackAnimation(owner, attacker),
group(ECreatureAnimType::SHOOT_FRONT),
soundPlayed(false),
: StackActionAnimation(owner, attacker),
dest(_dest),
defendingStack(defender),
attackingStack(attacker)
@ -146,61 +176,34 @@ AttackAnimation::AttackAnimation(BattleInterface & owner, const CStack *attacker
attackingStackPosBeforeReturn = attackingStack->getPosition();
}
bool HittedAnimation::init()
{
CCS->soundh->playSound(battle_sound(stack->getCreature(), wince));
myAnim->playOnce(ECreatureAnimType::HITTED);
myAnim->onAnimationReset += [&](){ delete this; };
return true;
}
HittedAnimation::HittedAnimation(BattleInterface & owner, const CStack * stack)
: BattleStackAnimation(owner, stack)
: StackActionAnimation(owner, stack)
{
setGroup(ECreatureAnimType::HITTED);
setSound(battle_sound(stack->getCreature(), wince));
}
DefenceAnimation::DefenceAnimation(BattleInterface & owner, const CStack * stack)
: BattleStackAnimation(owner, stack)
: StackActionAnimation(owner, stack)
{
}
bool DefenceAnimation::init()
{
CCS->soundh->playSound(battle_sound(stack->getCreature(), defend));
myAnim->playOnce(ECreatureAnimType::DEFENCE);
myAnim->onAnimationReset += [&](){ delete this; };
return true; //initialized successfuly
}
ECreatureAnimType::Type DeathAnimation::getMyAnimType()
{
if(rangedAttack && myAnim->framesInGroup(ECreatureAnimType::DEATH_RANGED) > 0)
return ECreatureAnimType::DEATH_RANGED;
else
return ECreatureAnimType::DEATH;
}
bool DeathAnimation::init()
{
CCS->soundh->playSound(battle_sound(stack->getCreature(), killed));
myAnim->playOnce(getMyAnimType());
myAnim->onAnimationReset += [&](){ delete this; };
return true;
setGroup(ECreatureAnimType::DEFENCE);
setSound(battle_sound(stack->getCreature(), defend));
}
DeathAnimation::DeathAnimation(BattleInterface & owner, const CStack * stack, bool ranged):
BattleStackAnimation(owner, stack),
rangedAttack(ranged)
StackActionAnimation(owner, stack)
{
}
setSound(battle_sound(stack->getCreature(), killed));
DeathAnimation::~DeathAnimation()
{
if(rangedAttack && myAnim->framesInGroup(ECreatureAnimType::DEAD_RANGED) > 0)
myAnim->setType(ECreatureAnimType::DEAD_RANGED);
if(ranged && myAnim->framesInGroup(ECreatureAnimType::DEATH_RANGED) > 0)
setGroup(ECreatureAnimType::DEATH_RANGED);
else
myAnim->setType(ECreatureAnimType::DEAD);
setGroup(ECreatureAnimType::DEATH);
if(ranged && myAnim->framesInGroup(ECreatureAnimType::DEAD_RANGED) > 0)
setNextGroup(ECreatureAnimType::DEAD_RANGED);
else
setNextGroup(ECreatureAnimType::DEAD);
}
DummyAnimation::DummyAnimation(BattleInterface & owner, int howManyFrames)
@ -223,7 +226,7 @@ void DummyAnimation::nextFrame()
delete this;
}
ECreatureAnimType::Type MeleeAttackAnimation::getUpwardsGroup() const
ECreatureAnimType::Type MeleeAttackAnimation::getUpwardsGroup(bool multiAttack) const
{
if (!multiAttack)
return ECreatureAnimType::ATTACK_UP;
@ -235,7 +238,7 @@ ECreatureAnimType::Type MeleeAttackAnimation::getUpwardsGroup() const
});
}
ECreatureAnimType::Type MeleeAttackAnimation::getForwardGroup() const
ECreatureAnimType::Type MeleeAttackAnimation::getForwardGroup(bool multiAttack) const
{
if (!multiAttack)
return ECreatureAnimType::ATTACK_FRONT;
@ -247,7 +250,7 @@ ECreatureAnimType::Type MeleeAttackAnimation::getForwardGroup() const
});
}
ECreatureAnimType::Type MeleeAttackAnimation::getDownwardsGroup() const
ECreatureAnimType::Type MeleeAttackAnimation::getDownwardsGroup(bool multiAttack) const
{
if (!multiAttack)
return ECreatureAnimType::ATTACK_DOWN;
@ -259,27 +262,16 @@ ECreatureAnimType::Type MeleeAttackAnimation::getDownwardsGroup() const
});
}
bool MeleeAttackAnimation::init()
ECreatureAnimType::Type MeleeAttackAnimation::selectGroup(bool multiAttack)
{
assert(attackingStack);
assert(!myAnim->isDeadOrDying());
if(!attackingStack || myAnim->isDeadOrDying())
{
delete this;
return false;
}
logAnim->info("CMeleeAttackAnimation::init: stack %s -> stack %s", stack->getName(), defendingStack->getName());
const ECreatureAnimType::Type mutPosToGroup[] =
{
getUpwardsGroup(),
getUpwardsGroup(),
getForwardGroup(),
getDownwardsGroup(),
getDownwardsGroup(),
getForwardGroup()
getUpwardsGroup (multiAttack),
getUpwardsGroup (multiAttack),
getForwardGroup (multiAttack),
getDownwardsGroup(multiAttack),
getDownwardsGroup(multiAttack),
getForwardGroup (multiAttack)
};
int revShiftattacker = (attackingStack->side == BattleSide::ATTACKER ? -1 : 1);
@ -300,14 +292,13 @@ bool MeleeAttackAnimation::init()
assert(mutPos >= 0 && mutPos <=5);
group = mutPosToGroup[mutPos];
return true;
return mutPosToGroup[mutPos];
}
void MeleeAttackAnimation::nextFrame()
{
size_t currentFrame = stackAnimation(attackingStack)->getCurrentFrame();
size_t totalFrames = stackAnimation(attackingStack)->framesInGroup(group);
size_t totalFrames = stackAnimation(attackingStack)->framesInGroup(getGroup());
if ( currentFrame * 2 >= totalFrames )
{
@ -317,23 +308,18 @@ void MeleeAttackAnimation::nextFrame()
AttackAnimation::nextFrame();
}
void MeleeAttackAnimation::playSound()
{
CCS->soundh->playSound(battle_sound(getCreature(), attack));
}
MeleeAttackAnimation::MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack)
: AttackAnimation(owner, attacker, _dest, _attacked),
multiAttack(multiAttack)
: AttackAnimation(owner, attacker, _dest, _attacked)
{
logAnim->debug("Created melee attack anim for %s", attacker->getName());
setSound(battle_sound(getCreature(), attack));
setGroup(selectGroup(multiAttack));
}
StackMoveAnimation::StackMoveAnimation(BattleInterface & owner, const CStack * _stack, BattleHex _currentHex):
BattleStackAnimation(owner, _stack),
currentHex(_currentHex)
{
}
bool MovementAnimation::init()
@ -585,32 +571,10 @@ void ReverseAnimation::setupSecondPart()
delete this;
}
bool ResurrectionAnimation::init()
{
assert(stack);
if(!stack)
{
delete this;
return false;
}
logAnim->info("CResurrectionAnimation::init: stack %s", stack->getName());
myAnim->playOnce(ECreatureAnimType::RESURRECTION);
myAnim->onAnimationReset += [&](){ delete this; };
return true;
}
ResurrectionAnimation::ResurrectionAnimation(BattleInterface & owner, const CStack * _stack):
BattleStackAnimation(owner, _stack)
StackActionAnimation(owner, _stack)
{
}
bool ColorTransformAnimation::init()
{
return true;
setGroup(ECreatureAnimType::RESURRECTION);
}
void ColorTransformAnimation::nextFrame()
@ -651,7 +615,7 @@ void ColorTransformAnimation::nextFrame()
}
ColorTransformAnimation::ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const CSpell * spell):
BattleStackAnimation(owner, _stack),
StackActionAnimation(owner, _stack),
spell(spell),
totalProgress(0.f)
{
@ -716,31 +680,15 @@ RangedAttackAnimation::RangedAttackAnimation(BattleInterface & owner_, const CSt
: AttackAnimation(owner_, attacker, dest_, defender),
projectileEmitted(false)
{
}
void RangedAttackAnimation::playSound()
{
CCS->soundh->playSound(battle_sound(getCreature(), shoot));
setSound(battle_sound(getCreature(), shoot));
}
bool RangedAttackAnimation::init()
{
assert(attackingStack);
assert(!myAnim->isDeadOrDying());
if(!attackingStack || myAnim->isDeadOrDying())
{
//FIXME: how is this possible?
logAnim->warn("Shooting animation has not started yet but attacker is dead! Aborting...");
delete this;
return false;
}
logAnim->info("CRangedAttackAnimation::init: stack %s", stack->getName());
setAnimationGroup();
initializeProjectile();
return true;
return AttackAnimation::init();
}
void RangedAttackAnimation::setAnimationGroup()
@ -755,11 +703,11 @@ void RangedAttackAnimation::setAnimationGroup()
// Calculate projectile start position. Offsets are read out of the CRANIM.TXT.
if (projectileAngle > straightAngle)
group = getUpwardsGroup();
setGroup(getUpwardsGroup());
else if (projectileAngle < -straightAngle)
group = getDownwardsGroup();
setGroup(getDownwardsGroup());
else
group = getForwardGroup();
setGroup(getForwardGroup());
}
void RangedAttackAnimation::initializeProjectile()
@ -769,17 +717,17 @@ void RangedAttackAnimation::initializeProjectile()
Point shotOrigin = stackAnimation(attackingStack)->pos.topLeft() + Point(222, 265);
int multiplier = stackFacingRight(attackingStack) ? 1 : -1;
if (group == getUpwardsGroup())
if (getGroup() == getUpwardsGroup())
{
shotOrigin.x += ( -25 + shooterInfo->animation.upperRightMissleOffsetX ) * multiplier;
shotOrigin.y += shooterInfo->animation.upperRightMissleOffsetY;
}
else if (group == getDownwardsGroup())
else if (getGroup() == getDownwardsGroup())
{
shotOrigin.x += ( -25 + shooterInfo->animation.lowerRightMissleOffsetX ) * multiplier;
shotOrigin.y += shooterInfo->animation.lowerRightMissleOffsetY;
}
else if (group == getForwardGroup())
else if (getGroup() == getForwardGroup())
{
shotOrigin.x += ( -25 + shooterInfo->animation.rightMissleOffsetX ) * multiplier;
shotOrigin.y += shooterInfo->animation.rightMissleOffsetY;
@ -953,11 +901,9 @@ void CastAnimation::createProjectile(const Point & from, const Point & dest) con
uint32_t CastAnimation::getAttackClimaxFrame() const
{
//TODO: allow defining this parameter in config file, separately from attackClimaxFrame of missile attacks
uint32_t maxFrames = stackAnimation(attackingStack)->framesInGroup(group);
uint32_t maxFrames = stackAnimation(attackingStack)->framesInGroup(getGroup());
if (maxFrames > 2)
return maxFrames - 2;
return 0;
return maxFrames / 2;
}
PointEffectAnimation::PointEffectAnimation(BattleInterface & owner, std::string soundName, std::string animationName, int effects):

View File

@ -63,86 +63,73 @@ public:
const CStack * stack; //id of stack whose animation it is
BattleStackAnimation(BattleInterface & owner, const CStack * _stack);
void rotateStack(BattleHex hex);
};
/// This class is responsible for managing the battle attack animation
class AttackAnimation : public BattleStackAnimation
class StackActionAnimation : public BattleStackAnimation
{
bool soundPlayed;
protected:
BattleHex dest; //attacked hex
ECreatureAnimType::Type group;
const CStack *defendingStack;
const CStack *attackingStack;
int attackingStackPosBeforeReturn; //for stacks with return_after_strike feature
const CCreature * getCreature() const;
ECreatureAnimType::Type findValidGroup( const std::vector<ECreatureAnimType::Type> candidates ) const;
ECreatureAnimType::Type nextGroup;
ECreatureAnimType::Type currGroup;
std::string sound;
public:
virtual void playSound() = 0;
void setNextGroup( ECreatureAnimType::Type group );
void setGroup( ECreatureAnimType::Type group );
void setSound( std::string sound );
void nextFrame() override;
AttackAnimation(BattleInterface & owner, const CStack *attacker, BattleHex _dest, const CStack *defender);
~AttackAnimation();
ECreatureAnimType::Type getGroup() const;
StackActionAnimation(BattleInterface & owner, const CStack * _stack);
~StackActionAnimation();
bool init() override;
};
/// Animation of a defending unit
class DefenceAnimation : public BattleStackAnimation
class DefenceAnimation : public StackActionAnimation
{
public:
bool init() override;
DefenceAnimation(BattleInterface & owner, const CStack * stack);
};
/// Animation of a hit unit
class HittedAnimation : public BattleStackAnimation
class HittedAnimation : public StackActionAnimation
{
public:
HittedAnimation(BattleInterface & owner, const CStack * stack);
bool init() override;
};
/// Animation of a dying unit
class DeathAnimation : public BattleStackAnimation
class DeathAnimation : public StackActionAnimation
{
bool rangedAttack;
ECreatureAnimType::Type getMyAnimType();
public:
bool init() override;
DeathAnimation(BattleInterface & owner, const CStack * stack, bool ranged);
~DeathAnimation();
};
class DummyAnimation : public BattleAnimation
/// Resurrects stack from dead state
class ResurrectionAnimation : public StackActionAnimation
{
private:
int counter;
int howMany;
public:
bool init() override;
void nextFrame() override;
DummyAnimation(BattleInterface & owner, int howManyFrames);
ResurrectionAnimation(BattleInterface & owner, const CStack * _stack);
};
/// Hand-to-hand attack
class MeleeAttackAnimation : public AttackAnimation
class ColorTransformAnimation : public StackActionAnimation
{
bool multiAttack;
std::vector<ColorFilter> steps;
std::vector<float> timePoints;
const CSpell * spell;
ECreatureAnimType::Type getUpwardsGroup() const;
ECreatureAnimType::Type getForwardGroup() const;
ECreatureAnimType::Type getDownwardsGroup() const;
float totalProgress;
public:
bool init() override;
void nextFrame() override;
void playSound() override;
MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack);
ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const CSpell * spell);
public:
static ColorTransformAnimation * petrifyAnimation (BattleInterface & owner, const CStack * _stack, const CSpell * spell);
static ColorTransformAnimation * cloneAnimation (BattleInterface & owner, const CStack * _stack, const CSpell * spell);
static ColorTransformAnimation * bloodlustAnimation(BattleInterface & owner, const CStack * _stack, const CSpell * spell);
static ColorTransformAnimation * fadeInAnimation (BattleInterface & owner, const CStack * _stack);
static ColorTransformAnimation * fadeOutAnimation (BattleInterface & owner, const CStack * _stack);
};
/// Base class for all animations that play during stack movement
@ -207,36 +194,38 @@ public:
ReverseAnimation(BattleInterface & owner, const CStack * stack, BattleHex dest);
};
/// Resurrects stack from dead state
class ResurrectionAnimation : public BattleStackAnimation
/// This class is responsible for managing the battle attack animation
class AttackAnimation : public StackActionAnimation
{
public:
bool init() override;
protected:
BattleHex dest; //attacked hex
const CStack *defendingStack;
const CStack *attackingStack;
int attackingStackPosBeforeReturn; //for stacks with return_after_strike feature
ResurrectionAnimation(BattleInterface & owner, const CStack * _stack);
const CCreature * getCreature() const;
ECreatureAnimType::Type findValidGroup( const std::vector<ECreatureAnimType::Type> candidates ) const;
public:
AttackAnimation(BattleInterface & owner, const CStack *attacker, BattleHex _dest, const CStack *defender);
};
class ColorTransformAnimation : public BattleStackAnimation
/// Hand-to-hand attack
class MeleeAttackAnimation : public AttackAnimation
{
std::vector<ColorFilter> steps;
std::vector<float> timePoints;
const CSpell * spell;
ECreatureAnimType::Type getUpwardsGroup(bool multiAttack) const;
ECreatureAnimType::Type getForwardGroup(bool multiAttack) const;
ECreatureAnimType::Type getDownwardsGroup(bool multiAttack) const;
float totalProgress;
ECreatureAnimType::Type selectGroup(bool multiAttack);
public:
MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack);
bool init() override;
void nextFrame() override;
ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const CSpell * spell);
public:
static ColorTransformAnimation * petrifyAnimation (BattleInterface & owner, const CStack * _stack, const CSpell * spell);
static ColorTransformAnimation * cloneAnimation (BattleInterface & owner, const CStack * _stack, const CSpell * spell);
static ColorTransformAnimation * bloodlustAnimation(BattleInterface & owner, const CStack * _stack, const CSpell * spell);
static ColorTransformAnimation * fadeInAnimation (BattleInterface & owner, const CStack * _stack);
static ColorTransformAnimation * fadeOutAnimation (BattleInterface & owner, const CStack * _stack);
};
class RangedAttackAnimation : public AttackAnimation
{
void setAnimationGroup();
@ -260,7 +249,6 @@ public:
bool init() override;
void nextFrame() override;
void playSound() override;
};
/// Shooting attack
@ -307,6 +295,18 @@ public:
CastAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest_, const CStack * defender, const CSpell * spell);
};
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 PointEffectAnimation : public BattleAnimation
{

View File

@ -56,6 +56,8 @@ namespace ECreatureAnimType
{
enum Type // list of creature animations, numbers were taken from def files
{
INVALID = -1,
MOVING = 0,
MOUSEON = 1,
HOLDING = 2, // base idling animation
@ -82,6 +84,7 @@ enum Type // list of creature animations, numbers were taken from def files
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
RESURRECTION = 24, // new group, used for animating resurrection, if empty - reversed "DEATH" animation will be copiend here
FROZEN = 25, // new group, used when stack animation is paused (e.g. petrified). If empty - consist of first frame from HOLDING animation
CAST_UP = 30,
CAST_FRONT = 31,

View File

@ -40,8 +40,14 @@ static void onAnimationFinished(const CStack *stack, std::weak_ptr<CreatureAnima
if(!animation)
return;
if (!stack->isFrozen() && animation->getType() == ECreatureAnimType::FROZEN)
animation->setType(ECreatureAnimType::HOLDING);
if (animation->isIdle())
{
if (stack->isFrozen())
animation->setType(ECreatureAnimType::FROZEN);
const CCreature *creature = stack->getCreature();
if (animation->framesInGroup(ECreatureAnimType::MOUSEON) > 0)
@ -249,7 +255,7 @@ void BattleStacksController::setHoveredStack(const CStack *stack)
{
mouseHoveredStack = stack;
if (mouseHoveredStack)
if (mouseHoveredStack && !mouseHoveredStack->isFrozen())
{
stackAnimation[mouseHoveredStack->ID]->setBorderColor(AnimationControls::getBlueBorder());
if (stackAnimation[mouseHoveredStack->ID]->framesInGroup(ECreatureAnimType::MOUSEON) > 0)
@ -361,8 +367,6 @@ void BattleStacksController::showStack(Canvas & canvas, const CStack * stack)
}
bool stackHasProjectile = owner.projectilesController->hasActiveProjectile(stack, true);
//bool stackPetrified = stack->hasBonus(Selector::source(Bonus::SPELL_EFFECT, SpellID::STONE_GAZE));
//bool stackFrozen = stackHasProjectile || stackPetrified;
if (stackHasProjectile)
stackAnimation[stack->ID]->pause();

View File

@ -190,6 +190,12 @@ CreatureAnimation::CreatureAnimation(const std::string & name_, TSpeedController
reverse->duplicateImage(ECreatureAnimType::DEATH_RANGED, reverse->size(ECreatureAnimType::DEATH_RANGED)-1, ECreatureAnimType::DEAD_RANGED);
}
if(forward->size(ECreatureAnimType::FROZEN) == 0)
{
forward->duplicateImage(ECreatureAnimType::HOLDING, 0, ECreatureAnimType::FROZEN);
reverse->duplicateImage(ECreatureAnimType::HOLDING, 0, ECreatureAnimType::FROZEN);
}
if(forward->size(ECreatureAnimType::RESURRECTION) == 0)
{
for (size_t i = 0; i < forward->size(ECreatureAnimType::DEATH); ++i)

View File

@ -547,6 +547,11 @@ bool CUnitState::isGhost() const
return ghost;
}
bool CUnitState::isFrozen() const
{
return hasBonus(Selector::source(Bonus::SPELL_EFFECT, SpellID::STONE_GAZE));
}
bool CUnitState::isValidTarget(bool allowDead) const
{
return (alive() || (allowDead && isDead())) && getPosition().isValid() && !isTurret();

View File

@ -195,6 +195,7 @@ public:
bool ableToRetaliate() const override;
bool alive() const override;
bool isGhost() const override;
bool isFrozen() const override;
bool isValidTarget(bool allowDead = false) const override;
bool isClone() const override;

View File

@ -43,6 +43,7 @@ public:
virtual bool ableToRetaliate() const = 0;
virtual bool alive() const = 0;
virtual bool isGhost() const = 0;
virtual bool isFrozen() const = 0;
bool isDead() const;
bool isTurret() const;