2017-07-13 10:26:03 +02:00
/*
* CBattleAnimations . cpp , 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
*
*/
2011-12-22 16:05:19 +03:00
# include "StdInc.h"
# include "CBattleAnimations.h"
# include <boost/math/constants/constants.hpp>
2013-07-06 19:10:20 +03:00
2011-12-22 16:05:19 +03:00
# include "CBattleInterfaceClasses.h"
2013-07-06 19:10:20 +03:00
# include "CBattleInterface.h"
2011-12-22 16:05:19 +03:00
# include "CCreatureAnimation.h"
2013-07-06 19:10:20 +03:00
# include "../CGameInfo.h"
# include "../CMusicHandler.h"
2011-12-22 16:05:19 +03:00
# include "../CPlayerInterface.h"
# include "../Graphics.h"
2017-09-05 16:21:44 +02:00
# include "../gui/CAnimation.h"
2013-04-07 14:52:07 +03:00
# include "../gui/CCursorHandler.h"
2013-07-06 19:10:20 +03:00
# include "../gui/CGuiHandler.h"
# include "../gui/SDL_Extensions.h"
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
# include "../../CCallback.h"
2017-03-17 17:48:44 +02:00
# include "../../lib/CStack.h"
2013-07-06 19:10:20 +03:00
# include "../../lib/CTownHandler.h"
2014-07-15 10:14:49 +03:00
# include "../../lib/mapObjects/CGTownInstance.h"
2015-02-02 10:25:26 +02:00
# include "../../lib/spells/CSpellHandler.h"
2011-12-22 16:05:19 +03:00
CBattleAnimation : : CBattleAnimation ( CBattleInterface * _owner )
2020-10-06 01:27:04 +02:00
: owner ( _owner ) , ID ( _owner - > animIDhelper + + )
2013-07-16 21:12:47 +03:00
{
2017-08-10 22:47:58 +02:00
logAnim - > trace ( " Animation #%d created " , ID ) ;
2013-07-16 21:12:47 +03:00
}
CBattleAnimation : : ~ CBattleAnimation ( )
{
2017-08-10 22:47:58 +02:00
logAnim - > trace ( " Animation #%d deleted " , ID ) ;
2013-07-16 21:12:47 +03:00
}
2011-12-22 16:05:19 +03:00
void CBattleAnimation : : endAnim ( )
{
2017-08-10 22:47:58 +02:00
logAnim - > trace ( " Animation #%d ended, type is %s " , ID , typeid ( this ) . name ( ) ) ;
2013-06-29 16:05:48 +03:00
for ( auto & elem : owner - > pendingAnims )
2011-12-22 16:05:19 +03:00
{
2013-06-29 16:05:48 +03:00
if ( elem . first = = this )
2011-12-22 16:05:19 +03:00
{
2013-06-29 16:05:48 +03:00
elem . first = nullptr ;
2011-12-22 16:05:19 +03:00
}
}
}
bool CBattleAnimation : : isEarliest ( bool perStackConcurrency )
{
int lowestMoveID = owner - > animIDhelper + 5 ;
CBattleStackAnimation * thAnim = dynamic_cast < CBattleStackAnimation * > ( this ) ;
2017-09-05 19:45:29 +02:00
CEffectAnimation * thSen = dynamic_cast < CEffectAnimation * > ( this ) ;
2011-12-22 16:05:19 +03:00
2013-06-29 16:05:48 +03:00
for ( auto & elem : owner - > pendingAnims )
2011-12-22 16:05:19 +03:00
{
2013-08-06 18:25:51 +03:00
2013-06-29 16:05:48 +03:00
CBattleStackAnimation * stAnim = dynamic_cast < CBattleStackAnimation * > ( elem . first ) ;
2017-09-05 19:45:29 +02:00
CEffectAnimation * sen = dynamic_cast < CEffectAnimation * > ( elem . first ) ;
2011-12-22 16:05:19 +03:00
if ( perStackConcurrency & & stAnim & & thAnim & & stAnim - > stack - > ID ! = thAnim - > stack - > ID )
continue ;
2013-07-16 21:12:47 +03:00
if ( perStackConcurrency & & sen & & thSen & & sen ! = thSen )
2011-12-22 16:05:19 +03:00
continue ;
CReverseAnimation * revAnim = dynamic_cast < CReverseAnimation * > ( stAnim ) ;
if ( revAnim & & thAnim & & stAnim & & stAnim - > stack - > ID = = thAnim - > stack - > ID & & revAnim - > priority )
return false ;
2013-06-29 16:05:48 +03:00
if ( elem . first )
vstd : : amin ( lowestMoveID , elem . first - > ID ) ;
2011-12-22 16:05:19 +03:00
}
return ( ID = = lowestMoveID ) | | ( lowestMoveID = = ( owner - > animIDhelper + 5 ) ) ;
}
2013-07-06 19:10:20 +03:00
CBattleStackAnimation : : CBattleStackAnimation ( CBattleInterface * owner , const CStack * stack )
2020-10-06 01:27:04 +02:00
: CBattleAnimation ( owner ) ,
myAnim ( owner - > creAnims [ stack - > ID ] ) ,
stack ( stack )
2013-09-27 22:42:17 +03:00
{
assert ( myAnim ) ;
}
2011-12-22 16:05:19 +03:00
2020-01-27 02:18:07 +02:00
void CBattleStackAnimation : : shiftColor ( const ColorShifter * shifter )
2020-01-25 11:21:26 +02:00
{
assert ( myAnim ) ;
myAnim - > shiftColor ( shifter ) ;
}
2011-12-22 16:05:19 +03:00
void CAttackAnimation : : nextFrame ( )
{
2013-07-06 19:10:20 +03:00
if ( myAnim - > getType ( ) ! = group )
{
myAnim - > setType ( group ) ;
myAnim - > onAnimationReset + = std : : bind ( & CAttackAnimation : : endAnim , this ) ;
}
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
if ( ! soundPlayed )
2011-12-22 16:05:19 +03:00
{
if ( shooting )
CCS - > soundh - > playSound ( battle_sound ( attackingStack - > getCreature ( ) , shoot ) ) ;
else
CCS - > soundh - > playSound ( battle_sound ( attackingStack - > getCreature ( ) , attack ) ) ;
2013-07-06 19:10:20 +03:00
soundPlayed = true ;
2011-12-22 16:05:19 +03:00
}
2013-07-06 19:10:20 +03:00
CBattleAnimation : : nextFrame ( ) ;
}
void CAttackAnimation : : endAnim ( )
{
myAnim - > setType ( CCreatureAnim : : HOLDING ) ;
CBattleStackAnimation : : endAnim ( ) ;
2011-12-22 16:05:19 +03:00
}
bool CAttackAnimation : : checkInitialConditions ( )
{
2013-07-16 21:12:47 +03:00
for ( auto & elem : owner - > pendingAnims )
{
CBattleStackAnimation * stAnim = dynamic_cast < CBattleStackAnimation * > ( elem . first ) ;
CReverseAnimation * revAnim = dynamic_cast < CReverseAnimation * > ( stAnim ) ;
2017-05-28 13:39:36 +02:00
if ( revAnim & & attackedStack ) // enemy must be fully reversed
2013-07-16 21:12:47 +03:00
{
if ( revAnim - > stack - > ID = = attackedStack - > ID )
return false ;
}
}
2011-12-22 16:05:19 +03:00
return isEarliest ( false ) ;
}
CAttackAnimation : : CAttackAnimation ( CBattleInterface * _owner , const CStack * attacker , BattleHex _dest , const CStack * defender )
2016-11-27 16:48:18 +02:00
: CBattleStackAnimation ( _owner , attacker ) ,
shooting ( false ) , group ( CCreatureAnim : : SHOOT_FRONT ) ,
soundPlayed ( false ) ,
dest ( _dest ) , attackedStack ( defender ) , attackingStack ( attacker )
2011-12-22 16:05:19 +03:00
{
2013-06-26 14:18:27 +03:00
assert ( attackingStack & & " attackingStack is nullptr in CBattleAttack::CBattleAttack ! \n " ) ;
2017-07-20 06:08:49 +02:00
attackingStackPosBeforeReturn = attackingStack - > getPosition ( ) ;
2011-12-22 16:05:19 +03:00
}
CDefenceAnimation : : CDefenceAnimation ( StackAttackedInfo _attackedInfo , CBattleInterface * _owner )
2013-01-21 01:49:34 +03:00
: CBattleStackAnimation ( _owner , _attackedInfo . defender ) ,
2013-08-09 20:37:41 +03:00
attacker ( _attackedInfo . attacker ) , rangedAttack ( _attackedInfo . indirectAttack ) ,
2016-11-27 16:48:18 +02:00
killed ( _attackedInfo . killed ) , timeToWait ( 0 )
2014-09-23 16:02:30 +03:00
{
2017-08-10 22:47:58 +02:00
logAnim - > debug ( " Created defence anim for %s " , _attackedInfo . defender - > getName ( ) ) ;
2014-09-23 16:02:30 +03:00
}
2011-12-22 16:05:19 +03:00
bool CDefenceAnimation : : init ( )
{
2013-06-26 14:18:27 +03:00
if ( attacker = = nullptr & & owner - > battleEffects . size ( ) > 0 )
2011-12-22 16:05:19 +03:00
return false ;
ui32 lowestMoveID = owner - > animIDhelper + 5 ;
2013-06-29 16:05:48 +03:00
for ( auto & elem : owner - > pendingAnims )
2011-12-22 16:05:19 +03:00
{
2013-08-06 18:25:51 +03:00
2013-06-29 16:05:48 +03:00
CDefenceAnimation * defAnim = dynamic_cast < CDefenceAnimation * > ( elem . first ) ;
2011-12-22 16:05:19 +03:00
if ( defAnim & & defAnim - > stack - > ID ! = stack - > ID )
continue ;
2013-06-29 16:05:48 +03:00
CAttackAnimation * attAnim = dynamic_cast < CAttackAnimation * > ( elem . first ) ;
2011-12-22 16:05:19 +03:00
if ( attAnim & & attAnim - > stack - > ID ! = stack - > ID )
continue ;
2017-09-05 19:45:29 +02:00
CEffectAnimation * sen = dynamic_cast < CEffectAnimation * > ( elem . first ) ;
2013-08-09 20:37:41 +03:00
if ( sen )
continue ;
2013-06-29 16:05:48 +03:00
CReverseAnimation * animAsRev = dynamic_cast < CReverseAnimation * > ( elem . first ) ;
2011-12-22 16:05:19 +03:00
2013-08-06 18:25:51 +03:00
if ( animAsRev )
2011-12-22 16:05:19 +03:00
return false ;
2013-06-29 16:05:48 +03:00
if ( elem . first )
vstd : : amin ( lowestMoveID , elem . first - > ID ) ;
2011-12-22 16:05:19 +03:00
}
2013-07-16 21:12:47 +03:00
2011-12-22 16:05:19 +03:00
if ( ID > lowestMoveID )
return false ;
2013-08-06 18:25:51 +03:00
2011-12-22 16:05:19 +03:00
//reverse unit if necessary
2017-07-20 06:08:49 +02:00
if ( attacker & & owner - > getCurrentPlayerInterface ( ) - > cb - > isToReverse ( stack - > getPosition ( ) , attacker - > getPosition ( ) , owner - > creDir [ stack - > ID ] , attacker - > doubleWide ( ) , owner - > creDir [ attacker - > ID ] ) )
2011-12-22 16:05:19 +03:00
{
2017-07-20 06:08:49 +02:00
owner - > addNewAnim ( new CReverseAnimation ( owner , stack , stack - > getPosition ( ) , true ) ) ;
2011-12-22 16:05:19 +03:00
return false ;
}
//unit reversed
2013-07-16 21:12:47 +03:00
if ( rangedAttack ) //delay hit animation
2016-11-27 16:48:18 +02:00
{
2011-12-22 16:05:19 +03:00
for ( std : : list < ProjectileInfo > : : const_iterator it = owner - > projectiles . begin ( ) ; it ! = owner - > projectiles . end ( ) ; + + it )
{
if ( it - > creID = = attacker - > getCreature ( ) - > idNumber )
{
return false ;
}
}
}
2013-07-16 21:12:47 +03:00
// synchronize animation with attacker, unless defending or attacked by shooter:
// wait for 1/2 of attack animation
if ( ! rangedAttack & & getMyAnimType ( ) ! = CCreatureAnim : : DEFENCE )
2011-12-22 16:05:19 +03:00
{
2013-07-17 01:59:39 +03:00
float frameLength = AnimationControls : : getCreatureAnimationSpeed (
2020-10-06 01:27:04 +02:00
stack - > getCreature ( ) , owner - > creAnims [ stack - > ID ] . get ( ) , getMyAnimType ( ) ) ;
2013-07-16 21:12:47 +03:00
2013-07-17 01:59:39 +03:00
timeToWait = myAnim - > framesInGroup ( getMyAnimType ( ) ) * frameLength / 2 ;
2013-07-16 21:12:47 +03:00
myAnim - > setType ( CCreatureAnim : : HOLDING ) ;
2011-12-22 16:05:19 +03:00
}
else
{
2013-07-16 21:12:47 +03:00
timeToWait = 0 ;
startAnimation ( ) ;
2011-12-22 16:05:19 +03:00
}
return true ; //initialized successfuly
}
2013-07-16 21:12:47 +03:00
std : : string CDefenceAnimation : : getMySound ( )
{
if ( killed )
return battle_sound ( stack - > getCreature ( ) , killed ) ;
2017-07-20 06:08:49 +02:00
else if ( stack - > defendingAnim )
2013-07-16 21:12:47 +03:00
return battle_sound ( stack - > getCreature ( ) , defend ) ;
2017-07-20 06:08:49 +02:00
else
return battle_sound ( stack - > getCreature ( ) , wince ) ;
2013-07-16 21:12:47 +03:00
}
CCreatureAnim : : EAnimType CDefenceAnimation : : getMyAnimType ( )
{
if ( killed )
2017-09-08 13:25:12 +02:00
{
if ( rangedAttack & & myAnim - > framesInGroup ( CCreatureAnim : : DEATH_RANGED ) > 0 )
return CCreatureAnim : : DEATH_RANGED ;
else
return CCreatureAnim : : DEATH ;
}
2016-11-27 16:48:18 +02:00
2017-07-20 06:08:49 +02:00
if ( stack - > defendingAnim )
2013-07-16 21:12:47 +03:00
return CCreatureAnim : : DEFENCE ;
2017-07-20 06:08:49 +02:00
else
return CCreatureAnim : : HITTED ;
2013-07-16 21:12:47 +03:00
}
void CDefenceAnimation : : startAnimation ( )
{
CCS - > soundh - > playSound ( getMySound ( ) ) ;
myAnim - > setType ( getMyAnimType ( ) ) ;
myAnim - > onAnimationReset + = std : : bind ( & CDefenceAnimation : : endAnim , this ) ;
}
2011-12-22 16:05:19 +03:00
void CDefenceAnimation : : nextFrame ( )
{
2013-07-16 21:12:47 +03:00
if ( timeToWait > 0 )
{
timeToWait - = float ( GH . mainFPSmng - > getElapsedMilliseconds ( ) ) / 1000 ;
if ( timeToWait < = 0 )
startAnimation ( ) ;
}
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
CBattleAnimation : : nextFrame ( ) ;
2011-12-22 16:05:19 +03:00
}
void CDefenceAnimation : : endAnim ( )
{
2017-09-05 19:45:29 +02:00
if ( killed )
{
2017-09-08 13:25:12 +02:00
if ( rangedAttack & & myAnim - > framesInGroup ( CCreatureAnim : : DEAD_RANGED ) > 0 )
myAnim - > setType ( CCreatureAnim : : DEAD_RANGED ) ;
else
myAnim - > setType ( CCreatureAnim : : DEAD ) ;
2017-09-05 19:45:29 +02:00
}
2013-07-06 19:10:20 +03:00
else
2017-09-05 19:45:29 +02:00
{
2013-07-06 19:10:20 +03:00
myAnim - > setType ( CCreatureAnim : : HOLDING ) ;
2017-09-05 19:45:29 +02:00
}
2011-12-22 16:05:19 +03:00
CBattleAnimation : : endAnim ( ) ;
delete this ;
}
2016-11-27 16:48:18 +02:00
CDummyAnimation : : CDummyAnimation ( CBattleInterface * _owner , int howManyFrames )
2013-07-06 19:10:20 +03:00
: CBattleAnimation ( _owner ) , counter ( 0 ) , howMany ( howManyFrames )
2015-09-12 23:01:36 +02:00
{
2017-08-11 13:38:10 +02:00
logAnim - > debug ( " Created dummy animation for %d frames " , howManyFrames ) ;
2015-09-12 23:01:36 +02:00
}
2011-12-22 16:05:19 +03:00
bool CDummyAnimation : : init ( )
{
return true ;
}
void CDummyAnimation : : nextFrame ( )
{
counter + + ;
if ( counter > howMany )
endAnim ( ) ;
}
void CDummyAnimation : : endAnim ( )
{
CBattleAnimation : : endAnim ( ) ;
delete this ;
}
bool CMeleeAttackAnimation : : init ( )
{
2017-09-08 13:25:12 +02:00
if ( ! CAttackAnimation : : checkInitialConditions ( ) )
2011-12-22 16:05:19 +03:00
return false ;
2013-07-06 19:10:20 +03:00
if ( ! attackingStack | | myAnim - > isDead ( ) )
2011-12-22 16:05:19 +03:00
{
endAnim ( ) ;
return false ;
}
2017-07-20 06:08:49 +02:00
bool toReverse = owner - > getCurrentPlayerInterface ( ) - > cb - > isToReverse ( attackingStackPosBeforeReturn , attackedStack - > getPosition ( ) , owner - > creDir [ stack - > ID ] , attackedStack - > doubleWide ( ) , owner - > creDir [ attackedStack - > ID ] ) ;
2011-12-22 16:05:19 +03:00
2017-09-08 13:25:12 +02:00
if ( toReverse )
2011-12-22 16:05:19 +03:00
{
owner - > addNewAnim ( new CReverseAnimation ( owner , stack , attackingStackPosBeforeReturn , true ) ) ;
return false ;
}
2013-07-16 21:12:47 +03:00
// opponent must face attacker ( = different directions) before he can be attacked
2017-09-08 13:25:12 +02:00
if ( attackingStack & & attackedStack & &
2020-10-06 01:27:04 +02:00
owner - > creDir [ attackingStack - > ID ] = = owner - > creDir [ attackedStack - > ID ] )
2013-07-16 21:12:47 +03:00
return false ;
2011-12-22 16:05:19 +03:00
//reversed
shooting = false ;
2017-09-08 13:25:12 +02:00
static const CCreatureAnim : : EAnimType mutPosToGroup [ ] =
{
CCreatureAnim : : ATTACK_UP ,
CCreatureAnim : : ATTACK_UP ,
CCreatureAnim : : ATTACK_FRONT ,
CCreatureAnim : : ATTACK_DOWN ,
CCreatureAnim : : ATTACK_DOWN ,
CCreatureAnim : : ATTACK_FRONT
} ;
static const CCreatureAnim : : EAnimType mutPosToGroup2H [ ] =
{
CCreatureAnim : : VCMI_2HEX_UP ,
CCreatureAnim : : VCMI_2HEX_UP ,
CCreatureAnim : : VCMI_2HEX_FRONT ,
CCreatureAnim : : VCMI_2HEX_DOWN ,
CCreatureAnim : : VCMI_2HEX_DOWN ,
CCreatureAnim : : VCMI_2HEX_FRONT
} ;
2011-12-22 16:05:19 +03:00
2017-07-01 10:34:00 +02:00
int revShiftattacker = ( attackingStack - > side = = BattleSide : : ATTACKER ? - 1 : 1 ) ;
2011-12-22 16:05:19 +03:00
int mutPos = BattleHex : : mutualPosition ( attackingStackPosBeforeReturn , dest ) ;
if ( mutPos = = - 1 & & attackingStack - > doubleWide ( ) )
{
2017-07-20 06:08:49 +02:00
mutPos = BattleHex : : mutualPosition ( attackingStackPosBeforeReturn + revShiftattacker , attackedStack - > getPosition ( ) ) ;
2011-12-22 16:05:19 +03:00
}
if ( mutPos = = - 1 & & attackedStack - > doubleWide ( ) )
{
mutPos = BattleHex : : mutualPosition ( attackingStackPosBeforeReturn , attackedStack - > occupiedHex ( ) ) ;
}
if ( mutPos = = - 1 & & attackedStack - > doubleWide ( ) & & attackingStack - > doubleWide ( ) )
{
mutPos = BattleHex : : mutualPosition ( attackingStackPosBeforeReturn + revShiftattacker , attackedStack - > occupiedHex ( ) ) ;
}
switch ( mutPos ) //attack direction
{
2017-09-08 13:25:12 +02:00
case 0 :
case 1 :
case 2 :
case 3 :
case 4 :
case 5 :
2011-12-22 16:05:19 +03:00
group = mutPosToGroup [ mutPos ] ;
2020-10-06 01:27:04 +02:00
if ( attackingStack - > hasBonusOfType ( Bonus : : TWO_HEX_ATTACK_BREATH ) )
2017-09-08 13:25:12 +02:00
{
CCreatureAnim : : EAnimType group2H = mutPosToGroup2H [ mutPos ] ;
if ( myAnim - > framesInGroup ( group2H ) > 0 )
group = group2H ;
}
2011-12-22 16:05:19 +03:00
break ;
default :
2017-08-11 13:38:10 +02:00
logGlobal - > error ( " Critical Error! Wrong dest in stackAttacking! dest: %d; attacking stack pos: %d; mutual pos: %d " , dest . hex , attackingStackPosBeforeReturn , mutPos ) ;
2011-12-22 16:05:19 +03:00
group = CCreatureAnim : : ATTACK_FRONT ;
break ;
}
return true ;
}
CMeleeAttackAnimation : : CMeleeAttackAnimation ( CBattleInterface * _owner , const CStack * attacker , BattleHex _dest , const CStack * _attacked )
2016-11-27 16:48:18 +02:00
: CAttackAnimation ( _owner , attacker , _dest , _attacked )
2014-09-23 16:02:30 +03:00
{
2017-08-10 20:59:55 +02:00
logAnim - > debug ( " Created melee attack anim for %s " , attacker - > getName ( ) ) ;
2014-09-23 16:02:30 +03:00
}
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
void CMeleeAttackAnimation : : endAnim ( )
2011-12-22 16:05:19 +03:00
{
2013-07-06 19:10:20 +03:00
CAttackAnimation : : endAnim ( ) ;
delete this ;
2011-12-22 16:05:19 +03:00
}
2013-07-06 19:10:20 +03:00
bool CMovementAnimation : : shouldRotate ( )
2011-12-22 16:05:19 +03:00
{
2013-07-07 22:44:08 +03:00
Point begPosition = CClickableHex : : getXYUnitAnim ( oldPos , stack , owner ) ;
Point endPosition = CClickableHex : : getXYUnitAnim ( nextHex , stack , owner ) ;
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
if ( ( begPosition . x > endPosition . x ) & & owner - > creDir [ stack - > ID ] = = true )
{
return true ;
}
else if ( ( begPosition . x < endPosition . x ) & & owner - > creDir [ stack - > ID ] = = false )
{
return true ;
}
return false ;
2011-12-22 16:05:19 +03:00
}
bool CMovementAnimation : : init ( )
{
2013-07-06 19:10:20 +03:00
if ( ! isEarliest ( false ) )
2011-12-22 16:05:19 +03:00
return false ;
2013-07-06 19:10:20 +03:00
if ( ! stack | | myAnim - > isDead ( ) )
2011-12-22 16:05:19 +03:00
{
endAnim ( ) ;
return false ;
}
2013-07-06 19:10:20 +03:00
if ( owner - > creAnims [ stack - > ID ] - > framesInGroup ( CCreatureAnim : : MOVING ) = = 0 | |
stack - > hasBonus ( Selector : : typeSubtype ( Bonus : : FLYING , 1 ) ) )
2012-09-29 11:35:31 +03:00
{
2013-07-06 19:10:20 +03:00
//no movement or teleport, end immediately
2012-09-29 11:35:31 +03:00
endAnim ( ) ;
return false ;
}
2011-12-22 16:05:19 +03:00
//reverse unit if necessary
2013-07-06 19:10:20 +03:00
if ( shouldRotate ( ) )
2011-12-22 16:05:19 +03:00
{
2013-07-06 19:10:20 +03:00
// it seems that H3 does NOT plays full rotation animation here in most situations
// Logical since it takes quite a lot of time
if ( curentMoveIndex = = 0 ) // full rotation only for moving towards first tile.
{
owner - > addNewAnim ( new CReverseAnimation ( owner , stack , oldPos , true ) ) ;
return false ;
}
else
{
CReverseAnimation : : rotateStack ( owner , stack , oldPos ) ;
}
2011-12-22 16:05:19 +03:00
}
2013-07-06 19:10:20 +03:00
if ( myAnim - > getType ( ) ! = CCreatureAnim : : MOVING )
2011-12-22 16:05:19 +03:00
{
2013-07-06 19:10:20 +03:00
myAnim - > setType ( CCreatureAnim : : MOVING ) ;
2011-12-22 16:05:19 +03:00
}
2013-07-06 19:10:20 +03:00
if ( owner - > moveSoundHander = = - 1 )
2012-05-15 19:29:40 +03:00
{
2013-07-06 19:10:20 +03:00
owner - > moveSoundHander = CCS - > soundh - > playSound ( battle_sound ( stack - > getCreature ( ) , move ) , - 1 ) ;
2012-05-15 19:29:40 +03:00
}
2011-12-22 16:05:19 +03:00
2013-07-07 22:44:08 +03:00
Point begPosition = CClickableHex : : getXYUnitAnim ( oldPos , stack , owner ) ;
Point endPosition = CClickableHex : : getXYUnitAnim ( nextHex , stack , owner ) ;
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
timeToMove = AnimationControls : : getMovementDuration ( stack - > getCreature ( ) ) ;
begX = begPosition . x ;
begY = begPosition . y ;
progress = 0 ;
distanceX = endPosition . x - begPosition . x ;
distanceY = endPosition . y - begPosition . y ;
2020-11-11 21:43:40 +02:00
if ( stack - > hasBonus ( Selector : : type ( ) ( Bonus : : FLYING ) ) )
2011-12-22 16:05:19 +03:00
{
2020-10-01 10:38:06 +02:00
float distance = static_cast < float > ( sqrt ( distanceX * distanceX + distanceY * distanceY ) ) ;
2013-07-06 19:10:20 +03:00
2013-07-16 21:12:47 +03:00
timeToMove * = AnimationControls : : getFlightDistance ( stack - > getCreature ( ) ) / distance ;
2011-12-22 16:05:19 +03:00
}
return true ;
}
void CMovementAnimation : : nextFrame ( )
{
2013-07-06 19:10:20 +03:00
progress + = float ( GH . mainFPSmng - > getElapsedMilliseconds ( ) ) / 1000 * timeToMove ;
2011-12-22 16:05:19 +03:00
//moving instructions
2013-07-06 19:10:20 +03:00
myAnim - > pos . x = static_cast < Sint16 > ( begX + distanceX * progress ) ;
myAnim - > pos . y = static_cast < Sint16 > ( begY + distanceY * progress ) ;
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
CBattleAnimation : : nextFrame ( ) ;
if ( progress > = 1.0 )
2011-12-22 16:05:19 +03:00
{
// Sets the position of the creature animation sprites
2013-07-07 22:44:08 +03:00
Point coords = CClickableHex : : getXYUnitAnim ( nextHex , stack , owner ) ;
2013-07-06 19:10:20 +03:00
myAnim - > pos = coords ;
2011-12-22 16:05:19 +03:00
// true if creature haven't reached the final destination hex
2013-07-06 19:10:20 +03:00
if ( ( curentMoveIndex + 1 ) < destTiles . size ( ) )
2011-12-22 16:05:19 +03:00
{
// update the next hex field which has to be reached by the stack
2013-07-06 19:10:20 +03:00
curentMoveIndex + + ;
oldPos = nextHex ;
nextHex = destTiles [ curentMoveIndex ] ;
2011-12-22 16:05:19 +03:00
// re-init animation
2013-06-29 16:05:48 +03:00
for ( auto & elem : owner - > pendingAnims )
2011-12-22 16:05:19 +03:00
{
2013-06-29 16:05:48 +03:00
if ( elem . first = = this )
2011-12-22 16:05:19 +03:00
{
2013-06-29 16:05:48 +03:00
elem . second = false ;
2011-12-22 16:05:19 +03:00
break ;
}
}
}
else
endAnim ( ) ;
}
}
void CMovementAnimation : : endAnim ( )
{
2013-07-06 19:10:20 +03:00
assert ( stack ) ;
2011-12-22 16:05:19 +03:00
2013-07-07 22:44:08 +03:00
myAnim - > pos = CClickableHex : : getXYUnitAnim ( nextHex , stack , owner ) ;
2011-12-22 16:05:19 +03:00
CBattleAnimation : : endAnim ( ) ;
2013-07-06 19:10:20 +03:00
owner - > addNewAnim ( new CMovementEndAnimation ( owner , stack , nextHex ) ) ;
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
if ( owner - > moveSoundHander ! = - 1 )
2011-12-22 16:05:19 +03:00
{
2013-07-06 19:10:20 +03:00
CCS - > soundh - > stopSound ( owner - > moveSoundHander ) ;
owner - > moveSoundHander = - 1 ;
2011-12-22 16:05:19 +03:00
}
delete this ;
}
CMovementAnimation : : CMovementAnimation ( CBattleInterface * _owner , const CStack * _stack , std : : vector < BattleHex > _destTiles , int _distance )
2020-10-06 01:27:04 +02:00
: CBattleStackAnimation ( _owner , _stack ) ,
destTiles ( _destTiles ) ,
curentMoveIndex ( 0 ) ,
oldPos ( stack - > getPosition ( ) ) ,
begX ( 0 ) , begY ( 0 ) ,
distanceX ( 0 ) , distanceY ( 0 ) ,
timeToMove ( 0.0 ) ,
progress ( 0.0 ) ,
nextHex ( destTiles . front ( ) )
2011-12-22 16:05:19 +03:00
{
2017-08-10 20:59:55 +02:00
logAnim - > debug ( " Created movement anim for %s " , stack - > getName ( ) ) ;
2011-12-22 16:05:19 +03:00
}
CMovementEndAnimation : : CMovementEndAnimation ( CBattleInterface * _owner , const CStack * _stack , BattleHex destTile )
2016-11-27 16:48:18 +02:00
: CBattleStackAnimation ( _owner , _stack ) , destinationTile ( destTile )
2014-09-23 16:02:30 +03:00
{
2017-08-10 20:59:55 +02:00
logAnim - > debug ( " Created movement end anim for %s " , stack - > getName ( ) ) ;
2014-09-23 16:02:30 +03:00
}
2011-12-22 16:05:19 +03:00
bool CMovementEndAnimation : : init ( )
{
if ( ! isEarliest ( true ) )
return false ;
2013-07-06 19:10:20 +03:00
if ( ! stack | | myAnim - > framesInGroup ( CCreatureAnim : : MOVE_END ) = = 0 | |
myAnim - > isDead ( ) )
2011-12-22 16:05:19 +03:00
{
endAnim ( ) ;
return false ;
}
CCS - > soundh - > playSound ( battle_sound ( stack - > getCreature ( ) , endMoving ) ) ;
2013-07-06 19:10:20 +03:00
myAnim - > setType ( CCreatureAnim : : MOVE_END ) ;
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
myAnim - > onAnimationReset + = std : : bind ( & CMovementEndAnimation : : endAnim , this ) ;
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
return true ;
2011-12-22 16:05:19 +03:00
}
void CMovementEndAnimation : : endAnim ( )
{
CBattleAnimation : : endAnim ( ) ;
2013-07-06 19:10:20 +03:00
if ( myAnim - > getType ( ) ! = CCreatureAnim : : DEAD )
myAnim - > setType ( CCreatureAnim : : HOLDING ) ; //resetting to default
2011-12-22 16:05:19 +03:00
CCS - > curh - > show ( ) ;
delete this ;
}
CMovementStartAnimation : : CMovementStartAnimation ( CBattleInterface * _owner , const CStack * _stack )
2016-11-27 16:48:18 +02:00
: CBattleStackAnimation ( _owner , _stack )
2014-09-23 16:02:30 +03:00
{
2017-08-10 20:59:55 +02:00
logAnim - > debug ( " Created movement start anim for %s " , stack - > getName ( ) ) ;
2014-09-23 16:02:30 +03:00
}
2011-12-22 16:05:19 +03:00
bool CMovementStartAnimation : : init ( )
{
if ( ! isEarliest ( false ) )
return false ;
2013-07-06 19:10:20 +03:00
if ( ! stack | | myAnim - > isDead ( ) )
2011-12-22 16:05:19 +03:00
{
CMovementStartAnimation : : endAnim ( ) ;
return false ;
}
CCS - > soundh - > playSound ( battle_sound ( stack - > getCreature ( ) , startMoving ) ) ;
2013-07-06 19:10:20 +03:00
myAnim - > setType ( CCreatureAnim : : MOVE_START ) ;
myAnim - > onAnimationReset + = std : : bind ( & CMovementStartAnimation : : endAnim , this ) ;
2011-12-22 16:05:19 +03:00
return true ;
}
void CMovementStartAnimation : : endAnim ( )
{
CBattleAnimation : : endAnim ( ) ;
delete this ;
}
CReverseAnimation : : CReverseAnimation ( CBattleInterface * _owner , const CStack * stack , BattleHex dest , bool _priority )
2013-07-06 19:10:20 +03:00
: CBattleStackAnimation ( _owner , stack ) , hex ( dest ) , priority ( _priority )
2014-09-23 16:02:30 +03:00
{
2017-08-10 20:59:55 +02:00
logAnim - > debug ( " Created reverse anim for %s " , stack - > getName ( ) ) ;
2014-09-23 16:02:30 +03:00
}
2011-12-22 16:05:19 +03:00
bool CReverseAnimation : : init ( )
{
2013-07-06 19:10:20 +03:00
if ( myAnim = = nullptr | | myAnim - > isDead ( ) )
2011-12-22 16:05:19 +03:00
{
endAnim ( ) ;
return false ; //there is no such creature
}
if ( ! priority & & ! isEarliest ( false ) )
return false ;
2013-07-06 19:10:20 +03:00
if ( myAnim - > framesInGroup ( CCreatureAnim : : TURN_L ) )
2011-12-22 16:05:19 +03:00
{
2013-07-06 19:10:20 +03:00
myAnim - > setType ( CCreatureAnim : : TURN_L ) ;
myAnim - > onAnimationReset + = std : : bind ( & CReverseAnimation : : setupSecondPart , this ) ;
2011-12-22 16:05:19 +03:00
}
2013-07-06 19:10:20 +03:00
else
2011-12-22 16:05:19 +03:00
{
2013-07-06 19:10:20 +03:00
setupSecondPart ( ) ;
2011-12-22 16:05:19 +03:00
}
2013-07-06 19:10:20 +03:00
return true ;
2011-12-22 16:05:19 +03:00
}
void CReverseAnimation : : endAnim ( )
{
CBattleAnimation : : endAnim ( ) ;
if ( stack - > alive ( ) ) //don't do that if stack is dead
2013-07-06 19:10:20 +03:00
myAnim - > setType ( CCreatureAnim : : HOLDING ) ;
2011-12-22 16:05:19 +03:00
delete this ;
}
2013-07-06 19:10:20 +03:00
void CReverseAnimation : : rotateStack ( CBattleInterface * owner , const CStack * stack , BattleHex hex )
2011-12-22 16:05:19 +03:00
{
2012-09-16 17:36:31 +03:00
owner - > creDir [ stack - > ID ] = ! owner - > creDir [ stack - > ID ] ;
2013-07-07 22:44:08 +03:00
owner - > creAnims [ stack - > ID ] - > pos = CClickableHex : : getXYUnitAnim ( hex , stack , owner ) ;
2013-07-06 19:10:20 +03:00
}
void CReverseAnimation : : setupSecondPart ( )
{
if ( ! stack )
{
endAnim ( ) ;
return ;
}
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
rotateStack ( owner , stack , hex ) ;
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
if ( myAnim - > framesInGroup ( CCreatureAnim : : TURN_R ) )
{
myAnim - > setType ( CCreatureAnim : : TURN_R ) ;
myAnim - > onAnimationReset + = std : : bind ( & CReverseAnimation : : endAnim , this ) ;
}
2011-12-22 16:05:19 +03:00
else
endAnim ( ) ;
}
2017-09-08 13:25:12 +02:00
CRangedAttackAnimation : : CRangedAttackAnimation ( CBattleInterface * owner_ , const CStack * attacker , BattleHex dest_ , const CStack * defender )
: CAttackAnimation ( owner_ , attacker , dest_ , defender )
{
}
2011-12-22 16:05:19 +03:00
CShootingAnimation : : CShootingAnimation ( CBattleInterface * _owner , const CStack * attacker , BattleHex _dest , const CStack * _attacked , bool _catapult , int _catapultDmg )
2017-09-08 13:25:12 +02:00
: CRangedAttackAnimation ( _owner , attacker , _dest , _attacked ) ,
catapultDamage ( _catapultDmg )
2014-09-23 16:02:30 +03:00
{
2017-08-10 20:59:55 +02:00
logAnim - > debug ( " Created shooting anim for %s " , stack - > getName ( ) ) ;
2014-09-23 16:02:30 +03:00
}
2011-12-22 16:05:19 +03:00
bool CShootingAnimation : : init ( )
{
if ( ! CAttackAnimation : : checkInitialConditions ( ) )
return false ;
const CStack * shooter = attackingStack ;
2013-07-06 19:10:20 +03:00
if ( ! shooter | | myAnim - > isDead ( ) )
2011-12-22 16:05:19 +03:00
{
endAnim ( ) ;
return false ;
}
2013-01-31 20:52:10 +03:00
//reverse unit if necessary
2017-07-20 06:08:49 +02:00
if ( attackingStack & & attackedStack & & owner - > getCurrentPlayerInterface ( ) - > cb - > isToReverse ( attackingStack - > getPosition ( ) , attackedStack - > getPosition ( ) , owner - > creDir [ attackingStack - > ID ] , attackingStack - > doubleWide ( ) , owner - > creDir [ attackedStack - > ID ] ) )
2013-01-31 20:52:10 +03:00
{
2017-07-20 06:08:49 +02:00
owner - > addNewAnim ( new CReverseAnimation ( owner , attackingStack , attackingStack - > getPosition ( ) , true ) ) ;
2013-01-31 20:52:10 +03:00
return false ;
}
2013-07-16 21:12:47 +03:00
// opponent must face attacker ( = different directions) before he can be attacked
2017-07-20 06:08:49 +02:00
//FIXME: this cause freeze
// if (attackingStack && attackedStack &&
// owner->creDir[attackingStack->ID] == owner->creDir[attackedStack->ID])
// return false;
2013-07-16 21:12:47 +03:00
2011-12-22 16:05:19 +03:00
// Create the projectile animation
2013-04-04 21:20:25 +03:00
//maximal angle in radians between straight horizontal line and shooting line for which shot is considered to be straight (absoulte value)
static const double straightAngle = 0.2 ;
2011-12-22 16:05:19 +03:00
// Get further info about the shooter e.g. relative pos of projectile to unit.
// If the creature id is 149 then it's a arrow tower which has no additional info so get the
// actual arrow tower shooter instead.
const CCreature * shooterInfo = shooter - > getCreature ( ) ;
2021-01-17 14:02:58 +02:00
if ( shooterInfo - > idNumber = = CreatureID : : ARROW_TOWERS )
2011-12-22 16:05:19 +03:00
{
2012-10-05 21:03:49 +03:00
int creID = owner - > siegeH - > town - > town - > clientInfo . siegeShooter ;
2011-12-22 16:05:19 +03:00
shooterInfo = CGI - > creh - > creatures [ creID ] ;
}
2021-01-17 14:02:58 +02:00
if ( ! shooterInfo - > animation . missleFrameAngles . size ( ) )
logAnim - > error ( " Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead... "
, shooterInfo - > nameSing ) ;
auto & angles = shooterInfo - > animation . missleFrameAngles . size ( )
? shooterInfo - > animation . missleFrameAngles
: CGI - > creh - > creatures [ CreatureID : : ARCHER ] - > animation . missleFrameAngles ;
2011-12-22 16:05:19 +03:00
ProjectileInfo spi ;
2013-07-06 19:10:20 +03:00
spi . shotDone = false ;
2011-12-22 16:05:19 +03:00
spi . creID = shooter - > getCreature ( ) - > idNumber ;
spi . stackID = shooter - > ID ;
2013-04-04 16:18:38 +03:00
// reverse if creature is facing right OR this is non-existing stack that is not tower (war machines)
spi . reverse = attackingStack ? ! owner - > creDir [ attackingStack - > ID ] : shooter - > getCreature ( ) - > idNumber ! = CreatureID : : ARROW_TOWERS ;
2011-12-22 16:05:19 +03:00
spi . step = 0 ;
spi . frameNum = 0 ;
2013-04-04 21:20:25 +03:00
Point fromPos ;
Point destPos ;
2011-12-22 16:05:19 +03:00
2013-04-04 16:18:38 +03:00
// NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise
2013-04-04 21:20:25 +03:00
fromPos = owner - > creAnims [ spi . stackID ] - > pos . topLeft ( ) ;
2013-04-04 16:18:38 +03:00
//xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner);
2011-12-22 16:05:19 +03:00
2013-07-07 22:44:08 +03:00
destPos = CClickableHex : : getXYUnitAnim ( dest , attackedStack , owner ) ;
2013-04-04 16:18:38 +03:00
2013-04-04 21:20:25 +03:00
// to properly translate coordinates when shooter is rotated
int multiplier = spi . reverse ? - 1 : 1 ;
2013-04-04 16:18:38 +03:00
2013-04-04 22:54:58 +03:00
double projectileAngle = atan2 ( fabs ( ( double ) destPos . y - fromPos . y ) , fabs ( ( double ) destPos . x - fromPos . x ) ) ;
2017-07-20 06:08:49 +02:00
if ( shooter - > getPosition ( ) < dest )
2013-04-04 21:20:25 +03:00
projectileAngle = - projectileAngle ;
2011-12-22 16:05:19 +03:00
2013-04-04 21:20:25 +03:00
// Calculate projectile start position. Offsets are read out of the CRANIM.TXT.
if ( projectileAngle > straightAngle )
{
//upper shot
spi . x = fromPos . x + 222 + ( - 25 + shooterInfo - > animation . upperRightMissleOffsetX ) * multiplier ;
spi . y = fromPos . y + 265 + shooterInfo - > animation . upperRightMissleOffsetY ;
}
else if ( projectileAngle < - straightAngle )
{
//lower shot
spi . x = fromPos . x + 222 + ( - 25 + shooterInfo - > animation . lowerRightMissleOffsetX ) * multiplier ;
spi . y = fromPos . y + 265 + shooterInfo - > animation . lowerRightMissleOffsetY ;
}
else
{
//straight shot
spi . x = fromPos . x + 222 + ( - 25 + shooterInfo - > animation . rightMissleOffsetX ) * multiplier ;
spi . y = fromPos . y + 265 + shooterInfo - > animation . rightMissleOffsetY ;
}
destPos + = Point ( 225 , 225 ) ;
2011-12-22 16:05:19 +03:00
2013-04-04 21:20:25 +03:00
// recalculate angle taking in account offsets
//projectileAngle = atan2(fabs(destPos.y - spi.y), fabs(destPos.x - spi.x));
//if(shooter->position < dest)
// projectileAngle = -projectileAngle;
if ( attackedStack )
{
2013-07-06 19:10:20 +03:00
double animSpeed = AnimationControls : : getProjectileSpeed ( ) ; // flight speed of projectile
2013-04-04 21:20:25 +03:00
spi . lastStep = static_cast < int > ( sqrt ( static_cast < double > ( ( destPos . x - spi . x ) * ( destPos . x - spi . x ) + ( destPos . y - spi . y ) * ( destPos . y - spi . y ) ) ) / animSpeed ) ;
2011-12-22 16:05:19 +03:00
if ( spi . lastStep = = 0 )
spi . lastStep = 1 ;
2013-04-04 21:20:25 +03:00
spi . dx = ( destPos . x - spi . x ) / spi . lastStep ;
spi . dy = ( destPos . y - spi . y ) / spi . lastStep ;
2011-12-22 16:05:19 +03:00
}
2016-11-27 16:48:18 +02:00
else
2011-12-22 16:05:19 +03:00
{
// Catapult attack
2020-10-01 10:38:06 +02:00
spi . catapultInfo . reset ( new CatapultProjectileInfo ( Point ( ( int ) spi . x , ( int ) spi . y ) , destPos ) ) ;
2011-12-22 16:05:19 +03:00
2013-07-06 19:10:20 +03:00
double animSpeed = AnimationControls : : getProjectileSpeed ( ) / 10 ;
2020-10-01 10:38:06 +02:00
spi . lastStep = static_cast < int > ( std : : abs ( ( destPos . x - spi . x ) / animSpeed ) ) ;
2013-04-04 21:20:25 +03:00
spi . dx = animSpeed ;
spi . dy = 0 ;
2011-12-22 16:05:19 +03:00
2018-03-30 13:02:04 +02:00
auto img = owner - > idToProjectile [ spi . creID ] - > getImage ( 0 ) ;
2011-12-22 16:05:19 +03:00
2013-04-04 21:20:25 +03:00
// Add explosion anim
2017-09-05 19:04:17 +02:00
Point animPos ( destPos . x - 126 + img - > width ( ) / 2 ,
2020-10-06 01:27:04 +02:00
destPos . y - 105 + img - > height ( ) / 2 ) ;
2013-04-04 21:20:25 +03:00
2017-09-05 19:45:29 +02:00
owner - > addNewAnim ( new CEffectAnimation ( owner , catapultDamage ? " SGEXPL.DEF " : " CSGRCK.DEF " , animPos . x , animPos . y ) ) ;
2011-12-22 16:05:19 +03:00
}
2013-04-04 16:18:38 +03:00
double pi = boost : : math : : constants : : pi < double > ( ) ;
2017-02-02 21:27:51 +02:00
if ( owner - > idToProjectile . count ( spi . creID ) = = 0 ) //in some cases (known one: hero grants shooter bonus to unit) the shooter stack's projectile may not be properly initialized
owner - > initStackProjectile ( shooter ) ;
2013-04-04 16:18:38 +03:00
// only frames below maxFrame are usable: anything higher is either no present or we don't know when it should be used
2017-09-05 19:04:17 +02:00
size_t maxFrame = std : : min < size_t > ( angles . size ( ) , owner - > idToProjectile . at ( spi . creID ) - > size ( 0 ) ) ;
2013-04-04 16:18:38 +03:00
assert ( maxFrame > 0 ) ;
// values in angles array indicate position from which this frame was rendered, in degrees.
// find frame that has closest angle to one that we need for this shot
size_t bestID = 0 ;
double bestDiff = fabs ( angles [ 0 ] / 180 * pi - projectileAngle ) ;
for ( size_t i = 1 ; i < maxFrame ; i + + )
2011-12-22 16:05:19 +03:00
{
2013-04-04 16:18:38 +03:00
double currentDiff = fabs ( angles [ i ] / 180 * pi - projectileAngle ) ;
if ( currentDiff < bestDiff )
{
bestID = i ;
bestDiff = currentDiff ;
}
2011-12-22 16:05:19 +03:00
}
2020-10-01 10:38:06 +02:00
spi . frameNum = static_cast < int > ( bestID ) ;
2013-04-04 16:18:38 +03:00
2011-12-22 16:05:19 +03:00
// Set projectile animation start delay which is specified in frames
2013-03-02 19:55:51 +03:00
spi . animStartDelay = shooterInfo - > animation . attackClimaxFrame ;
2011-12-22 16:05:19 +03:00
owner - > projectiles . push_back ( spi ) ;
//attack animation
shooting = true ;
2017-09-08 13:25:12 +02:00
if ( projectileAngle > straightAngle )
2011-12-22 16:05:19 +03:00
group = CCreatureAnim : : SHOOT_UP ;
2017-09-08 13:25:12 +02:00
else if ( projectileAngle < - straightAngle )
2011-12-22 16:05:19 +03:00
group = CCreatureAnim : : SHOOT_DOWN ;
else //straight shot
group = CCreatureAnim : : SHOOT_FRONT ;
return true ;
}
void CShootingAnimation : : nextFrame ( )
{
2013-07-06 19:10:20 +03:00
for ( auto & it : owner - > pendingAnims )
2011-12-22 16:05:19 +03:00
{
2013-07-06 19:10:20 +03:00
CMovementStartAnimation * anim = dynamic_cast < CMovementStartAnimation * > ( it . first ) ;
CReverseAnimation * anim2 = dynamic_cast < CReverseAnimation * > ( it . first ) ;
2011-12-22 16:05:19 +03:00
if ( ( anim & & anim - > stack - > ID = = stack - > ID ) | | ( anim2 & & anim2 - > stack - > ID = = stack - > ID & & anim2 - > priority ) )
return ;
}
CAttackAnimation : : nextFrame ( ) ;
}
void CShootingAnimation : : endAnim ( )
{
2017-09-08 13:25:12 +02:00
// play wall hit/miss sound for catapult attack
if ( ! attackedStack )
{
if ( catapultDamage > 0 )
{
CCS - > soundh - > playSound ( " WALLHIT " ) ;
}
else
{
CCS - > soundh - > playSound ( " WALLMISS " ) ;
}
}
2014-06-01 19:48:40 +03:00
2013-07-06 19:10:20 +03:00
CAttackAnimation : : endAnim ( ) ;
2011-12-22 16:05:19 +03:00
delete this ;
}
2017-09-08 13:25:12 +02:00
CCastAnimation : : CCastAnimation ( CBattleInterface * owner_ , const CStack * attacker , BattleHex dest_ , const CStack * defender )
: CRangedAttackAnimation ( owner_ , attacker , dest_ , defender )
{
if ( ! dest_ . isValid ( ) & & defender )
2017-07-20 06:08:49 +02:00
dest = defender - > getPosition ( ) ;
2017-09-08 13:25:12 +02:00
}
bool CCastAnimation : : init ( )
{
if ( ! CAttackAnimation : : checkInitialConditions ( ) )
return false ;
if ( ! attackingStack | | myAnim - > isDead ( ) )
{
endAnim ( ) ;
return false ;
}
//reverse unit if necessary
if ( attackedStack )
{
2020-10-06 01:27:04 +02:00
if ( owner - > getCurrentPlayerInterface ( ) - > cb - > isToReverse ( attackingStack - > getPosition ( ) , attackedStack - > getPosition ( ) , owner - > creDir [ attackingStack - > ID ] , attackingStack - > doubleWide ( ) , owner - > creDir [ attackedStack - > ID ] ) )
2017-09-08 13:25:12 +02:00
{
2017-07-20 06:08:49 +02:00
owner - > addNewAnim ( new CReverseAnimation ( owner , attackingStack , attackingStack - > getPosition ( ) , true ) ) ;
2017-09-08 13:25:12 +02:00
return false ;
}
}
else
{
2020-10-06 01:27:04 +02:00
if ( dest . isValid ( ) & & owner - > getCurrentPlayerInterface ( ) - > cb - > isToReverse ( attackingStack - > getPosition ( ) , dest , owner - > creDir [ attackingStack - > ID ] , false , false ) )
2017-09-08 13:25:12 +02:00
{
2017-07-20 06:08:49 +02:00
owner - > addNewAnim ( new CReverseAnimation ( owner , attackingStack , attackingStack - > getPosition ( ) , true ) ) ;
2017-09-08 13:25:12 +02:00
return false ;
}
}
//TODO: display spell projectile here
static const double straightAngle = 0.2 ;
Point fromPos ;
Point destPos ;
// NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise
fromPos = owner - > creAnims [ attackingStack - > ID ] - > pos . topLeft ( ) ;
2017-07-20 06:08:49 +02:00
//xycoord = CClickableHex::getXYUnitAnim(shooter->getPosition(), true, shooter, owner);
2017-09-08 13:25:12 +02:00
destPos = CClickableHex : : getXYUnitAnim ( dest , attackedStack , owner ) ;
double projectileAngle = atan2 ( fabs ( ( double ) destPos . y - fromPos . y ) , fabs ( ( double ) destPos . x - fromPos . x ) ) ;
2017-07-20 06:08:49 +02:00
if ( attackingStack - > getPosition ( ) < dest )
2017-09-08 13:25:12 +02:00
projectileAngle = - projectileAngle ;
if ( projectileAngle > straightAngle )
group = CCreatureAnim : : VCMI_CAST_UP ;
else if ( projectileAngle < - straightAngle )
group = CCreatureAnim : : VCMI_CAST_DOWN ;
else
group = CCreatureAnim : : VCMI_CAST_FRONT ;
//fall back to H3 cast/2hex
//even if creature have 2hex attack instead of cast it is ok since we fall back to attack anyway
if ( myAnim - > framesInGroup ( group ) = = 0 )
{
if ( projectileAngle > straightAngle )
group = CCreatureAnim : : CAST_UP ;
else if ( projectileAngle < - straightAngle )
group = CCreatureAnim : : CAST_DOWN ;
else
group = CCreatureAnim : : CAST_FRONT ;
}
//fall back to ranged attack
if ( myAnim - > framesInGroup ( group ) = = 0 )
{
if ( projectileAngle > straightAngle )
group = CCreatureAnim : : SHOOT_UP ;
else if ( projectileAngle < - straightAngle )
group = CCreatureAnim : : SHOOT_DOWN ;
else
group = CCreatureAnim : : SHOOT_FRONT ;
}
//fall back to normal attack
if ( myAnim - > framesInGroup ( group ) = = 0 )
{
if ( projectileAngle > straightAngle )
group = CCreatureAnim : : ATTACK_UP ;
else if ( projectileAngle < - straightAngle )
group = CCreatureAnim : : ATTACK_DOWN ;
else
group = CCreatureAnim : : ATTACK_FRONT ;
}
return true ;
}
void CCastAnimation : : nextFrame ( )
{
for ( auto & it : owner - > pendingAnims )
{
CReverseAnimation * anim = dynamic_cast < CReverseAnimation * > ( it . first ) ;
if ( anim & & anim - > stack - > ID = = stack - > ID & & anim - > priority )
return ;
}
if ( myAnim - > getType ( ) ! = group )
{
myAnim - > setType ( group ) ;
myAnim - > onAnimationReset + = std : : bind ( & CAttackAnimation : : endAnim , this ) ;
}
CBattleAnimation : : nextFrame ( ) ;
}
void CCastAnimation : : endAnim ( )
{
CAttackAnimation : : endAnim ( ) ;
delete this ;
}
2017-09-05 19:45:29 +02:00
CEffectAnimation : : CEffectAnimation ( CBattleInterface * _owner , std : : string _customAnim , int _x , int _y , int _dx , int _dy , bool _Vflip , bool _alignToBottom )
: CBattleAnimation ( _owner ) ,
destTile ( BattleHex : : INVALID ) ,
x ( _x ) ,
y ( _y ) ,
dx ( _dx ) ,
dy ( _dy ) ,
Vflip ( _Vflip ) ,
alignToBottom ( _alignToBottom )
2014-09-23 17:30:36 +03:00
{
2017-07-20 06:08:49 +02:00
logAnim - > debug ( " Created effect animation %s " , _customAnim ) ;
customAnim = std : : make_shared < CAnimation > ( _customAnim ) ;
2014-09-23 17:30:36 +03:00
}
2011-12-22 16:05:19 +03:00
2017-07-20 06:08:49 +02:00
CEffectAnimation : : CEffectAnimation ( CBattleInterface * _owner , std : : shared_ptr < CAnimation > _customAnim , int _x , int _y , int _dx , int _dy )
: CBattleAnimation ( _owner ) ,
destTile ( BattleHex : : INVALID ) ,
customAnim ( _customAnim ) ,
x ( _x ) ,
y ( _y ) ,
dx ( _dx ) ,
dy ( _dy ) ,
Vflip ( false ) ,
alignToBottom ( false )
{
logAnim - > debug ( " Created custom effect animation " ) ;
}
2017-09-05 19:45:29 +02:00
CEffectAnimation : : CEffectAnimation ( CBattleInterface * _owner , std : : string _customAnim , BattleHex _destTile , bool _Vflip , bool _alignToBottom )
: CBattleAnimation ( _owner ) ,
destTile ( _destTile ) ,
x ( - 1 ) ,
y ( - 1 ) ,
dx ( 0 ) ,
dy ( 0 ) ,
Vflip ( _Vflip ) ,
alignToBottom ( _alignToBottom )
2014-09-23 17:30:36 +03:00
{
2017-07-20 06:08:49 +02:00
logAnim - > debug ( " Created effect animation %s " , _customAnim ) ;
customAnim = std : : make_shared < CAnimation > ( _customAnim ) ;
2014-11-27 15:51:16 +02:00
}
2017-09-05 19:45:29 +02:00
bool CEffectAnimation : : init ( )
2011-12-22 16:05:19 +03:00
{
if ( ! isEarliest ( true ) )
return false ;
2016-11-27 16:48:18 +02:00
2014-11-27 15:51:16 +02:00
const bool areaEffect = ( ! destTile . isValid ( ) & & x = = - 1 & & y = = - 1 ) ;
2011-12-22 16:05:19 +03:00
2017-07-20 06:08:49 +02:00
std : : shared_ptr < CAnimation > animation = customAnim ;
2017-09-05 16:21:44 +02:00
animation - > preload ( ) ;
if ( Vflip )
animation - > verticalFlip ( ) ;
2018-03-30 13:02:04 +02:00
auto first = animation - > getImage ( 0 , 0 , true ) ;
2017-09-05 16:21:44 +02:00
if ( ! first )
2014-11-27 15:51:16 +02:00
{
2017-09-05 16:21:44 +02:00
endAnim ( ) ;
return false ;
}
2011-12-22 16:05:19 +03:00
2017-09-05 16:21:44 +02:00
if ( areaEffect ) //f.e. armageddon
{
for ( int i = 0 ; i * first - > width ( ) < owner - > pos . w ; + + i )
2014-11-27 15:51:16 +02:00
{
2017-09-05 16:21:44 +02:00
for ( int j = 0 ; j * first - > height ( ) < owner - > pos . h ; + + j )
2011-12-22 16:05:19 +03:00
{
2014-11-27 15:51:16 +02:00
BattleEffect be ;
be . effectID = ID ;
2017-09-05 16:21:44 +02:00
be . animation = animation ;
2014-11-27 15:51:16 +02:00
be . currentFrame = 0 ;
2017-09-05 16:21:44 +02:00
be . x = i * first - > width ( ) + owner - > pos . x ;
be . y = j * first - > height ( ) + owner - > pos . y ;
2014-11-27 15:51:16 +02:00
be . position = BattleHex : : INVALID ;
owner - > battleEffects . push_back ( be ) ;
2011-12-22 16:05:19 +03:00
}
}
}
else // Effects targeted at a specific creature/hex.
{
2017-09-05 16:21:44 +02:00
const CStack * destStack = owner - > getCurrentPlayerInterface ( ) - > cb - > battleGetStackByPos ( destTile , false ) ;
BattleEffect be ;
be . effectID = ID ;
be . animation = animation ;
be . currentFrame = 0 ;
2014-11-27 15:51:16 +02:00
2011-12-22 16:05:19 +03:00
2017-09-05 16:21:44 +02:00
//todo: lightning anim frame count override
2016-11-27 16:48:18 +02:00
2014-11-27 15:51:16 +02:00
// if(effect == 1)
// be.maxFrame = 3;
2011-12-22 16:05:19 +03:00
2018-04-24 07:36:43 +02:00
be . x = x ;
be . y = y ;
if ( destTile . isValid ( ) )
2017-09-05 16:21:44 +02:00
{
2018-04-24 07:36:43 +02:00
Rect & tilePos = owner - > bfield [ destTile ] - > pos ;
if ( x = = - 1 )
be . x = tilePos . x + tilePos . w / 2 - first - > width ( ) / 2 ;
if ( y = = - 1 )
{
if ( alignToBottom )
be . y = tilePos . y + tilePos . h - first - > height ( ) ;
else
be . y = tilePos . y - first - > height ( ) / 2 ;
}
2016-11-27 16:48:18 +02:00
2018-04-24 07:36:43 +02:00
// Correction for 2-hex creatures.
if ( destStack ! = nullptr & & destStack - > doubleWide ( ) )
be . x + = ( destStack - > side = = BattleSide : : ATTACKER ? - 1 : 1 ) * tilePos . w / 2 ;
2017-09-05 16:21:44 +02:00
}
2011-12-22 16:05:19 +03:00
2018-04-24 07:36:43 +02:00
assert ( be . x ! = - 1 & & be . y ! = - 1 ) ;
2013-07-10 13:45:51 +03:00
2017-09-05 16:21:44 +02:00
//Indicate if effect should be drawn on top of everything or just on top of the hex
be . position = destTile ;
2014-11-27 15:51:16 +02:00
2017-09-05 16:21:44 +02:00
owner - > battleEffects . push_back ( be ) ;
2011-12-22 16:05:19 +03:00
}
2016-11-27 16:48:18 +02:00
//battleEffects
2011-12-22 16:05:19 +03:00
return true ;
}
2017-09-05 19:45:29 +02:00
void CEffectAnimation : : nextFrame ( )
2011-12-22 16:05:19 +03:00
{
//notice: there may be more than one effect in owner->battleEffects correcponding to this animation (ie. armageddon)
2013-06-29 16:05:48 +03:00
for ( auto & elem : owner - > battleEffects )
2011-12-22 16:05:19 +03:00
{
2013-06-29 16:05:48 +03:00
if ( elem . effectID = = ID )
2011-12-22 16:05:19 +03:00
{
2013-07-16 21:12:47 +03:00
elem . currentFrame + = AnimationControls : : getSpellEffectSpeed ( ) * GH . mainFPSmng - > getElapsedMilliseconds ( ) / 1000 ;
2011-12-22 16:05:19 +03:00
2017-09-05 16:21:44 +02:00
if ( elem . currentFrame > = elem . animation - > size ( ) )
2011-12-22 16:05:19 +03:00
{
endAnim ( ) ;
break ;
}
else
{
2013-06-29 16:05:48 +03:00
elem . x + = dx ;
elem . y + = dy ;
2011-12-22 16:05:19 +03:00
}
}
}
}
2017-09-05 19:45:29 +02:00
void CEffectAnimation : : endAnim ( )
2011-12-22 16:05:19 +03:00
{
CBattleAnimation : : endAnim ( ) ;
std : : vector < std : : list < BattleEffect > : : iterator > toDel ;
2013-06-29 16:05:48 +03:00
for ( auto it = owner - > battleEffects . begin ( ) ; it ! = owner - > battleEffects . end ( ) ; + + it )
2011-12-22 16:05:19 +03:00
{
if ( it - > effectID = = ID )
{
toDel . push_back ( it ) ;
}
}
2013-06-29 16:05:48 +03:00
for ( auto & elem : toDel )
2011-12-22 16:05:19 +03:00
{
2013-06-29 16:05:48 +03:00
owner - > battleEffects . erase ( elem ) ;
2011-12-22 16:05:19 +03:00
}
delete this ;
2013-04-09 17:31:36 +03:00
}