CBattleAnimations.cpp 31 KB

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