CBattleAnimations.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167
  1. /*
  2. * CBattleAnimations.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "CBattleAnimations.h"
  12. #include <boost/math/constants/constants.hpp>
  13. #include "CBattleInterfaceClasses.h"
  14. #include "CBattleInterface.h"
  15. #include "CBattleProjectileController.h"
  16. #include "CBattleSiegeController.h"
  17. #include "CBattleFieldController.h"
  18. #include "CBattleEffectsController.h"
  19. #include "CBattleStacksController.h"
  20. #include "CCreatureAnimation.h"
  21. #include "../CGameInfo.h"
  22. #include "../CMusicHandler.h"
  23. #include "../CPlayerInterface.h"
  24. #include "../Graphics.h"
  25. #include "../gui/CAnimation.h"
  26. #include "../gui/CCursorHandler.h"
  27. #include "../gui/CGuiHandler.h"
  28. #include "../../CCallback.h"
  29. #include "../../lib/CStack.h"
  30. #include "../../lib/CTownHandler.h"
  31. #include "../../lib/mapObjects/CGTownInstance.h"
  32. CBattleAnimation::CBattleAnimation(CBattleInterface * _owner)
  33. : owner(_owner), ID(_owner->stacksController->animIDhelper++)
  34. {
  35. logAnim->trace("Animation #%d created", ID);
  36. }
  37. CBattleAnimation::~CBattleAnimation()
  38. {
  39. logAnim->trace("Animation #%d deleted", ID);
  40. }
  41. std::list<std::pair<CBattleAnimation *, bool>> & CBattleAnimation::pendingAnimations()
  42. {
  43. return owner->stacksController->pendingAnims;
  44. }
  45. std::shared_ptr<CCreatureAnimation> CBattleAnimation::stackAnimation(const CStack * stack)
  46. {
  47. return owner->stacksController->creAnims[stack->ID];
  48. }
  49. bool CBattleAnimation::stackFacingRight(const CStack * stack)
  50. {
  51. return owner->stacksController->creDir[stack->ID];
  52. }
  53. ui32 CBattleAnimation::maxAnimationID()
  54. {
  55. return owner->stacksController->animIDhelper;
  56. }
  57. void CBattleAnimation::setStackFacingRight(const CStack * stack, bool facingRight)
  58. {
  59. owner->stacksController->creDir[stack->ID] = facingRight;
  60. }
  61. void CBattleAnimation::endAnim()
  62. {
  63. logAnim->trace("Animation #%d ended, type is %s", ID, typeid(this).name());
  64. for(auto & elem : pendingAnimations())
  65. {
  66. if(elem.first == this)
  67. {
  68. elem.first = nullptr;
  69. }
  70. }
  71. }
  72. bool CBattleAnimation::isEarliest(bool perStackConcurrency)
  73. {
  74. int lowestMoveID = maxAnimationID() + 5;//FIXME: why 5?
  75. CBattleStackAnimation * thAnim = dynamic_cast<CBattleStackAnimation *>(this);
  76. CEffectAnimation * thSen = dynamic_cast<CEffectAnimation *>(this);
  77. for(auto & elem : pendingAnimations())
  78. {
  79. CBattleStackAnimation * stAnim = dynamic_cast<CBattleStackAnimation *>(elem.first);
  80. CEffectAnimation * sen = dynamic_cast<CEffectAnimation *>(elem.first);
  81. if(perStackConcurrency && stAnim && thAnim && stAnim->stack->ID != thAnim->stack->ID)
  82. continue;
  83. if(perStackConcurrency && sen && thSen && sen != thSen)
  84. continue;
  85. CReverseAnimation * revAnim = dynamic_cast<CReverseAnimation *>(stAnim);
  86. if(revAnim && thAnim && stAnim && stAnim->stack->ID == thAnim->stack->ID && revAnim->priority)
  87. return false;
  88. if(elem.first)
  89. vstd::amin(lowestMoveID, elem.first->ID);
  90. }
  91. return (ID == lowestMoveID) || (lowestMoveID == (maxAnimationID() + 5));
  92. }
  93. CBattleStackAnimation::CBattleStackAnimation(CBattleInterface * owner, const CStack * stack)
  94. : CBattleAnimation(owner),
  95. myAnim(stackAnimation(stack)),
  96. stack(stack)
  97. {
  98. assert(myAnim);
  99. }
  100. void CBattleStackAnimation::shiftColor(const ColorShifter * shifter)
  101. {
  102. assert(myAnim);
  103. myAnim->shiftColor(shifter);
  104. }
  105. void CAttackAnimation::nextFrame()
  106. {
  107. if(myAnim->getType() != group)
  108. {
  109. myAnim->setType(group);
  110. myAnim->onAnimationReset += std::bind(&CAttackAnimation::endAnim, this);
  111. }
  112. if(!soundPlayed)
  113. {
  114. if(shooting)
  115. CCS->soundh->playSound(battle_sound(attackingStack->getCreature(), shoot));
  116. else
  117. CCS->soundh->playSound(battle_sound(attackingStack->getCreature(), attack));
  118. soundPlayed = true;
  119. }
  120. CBattleAnimation::nextFrame();
  121. }
  122. void CAttackAnimation::endAnim()
  123. {
  124. myAnim->setType(CCreatureAnim::HOLDING);
  125. CBattleStackAnimation::endAnim();
  126. }
  127. bool CAttackAnimation::checkInitialConditions()
  128. {
  129. for(auto & elem : pendingAnimations())
  130. {
  131. CBattleStackAnimation * stAnim = dynamic_cast<CBattleStackAnimation *>(elem.first);
  132. CReverseAnimation * revAnim = dynamic_cast<CReverseAnimation *>(stAnim);
  133. if(revAnim && attackedStack) // enemy must be fully reversed
  134. {
  135. if (revAnim->stack->ID == attackedStack->ID)
  136. return false;
  137. }
  138. }
  139. return isEarliest(false);
  140. }
  141. CAttackAnimation::CAttackAnimation(CBattleInterface *_owner, const CStack *attacker, BattleHex _dest, const CStack *defender)
  142. : CBattleStackAnimation(_owner, attacker),
  143. shooting(false), group(CCreatureAnim::SHOOT_FRONT),
  144. soundPlayed(false),
  145. dest(_dest), attackedStack(defender), attackingStack(attacker)
  146. {
  147. assert(attackingStack && "attackingStack is nullptr in CBattleAttack::CBattleAttack !\n");
  148. attackingStackPosBeforeReturn = attackingStack->getPosition();
  149. }
  150. CDefenceAnimation::CDefenceAnimation(StackAttackedInfo _attackedInfo, CBattleInterface * _owner)
  151. : CBattleStackAnimation(_owner, _attackedInfo.defender),
  152. attacker(_attackedInfo.attacker), rangedAttack(_attackedInfo.indirectAttack),
  153. killed(_attackedInfo.killed), timeToWait(0)
  154. {
  155. logAnim->debug("Created defence anim for %s", _attackedInfo.defender->getName());
  156. }
  157. bool CDefenceAnimation::init()
  158. {
  159. ui32 lowestMoveID = maxAnimationID() + 5;
  160. for(auto & elem : pendingAnimations())
  161. {
  162. CDefenceAnimation * defAnim = dynamic_cast<CDefenceAnimation *>(elem.first);
  163. if(defAnim && defAnim->stack->ID != stack->ID)
  164. continue;
  165. CAttackAnimation * attAnim = dynamic_cast<CAttackAnimation *>(elem.first);
  166. if(attAnim && attAnim->stack->ID != stack->ID)
  167. continue;
  168. CEffectAnimation * sen = dynamic_cast<CEffectAnimation *>(elem.first);
  169. if (sen && attacker == nullptr)
  170. return false;
  171. if (sen)
  172. continue;
  173. CReverseAnimation * animAsRev = dynamic_cast<CReverseAnimation *>(elem.first);
  174. if(animAsRev)
  175. return false;
  176. if(elem.first)
  177. vstd::amin(lowestMoveID, elem.first->ID);
  178. }
  179. if(ID > lowestMoveID)
  180. return false;
  181. //reverse unit if necessary
  182. if(attacker && owner->getCurrentPlayerInterface()->cb->isToReverse(stack->getPosition(), attacker->getPosition(), stackFacingRight(stack), attacker->doubleWide(), stackFacingRight(attacker)))
  183. {
  184. owner->stacksController->addNewAnim(new CReverseAnimation(owner, stack, stack->getPosition(), true));
  185. return false;
  186. }
  187. //unit reversed
  188. if(rangedAttack && attacker != nullptr && owner->projectilesController->hasActiveProjectile(attacker)) //delay hit animation
  189. {
  190. return false;
  191. }
  192. // synchronize animation with attacker, unless defending or attacked by shooter:
  193. // wait for 1/2 of attack animation
  194. if (!rangedAttack && getMyAnimType() != CCreatureAnim::DEFENCE)
  195. {
  196. float frameLength = AnimationControls::getCreatureAnimationSpeed(
  197. stack->getCreature(), stackAnimation(stack).get(), getMyAnimType());
  198. timeToWait = myAnim->framesInGroup(getMyAnimType()) * frameLength / 2;
  199. myAnim->setType(CCreatureAnim::HOLDING);
  200. }
  201. else
  202. {
  203. timeToWait = 0;
  204. startAnimation();
  205. }
  206. return true; //initialized successfuly
  207. }
  208. std::string CDefenceAnimation::getMySound()
  209. {
  210. if(killed)
  211. return battle_sound(stack->getCreature(), killed);
  212. else if(stack->defendingAnim)
  213. return battle_sound(stack->getCreature(), defend);
  214. else
  215. return battle_sound(stack->getCreature(), wince);
  216. }
  217. CCreatureAnim::EAnimType CDefenceAnimation::getMyAnimType()
  218. {
  219. if(killed)
  220. {
  221. if(rangedAttack && myAnim->framesInGroup(CCreatureAnim::DEATH_RANGED) > 0)
  222. return CCreatureAnim::DEATH_RANGED;
  223. else
  224. return CCreatureAnim::DEATH;
  225. }
  226. if(stack->defendingAnim)
  227. return CCreatureAnim::DEFENCE;
  228. else
  229. return CCreatureAnim::HITTED;
  230. }
  231. void CDefenceAnimation::startAnimation()
  232. {
  233. CCS->soundh->playSound(getMySound());
  234. myAnim->setType(getMyAnimType());
  235. myAnim->onAnimationReset += std::bind(&CDefenceAnimation::endAnim, this);
  236. }
  237. void CDefenceAnimation::nextFrame()
  238. {
  239. if (timeToWait > 0)
  240. {
  241. timeToWait -= float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000;
  242. if (timeToWait <= 0)
  243. startAnimation();
  244. }
  245. CBattleAnimation::nextFrame();
  246. }
  247. void CDefenceAnimation::endAnim()
  248. {
  249. if(killed)
  250. {
  251. if(rangedAttack && myAnim->framesInGroup(CCreatureAnim::DEAD_RANGED) > 0)
  252. myAnim->setType(CCreatureAnim::DEAD_RANGED);
  253. else
  254. myAnim->setType(CCreatureAnim::DEAD);
  255. }
  256. else
  257. {
  258. myAnim->setType(CCreatureAnim::HOLDING);
  259. }
  260. CBattleAnimation::endAnim();
  261. delete this;
  262. }
  263. CDummyAnimation::CDummyAnimation(CBattleInterface * _owner, int howManyFrames)
  264. : CBattleAnimation(_owner), counter(0), howMany(howManyFrames)
  265. {
  266. logAnim->debug("Created dummy animation for %d frames", howManyFrames);
  267. }
  268. bool CDummyAnimation::init()
  269. {
  270. return true;
  271. }
  272. void CDummyAnimation::nextFrame()
  273. {
  274. counter++;
  275. if(counter > howMany)
  276. endAnim();
  277. }
  278. void CDummyAnimation::endAnim()
  279. {
  280. CBattleAnimation::endAnim();
  281. delete this;
  282. }
  283. bool CMeleeAttackAnimation::init()
  284. {
  285. if(!CAttackAnimation::checkInitialConditions())
  286. return false;
  287. if(!attackingStack || myAnim->isDead())
  288. {
  289. endAnim();
  290. return false;
  291. }
  292. bool toReverse = owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStackPosBeforeReturn, attackedStack->getPosition(), stackFacingRight(stack), attackedStack->doubleWide(), stackFacingRight(attackedStack));
  293. if(toReverse)
  294. {
  295. owner->stacksController->addNewAnim(new CReverseAnimation(owner, stack, attackingStackPosBeforeReturn, true));
  296. return false;
  297. }
  298. // opponent must face attacker ( = different directions) before he can be attacked
  299. if(attackingStack && attackedStack &&
  300. stackFacingRight(attackingStack) == stackFacingRight(attackedStack))
  301. return false;
  302. //reversed
  303. shooting = false;
  304. static const CCreatureAnim::EAnimType mutPosToGroup[] =
  305. {
  306. CCreatureAnim::ATTACK_UP,
  307. CCreatureAnim::ATTACK_UP,
  308. CCreatureAnim::ATTACK_FRONT,
  309. CCreatureAnim::ATTACK_DOWN,
  310. CCreatureAnim::ATTACK_DOWN,
  311. CCreatureAnim::ATTACK_FRONT
  312. };
  313. static const CCreatureAnim::EAnimType mutPosToGroup2H[] =
  314. {
  315. CCreatureAnim::VCMI_2HEX_UP,
  316. CCreatureAnim::VCMI_2HEX_UP,
  317. CCreatureAnim::VCMI_2HEX_FRONT,
  318. CCreatureAnim::VCMI_2HEX_DOWN,
  319. CCreatureAnim::VCMI_2HEX_DOWN,
  320. CCreatureAnim::VCMI_2HEX_FRONT
  321. };
  322. int revShiftattacker = (attackingStack->side == BattleSide::ATTACKER ? -1 : 1);
  323. int mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn, dest);
  324. if(mutPos == -1 && attackingStack->doubleWide())
  325. {
  326. mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn + revShiftattacker, attackedStack->getPosition());
  327. }
  328. if (mutPos == -1 && attackedStack->doubleWide())
  329. {
  330. mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn, attackedStack->occupiedHex());
  331. }
  332. if (mutPos == -1 && attackedStack->doubleWide() && attackingStack->doubleWide())
  333. {
  334. mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn + revShiftattacker, attackedStack->occupiedHex());
  335. }
  336. switch(mutPos) //attack direction
  337. {
  338. case 0:
  339. case 1:
  340. case 2:
  341. case 3:
  342. case 4:
  343. case 5:
  344. group = mutPosToGroup[mutPos];
  345. if(attackingStack->hasBonusOfType(Bonus::TWO_HEX_ATTACK_BREATH))
  346. {
  347. CCreatureAnim::EAnimType group2H = mutPosToGroup2H[mutPos];
  348. if(myAnim->framesInGroup(group2H)>0)
  349. group = group2H;
  350. }
  351. break;
  352. default:
  353. logGlobal->error("Critical Error! Wrong dest in stackAttacking! dest: %d; attacking stack pos: %d; mutual pos: %d", dest.hex, attackingStackPosBeforeReturn, mutPos);
  354. group = CCreatureAnim::ATTACK_FRONT;
  355. break;
  356. }
  357. return true;
  358. }
  359. CMeleeAttackAnimation::CMeleeAttackAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked)
  360. : CAttackAnimation(_owner, attacker, _dest, _attacked)
  361. {
  362. logAnim->debug("Created melee attack anim for %s", attacker->getName());
  363. }
  364. void CMeleeAttackAnimation::endAnim()
  365. {
  366. CAttackAnimation::endAnim();
  367. delete this;
  368. }
  369. bool CMovementAnimation::init()
  370. {
  371. if( !isEarliest(false) )
  372. return false;
  373. if(!stack || myAnim->isDead())
  374. {
  375. endAnim();
  376. return false;
  377. }
  378. if(stackAnimation(stack)->framesInGroup(CCreatureAnim::MOVING) == 0 ||
  379. stack->hasBonus(Selector::typeSubtype(Bonus::FLYING, 1)))
  380. {
  381. //no movement or teleport, end immediately
  382. endAnim();
  383. return false;
  384. }
  385. //reverse unit if necessary
  386. if(owner->stacksController->shouldRotate(stack, oldPos, nextHex))
  387. {
  388. // it seems that H3 does NOT plays full rotation animation here in most situations
  389. // Logical since it takes quite a lot of time
  390. if (curentMoveIndex == 0) // full rotation only for moving towards first tile.
  391. {
  392. owner->stacksController->addNewAnim(new CReverseAnimation(owner, stack, oldPos, true));
  393. return false;
  394. }
  395. else
  396. {
  397. rotateStack(oldPos);
  398. }
  399. }
  400. if(myAnim->getType() != CCreatureAnim::MOVING)
  401. {
  402. myAnim->setType(CCreatureAnim::MOVING);
  403. }
  404. if (owner->moveSoundHander == -1)
  405. {
  406. owner->moveSoundHander = CCS->soundh->playSound(battle_sound(stack->getCreature(), move), -1);
  407. }
  408. Point begPosition = owner->stacksController->getStackPositionAtHex(oldPos, stack);
  409. Point endPosition = owner->stacksController->getStackPositionAtHex(nextHex, stack);
  410. timeToMove = AnimationControls::getMovementDuration(stack->getCreature());
  411. begX = begPosition.x;
  412. begY = begPosition.y;
  413. progress = 0;
  414. distanceX = endPosition.x - begPosition.x;
  415. distanceY = endPosition.y - begPosition.y;
  416. if (stack->hasBonus(Selector::type()(Bonus::FLYING)))
  417. {
  418. float distance = static_cast<float>(sqrt(distanceX * distanceX + distanceY * distanceY));
  419. timeToMove *= AnimationControls::getFlightDistance(stack->getCreature()) / distance;
  420. }
  421. return true;
  422. }
  423. void CMovementAnimation::nextFrame()
  424. {
  425. progress += float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000 * timeToMove;
  426. //moving instructions
  427. myAnim->pos.x = static_cast<Sint16>(begX + distanceX * progress );
  428. myAnim->pos.y = static_cast<Sint16>(begY + distanceY * progress );
  429. CBattleAnimation::nextFrame();
  430. if(progress >= 1.0)
  431. {
  432. // Sets the position of the creature animation sprites
  433. Point coords = owner->stacksController->getStackPositionAtHex(nextHex, stack);
  434. myAnim->pos = coords;
  435. // true if creature haven't reached the final destination hex
  436. if ((curentMoveIndex + 1) < destTiles.size())
  437. {
  438. // update the next hex field which has to be reached by the stack
  439. curentMoveIndex++;
  440. oldPos = nextHex;
  441. nextHex = destTiles[curentMoveIndex];
  442. // re-init animation
  443. for(auto & elem : pendingAnimations())
  444. {
  445. if (elem.first == this)
  446. {
  447. elem.second = false;
  448. break;
  449. }
  450. }
  451. }
  452. else
  453. endAnim();
  454. }
  455. }
  456. void CMovementAnimation::endAnim()
  457. {
  458. assert(stack);
  459. myAnim->pos = owner->stacksController->getStackPositionAtHex(nextHex, stack);
  460. CBattleAnimation::endAnim();
  461. owner->stacksController->addNewAnim(new CMovementEndAnimation(owner, stack, nextHex));
  462. if(owner->moveSoundHander != -1)
  463. {
  464. CCS->soundh->stopSound(owner->moveSoundHander);
  465. owner->moveSoundHander = -1;
  466. }
  467. delete this;
  468. }
  469. CMovementAnimation::CMovementAnimation(CBattleInterface *_owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance)
  470. : CBattleStackAnimation(_owner, _stack),
  471. destTiles(_destTiles),
  472. curentMoveIndex(0),
  473. oldPos(stack->getPosition()),
  474. begX(0), begY(0),
  475. distanceX(0), distanceY(0),
  476. timeToMove(0.0),
  477. progress(0.0),
  478. nextHex(destTiles.front())
  479. {
  480. logAnim->debug("Created movement anim for %s", stack->getName());
  481. }
  482. CMovementEndAnimation::CMovementEndAnimation(CBattleInterface * _owner, const CStack * _stack, BattleHex destTile)
  483. : CBattleStackAnimation(_owner, _stack), destinationTile(destTile)
  484. {
  485. logAnim->debug("Created movement end anim for %s", stack->getName());
  486. }
  487. bool CMovementEndAnimation::init()
  488. {
  489. if( !isEarliest(true) )
  490. return false;
  491. if(!stack || myAnim->framesInGroup(CCreatureAnim::MOVE_END) == 0 ||
  492. myAnim->isDead())
  493. {
  494. endAnim();
  495. return false;
  496. }
  497. CCS->soundh->playSound(battle_sound(stack->getCreature(), endMoving));
  498. myAnim->setType(CCreatureAnim::MOVE_END);
  499. myAnim->onAnimationReset += std::bind(&CMovementEndAnimation::endAnim, this);
  500. return true;
  501. }
  502. void CMovementEndAnimation::endAnim()
  503. {
  504. CBattleAnimation::endAnim();
  505. if(myAnim->getType() != CCreatureAnim::DEAD)
  506. myAnim->setType(CCreatureAnim::HOLDING); //resetting to default
  507. CCS->curh->show();
  508. delete this;
  509. }
  510. CMovementStartAnimation::CMovementStartAnimation(CBattleInterface * _owner, const CStack * _stack)
  511. : CBattleStackAnimation(_owner, _stack)
  512. {
  513. logAnim->debug("Created movement start anim for %s", stack->getName());
  514. }
  515. bool CMovementStartAnimation::init()
  516. {
  517. if( !isEarliest(false) )
  518. return false;
  519. if(!stack || myAnim->isDead())
  520. {
  521. CMovementStartAnimation::endAnim();
  522. return false;
  523. }
  524. CCS->soundh->playSound(battle_sound(stack->getCreature(), startMoving));
  525. myAnim->setType(CCreatureAnim::MOVE_START);
  526. myAnim->onAnimationReset += std::bind(&CMovementStartAnimation::endAnim, this);
  527. return true;
  528. }
  529. void CMovementStartAnimation::endAnim()
  530. {
  531. CBattleAnimation::endAnim();
  532. delete this;
  533. }
  534. CReverseAnimation::CReverseAnimation(CBattleInterface * _owner, const CStack * stack, BattleHex dest, bool _priority)
  535. : CBattleStackAnimation(_owner, stack), hex(dest), priority(_priority)
  536. {
  537. logAnim->debug("Created reverse anim for %s", stack->getName());
  538. }
  539. bool CReverseAnimation::init()
  540. {
  541. if(myAnim == nullptr || myAnim->isDead())
  542. {
  543. endAnim();
  544. return false; //there is no such creature
  545. }
  546. if(!priority && !isEarliest(false))
  547. return false;
  548. if(myAnim->framesInGroup(CCreatureAnim::TURN_L))
  549. {
  550. myAnim->setType(CCreatureAnim::TURN_L);
  551. myAnim->onAnimationReset += std::bind(&CReverseAnimation::setupSecondPart, this);
  552. }
  553. else
  554. {
  555. setupSecondPart();
  556. }
  557. return true;
  558. }
  559. void CReverseAnimation::endAnim()
  560. {
  561. CBattleAnimation::endAnim();
  562. if( stack->alive() )//don't do that if stack is dead
  563. myAnim->setType(CCreatureAnim::HOLDING);
  564. delete this;
  565. }
  566. void CBattleStackAnimation::rotateStack(BattleHex hex)
  567. {
  568. setStackFacingRight(stack, !stackFacingRight(stack));
  569. stackAnimation(stack)->pos = owner->stacksController->getStackPositionAtHex(hex, stack);
  570. }
  571. void CReverseAnimation::setupSecondPart()
  572. {
  573. if(!stack)
  574. {
  575. endAnim();
  576. return;
  577. }
  578. rotateStack(hex);
  579. if(myAnim->framesInGroup(CCreatureAnim::TURN_R))
  580. {
  581. myAnim->setType(CCreatureAnim::TURN_R);
  582. myAnim->onAnimationReset += std::bind(&CReverseAnimation::endAnim, this);
  583. }
  584. else
  585. endAnim();
  586. }
  587. CRangedAttackAnimation::CRangedAttackAnimation(CBattleInterface * owner_, const CStack * attacker, BattleHex dest_, const CStack * defender)
  588. : CAttackAnimation(owner_, attacker, dest_, defender)
  589. {
  590. }
  591. CShootingAnimation::CShootingAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool _catapult, int _catapultDmg)
  592. : CRangedAttackAnimation(_owner, attacker, _dest, _attacked),
  593. catapultDamage(_catapultDmg),
  594. projectileEmitted(false)
  595. {
  596. logAnim->debug("Created shooting anim for %s", stack->getName());
  597. }
  598. bool CShootingAnimation::init()
  599. {
  600. if( !CAttackAnimation::checkInitialConditions() )
  601. return false;
  602. if(!attackingStack || myAnim->isDead())
  603. {
  604. //FIXME: how is this possible?
  605. logAnim->warn("Shooting animation has not started yet but attacker is dead! Aborting...");
  606. endAnim();
  607. return false;
  608. }
  609. //reverse unit if necessary
  610. if (attackingStack && attackedStack && owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->getPosition(), attackedStack->getPosition(), stackFacingRight(attackingStack), attackingStack->doubleWide(), stackFacingRight(attackedStack)))
  611. {
  612. owner->stacksController->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->getPosition(), true));
  613. return false;
  614. }
  615. //FIXME: this cause freeze
  616. // opponent must face attacker ( = different directions) before he can be attacked
  617. //if (attackingStack && attackedStack && owner->creDir[attackingStack->ID] == owner->creDir[attackedStack->ID])
  618. // return false;
  619. setAnimationGroup();
  620. initializeProjectile();
  621. shooting = true;
  622. return true;
  623. }
  624. void CShootingAnimation::setAnimationGroup()
  625. {
  626. Point shooterPos = stackAnimation(attackingStack)->pos.topLeft();
  627. Point shotTarget = owner->stacksController->getStackPositionAtHex(dest, attackedStack) + Point(225, 225);
  628. //maximal angle in radians between straight horizontal line and shooting line for which shot is considered to be straight (absoulte value)
  629. static const double straightAngle = 0.2;
  630. double projectileAngle = atan2(shotTarget.y - shooterPos.y, std::abs(shotTarget.x - shooterPos.x));
  631. // Calculate projectile start position. Offsets are read out of the CRANIM.TXT.
  632. if (projectileAngle > straightAngle)
  633. group = CCreatureAnim::SHOOT_UP;
  634. else if (projectileAngle < -straightAngle)
  635. group = CCreatureAnim::SHOOT_DOWN;
  636. else
  637. group = CCreatureAnim::SHOOT_FRONT;
  638. }
  639. void CShootingAnimation::initializeProjectile()
  640. {
  641. const CCreature *shooterInfo = attackingStack->getCreature();
  642. if(shooterInfo->idNumber == CreatureID::ARROW_TOWERS)
  643. shooterInfo = owner->siegeController->getTurretCreature();
  644. Point shotTarget = owner->stacksController->getStackPositionAtHex(dest, attackedStack) + Point(225, 225);
  645. Point shotOrigin = stackAnimation(attackingStack)->pos.topLeft() + Point(222, 265);
  646. int multiplier = stackFacingRight(attackingStack) ? 1 : -1;
  647. if (group == CCreatureAnim::SHOOT_UP)
  648. {
  649. shotOrigin.x += ( -25 + shooterInfo->animation.upperRightMissleOffsetX ) * multiplier;
  650. shotOrigin.y += shooterInfo->animation.upperRightMissleOffsetY;
  651. }
  652. else if (group == CCreatureAnim::SHOOT_DOWN)
  653. {
  654. shotOrigin.x += ( -25 + shooterInfo->animation.lowerRightMissleOffsetX ) * multiplier;
  655. shotOrigin.y += shooterInfo->animation.lowerRightMissleOffsetY;
  656. }
  657. else if (group == CCreatureAnim::SHOOT_FRONT)
  658. {
  659. shotOrigin.x += ( -25 + shooterInfo->animation.rightMissleOffsetX ) * multiplier;
  660. shotOrigin.y += shooterInfo->animation.rightMissleOffsetY;
  661. }
  662. else
  663. {
  664. assert(0);
  665. }
  666. owner->projectilesController->createProjectile(attackingStack, attackedStack, shotOrigin, shotTarget);
  667. }
  668. void CShootingAnimation::emitProjectile()
  669. {
  670. owner->projectilesController->emitStackProjectile(attackingStack);
  671. projectileEmitted = true;
  672. }
  673. void CShootingAnimation::nextFrame()
  674. {
  675. for(auto & it : pendingAnimations())
  676. {
  677. CMovementStartAnimation * anim = dynamic_cast<CMovementStartAnimation *>(it.first);
  678. CReverseAnimation * anim2 = dynamic_cast<CReverseAnimation *>(it.first);
  679. if( (anim && anim->stack->ID == stack->ID) || (anim2 && anim2->stack->ID == stack->ID && anim2->priority ) )
  680. return;
  681. }
  682. if (!projectileEmitted)
  683. {
  684. const CCreature *shooterInfo = attackingStack->getCreature();
  685. if(shooterInfo->idNumber == CreatureID::ARROW_TOWERS)
  686. shooterInfo = owner->siegeController->getTurretCreature();
  687. // emit projectile once animation playback reached "climax" frame
  688. if ( stackAnimation(attackingStack)->getCurrentFrame() >= shooterInfo->animation.attackClimaxFrame )
  689. {
  690. emitProjectile();
  691. return;
  692. }
  693. }
  694. // animation should be paused if there is an active projectile
  695. if (projectileEmitted && owner->projectilesController->hasActiveProjectile(attackingStack))
  696. return;
  697. CAttackAnimation::nextFrame();
  698. }
  699. void CShootingAnimation::endAnim()
  700. {
  701. // FIXME: is this possible? Animation is over but we're yet to fire projectile?
  702. if (!projectileEmitted)
  703. {
  704. logAnim->warn("Shooting animation has finished but projectile was not emitted! Emitting it now...");
  705. emitProjectile();
  706. }
  707. // play wall hit/miss sound for catapult attack
  708. if(!attackedStack)
  709. {
  710. if(catapultDamage > 0)
  711. {
  712. CCS->soundh->playSound("WALLHIT");
  713. }
  714. else
  715. {
  716. CCS->soundh->playSound("WALLMISS");
  717. }
  718. }
  719. CAttackAnimation::endAnim();
  720. delete this;
  721. }
  722. CCastAnimation::CCastAnimation(CBattleInterface * owner_, const CStack * attacker, BattleHex dest_, const CStack * defender)
  723. : CRangedAttackAnimation(owner_, attacker, dest_, defender)
  724. {
  725. if(!dest_.isValid() && defender)
  726. dest = defender->getPosition();
  727. }
  728. bool CCastAnimation::init()
  729. {
  730. if(!CAttackAnimation::checkInitialConditions())
  731. return false;
  732. if(!attackingStack || myAnim->isDead())
  733. {
  734. endAnim();
  735. return false;
  736. }
  737. //reverse unit if necessary
  738. if(attackedStack)
  739. {
  740. if(owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->getPosition(), attackedStack->getPosition(), stackFacingRight(attackingStack), attackingStack->doubleWide(), stackFacingRight(attackedStack)))
  741. {
  742. owner->stacksController->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->getPosition(), true));
  743. return false;
  744. }
  745. }
  746. else
  747. {
  748. if(dest.isValid() && owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->getPosition(), dest, stackFacingRight(attackingStack), false, false))
  749. {
  750. owner->stacksController->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->getPosition(), true));
  751. return false;
  752. }
  753. }
  754. //TODO: display spell projectile here
  755. static const double straightAngle = 0.2;
  756. Point fromPos;
  757. Point destPos;
  758. // NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise
  759. fromPos = stackAnimation(attackingStack)->pos.topLeft();
  760. //xycoord = owner->stacksController->getStackPositionAtHex(shooter->getPosition(), shooter);
  761. destPos = owner->stacksController->getStackPositionAtHex(dest, attackedStack);
  762. double projectileAngle = atan2(fabs((double)destPos.y - fromPos.y), fabs((double)destPos.x - fromPos.x));
  763. if(attackingStack->getPosition() < dest)
  764. projectileAngle = -projectileAngle;
  765. if(projectileAngle > straightAngle)
  766. group = CCreatureAnim::VCMI_CAST_UP;
  767. else if(projectileAngle < -straightAngle)
  768. group = CCreatureAnim::VCMI_CAST_DOWN;
  769. else
  770. group = CCreatureAnim::VCMI_CAST_FRONT;
  771. //fall back to H3 cast/2hex
  772. //even if creature have 2hex attack instead of cast it is ok since we fall back to attack anyway
  773. if(myAnim->framesInGroup(group) == 0)
  774. {
  775. if(projectileAngle > straightAngle)
  776. group = CCreatureAnim::CAST_UP;
  777. else if(projectileAngle < -straightAngle)
  778. group = CCreatureAnim::CAST_DOWN;
  779. else
  780. group = CCreatureAnim::CAST_FRONT;
  781. }
  782. //fall back to ranged attack
  783. if(myAnim->framesInGroup(group) == 0)
  784. {
  785. if(projectileAngle > straightAngle)
  786. group = CCreatureAnim::SHOOT_UP;
  787. else if(projectileAngle < -straightAngle)
  788. group = CCreatureAnim::SHOOT_DOWN;
  789. else
  790. group = CCreatureAnim::SHOOT_FRONT;
  791. }
  792. //fall back to normal attack
  793. if(myAnim->framesInGroup(group) == 0)
  794. {
  795. if(projectileAngle > straightAngle)
  796. group = CCreatureAnim::ATTACK_UP;
  797. else if(projectileAngle < -straightAngle)
  798. group = CCreatureAnim::ATTACK_DOWN;
  799. else
  800. group = CCreatureAnim::ATTACK_FRONT;
  801. }
  802. return true;
  803. }
  804. void CCastAnimation::nextFrame()
  805. {
  806. for(auto & it : pendingAnimations())
  807. {
  808. CReverseAnimation * anim = dynamic_cast<CReverseAnimation *>(it.first);
  809. if(anim && anim->stack->ID == stack->ID && anim->priority)
  810. return;
  811. }
  812. if(myAnim->getType() != group)
  813. {
  814. myAnim->setType(group);
  815. myAnim->onAnimationReset += std::bind(&CAttackAnimation::endAnim, this);
  816. }
  817. CBattleAnimation::nextFrame();
  818. }
  819. void CCastAnimation::endAnim()
  820. {
  821. CAttackAnimation::endAnim();
  822. delete this;
  823. }
  824. CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx, int _dy, bool _Vflip, bool _alignToBottom)
  825. : CBattleAnimation(_owner),
  826. destTile(BattleHex::INVALID),
  827. x(_x),
  828. y(_y),
  829. dx(_dx),
  830. dy(_dy),
  831. Vflip(_Vflip),
  832. alignToBottom(_alignToBottom)
  833. {
  834. logAnim->debug("Created effect animation %s", _customAnim);
  835. customAnim = std::make_shared<CAnimation>(_customAnim);
  836. }
  837. CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::shared_ptr<CAnimation> _customAnim, int _x, int _y, int _dx, int _dy)
  838. : CBattleAnimation(_owner),
  839. destTile(BattleHex::INVALID),
  840. customAnim(_customAnim),
  841. x(_x),
  842. y(_y),
  843. dx(_dx),
  844. dy(_dy),
  845. Vflip(false),
  846. alignToBottom(false)
  847. {
  848. logAnim->debug("Created custom effect animation");
  849. }
  850. CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip, bool _alignToBottom)
  851. : CBattleAnimation(_owner),
  852. destTile(_destTile),
  853. x(-1),
  854. y(-1),
  855. dx(0),
  856. dy(0),
  857. Vflip(_Vflip),
  858. alignToBottom(_alignToBottom)
  859. {
  860. logAnim->debug("Created effect animation %s", _customAnim);
  861. customAnim = std::make_shared<CAnimation>(_customAnim);
  862. }
  863. bool CEffectAnimation::init()
  864. {
  865. if(!isEarliest(true))
  866. return false;
  867. const bool areaEffect = (!destTile.isValid() && x == -1 && y == -1);
  868. std::shared_ptr<CAnimation> animation = customAnim;
  869. animation->preload();
  870. if(Vflip)
  871. animation->verticalFlip();
  872. auto first = animation->getImage(0, 0, true);
  873. if(!first)
  874. {
  875. endAnim();
  876. return false;
  877. }
  878. if(areaEffect) //f.e. armageddon
  879. {
  880. for(int i=0; i * first->width() < owner->pos.w ; ++i)
  881. {
  882. for(int j=0; j * first->height() < owner->pos.h ; ++j)
  883. {
  884. BattleEffect be;
  885. be.effectID = ID;
  886. be.animation = animation;
  887. be.currentFrame = 0;
  888. be.x = i * first->width() + owner->pos.x;
  889. be.y = j * first->height() + owner->pos.y;
  890. be.position = BattleHex::INVALID;
  891. owner->effectsController->battleEffects.push_back(be);
  892. }
  893. }
  894. }
  895. else // Effects targeted at a specific creature/hex.
  896. {
  897. const CStack * destStack = owner->getCurrentPlayerInterface()->cb->battleGetStackByPos(destTile, false);
  898. BattleEffect be;
  899. be.effectID = ID;
  900. be.animation = animation;
  901. be.currentFrame = 0;
  902. //todo: lightning anim frame count override
  903. // if(effect == 1)
  904. // be.maxFrame = 3;
  905. be.x = x;
  906. be.y = y;
  907. if(destTile.isValid())
  908. {
  909. Rect tilePos = owner->fieldController->hexPosition(destTile);
  910. if(x == -1)
  911. be.x = tilePos.x + tilePos.w/2 - first->width()/2;
  912. if(y == -1)
  913. {
  914. if(alignToBottom)
  915. be.y = tilePos.y + tilePos.h - first->height();
  916. else
  917. be.y = tilePos.y - first->height()/2;
  918. }
  919. // Correction for 2-hex creatures.
  920. if(destStack != nullptr && destStack->doubleWide())
  921. be.x += (destStack->side == BattleSide::ATTACKER ? -1 : 1)*tilePos.w/2;
  922. }
  923. assert(be.x != -1 && be.y != -1);
  924. //Indicate if effect should be drawn on top of everything or just on top of the hex
  925. be.position = destTile;
  926. owner->effectsController->battleEffects.push_back(be);
  927. }
  928. return true;
  929. }
  930. void CEffectAnimation::nextFrame()
  931. {
  932. //notice: there may be more than one effect in owner->battleEffects correcponding to this animation (ie. armageddon)
  933. for(auto & elem : owner->effectsController->battleEffects)
  934. {
  935. if(elem.effectID == ID)
  936. {
  937. elem.currentFrame += AnimationControls::getSpellEffectSpeed() * GH.mainFPSmng->getElapsedMilliseconds() / 1000;
  938. if(elem.currentFrame >= elem.animation->size())
  939. {
  940. endAnim();
  941. break;
  942. }
  943. else
  944. {
  945. elem.x += dx;
  946. elem.y += dy;
  947. }
  948. }
  949. }
  950. }
  951. void CEffectAnimation::endAnim()
  952. {
  953. CBattleAnimation::endAnim();
  954. boost::range::remove_if(owner->effectsController->battleEffects,
  955. [&](const BattleEffect & elem)
  956. {
  957. return elem.effectID == ID;
  958. });
  959. delete this;
  960. }