CBattleAnimations.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  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 "CBattleStacksController.h"
  19. #include "CCreatureAnimation.h"
  20. #include "../CGameInfo.h"
  21. #include "../CMusicHandler.h"
  22. #include "../CPlayerInterface.h"
  23. #include "../Graphics.h"
  24. #include "../gui/CAnimation.h"
  25. #include "../gui/CCursorHandler.h"
  26. #include "../gui/CGuiHandler.h"
  27. #include "../gui/SDL_Extensions.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. if(attacker == nullptr && owner->battleEffects.size() > 0)
  160. return false;
  161. ui32 lowestMoveID = maxAnimationID() + 5;
  162. for(auto & elem : pendingAnimations())
  163. {
  164. CDefenceAnimation * defAnim = dynamic_cast<CDefenceAnimation *>(elem.first);
  165. if(defAnim && defAnim->stack->ID != stack->ID)
  166. continue;
  167. CAttackAnimation * attAnim = dynamic_cast<CAttackAnimation *>(elem.first);
  168. if(attAnim && attAnim->stack->ID != stack->ID)
  169. continue;
  170. CEffectAnimation * sen = dynamic_cast<CEffectAnimation *>(elem.first);
  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 = CClickableHex::getXYUnitAnim(oldPos, stack, owner);
  409. Point endPosition = CClickableHex::getXYUnitAnim(nextHex, stack, owner);
  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 = CClickableHex::getXYUnitAnim(nextHex, stack, owner);
  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 = CClickableHex::getXYUnitAnim(nextHex, stack, owner);
  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 = CClickableHex::getXYUnitAnim(hex, stack, owner);
  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. {
  595. logAnim->debug("Created shooting anim for %s", stack->getName());
  596. }
  597. bool CShootingAnimation::init()
  598. {
  599. if( !CAttackAnimation::checkInitialConditions() )
  600. return false;
  601. const CStack * shooter = attackingStack;
  602. if(!shooter || myAnim->isDead())
  603. {
  604. endAnim();
  605. return false;
  606. }
  607. //reverse unit if necessary
  608. if (attackingStack && attackedStack && owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->getPosition(), attackedStack->getPosition(), stackFacingRight(attackingStack), attackingStack->doubleWide(), stackFacingRight(attackedStack)))
  609. {
  610. owner->stacksController->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->getPosition(), true));
  611. return false;
  612. }
  613. //FIXME: this cause freeze
  614. // opponent must face attacker ( = different directions) before he can be attacked
  615. //if (attackingStack && attackedStack && owner->creDir[attackingStack->ID] == owner->creDir[attackedStack->ID])
  616. // return false;
  617. //maximal angle in radians between straight horizontal line and shooting line for which shot is considered to be straight (absoulte value)
  618. static const double straightAngle = 0.2;
  619. // Get further info about the shooter e.g. relative pos of projectile to unit.
  620. // If the creature id is 149 then it's a arrow tower which has no additional info so get the
  621. // actual arrow tower shooter instead.
  622. const CCreature *shooterInfo = shooter->getCreature();
  623. if(shooterInfo->idNumber == CreatureID::ARROW_TOWERS)
  624. shooterInfo = owner->siegeController->turretCreature();
  625. Point shooterPos;
  626. Point shotPos;
  627. Point destPos;
  628. // NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise
  629. shooterPos = stackAnimation(shooter)->pos.topLeft();
  630. //xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner);
  631. destPos = CClickableHex::getXYUnitAnim(dest, attackedStack, owner) + Point(225, 225);
  632. // to properly translate coordinates when shooter is rotated
  633. int multiplier = 0;
  634. if (shooter)
  635. multiplier = stackFacingRight(shooter) ? 1 : -1;
  636. else
  637. {
  638. assert(false); // unreachable?
  639. multiplier = shooter->getCreature()->idNumber == CreatureID::ARROW_TOWERS ? -1 : 1;
  640. }
  641. double projectileAngle = atan2(fabs((double)destPos.y - shooterPos.y), fabs((double)destPos.x - shooterPos.x));
  642. if(shooter->getPosition() < dest)
  643. projectileAngle = -projectileAngle;
  644. // Calculate projectile start position. Offsets are read out of the CRANIM.TXT.
  645. if (projectileAngle > straightAngle)
  646. {
  647. //upper shot
  648. shotPos.x = shooterPos.x + 222 + ( -25 + shooterInfo->animation.upperRightMissleOffsetX ) * multiplier;
  649. shotPos.y = shooterPos.y + 265 + shooterInfo->animation.upperRightMissleOffsetY;
  650. }
  651. else if (projectileAngle < -straightAngle)
  652. {
  653. //lower shot
  654. shotPos.x = shooterPos.x + 222 + ( -25 + shooterInfo->animation.lowerRightMissleOffsetX ) * multiplier;
  655. shotPos.y = shooterPos.y + 265 + shooterInfo->animation.lowerRightMissleOffsetY;
  656. }
  657. else
  658. {
  659. //straight shot
  660. shotPos.x = shooterPos.x + 222 + ( -25 + shooterInfo->animation.rightMissleOffsetX ) * multiplier;
  661. shotPos.y = shooterPos.y + 265 + shooterInfo->animation.rightMissleOffsetY;
  662. }
  663. owner->projectilesController->createProjectile(attackingStack, attackedStack, shotPos, destPos);
  664. //attack animation
  665. shooting = true;
  666. if(projectileAngle > straightAngle)
  667. group = CCreatureAnim::SHOOT_UP;
  668. else if(projectileAngle < -straightAngle)
  669. group = CCreatureAnim::SHOOT_DOWN;
  670. else //straight shot
  671. group = CCreatureAnim::SHOOT_FRONT;
  672. return true;
  673. }
  674. void CShootingAnimation::nextFrame()
  675. {
  676. if (owner->projectilesController->hasActiveProjectile(attackingStack))
  677. {
  678. const CCreature *shooterInfo = attackingStack->getCreature();
  679. if(shooterInfo->idNumber == CreatureID::ARROW_TOWERS)
  680. shooterInfo = owner->siegeController->turretCreature();
  681. // animation should be paused if there is an active projectile
  682. if ( stackAnimation(attackingStack)->getCurrentFrame() >= shooterInfo->animation.attackClimaxFrame )
  683. {
  684. owner->projectilesController->fireStackProjectile(attackingStack);//FIXME: should only be called once
  685. return;
  686. }
  687. }
  688. for(auto & it : pendingAnimations())
  689. {
  690. CMovementStartAnimation * anim = dynamic_cast<CMovementStartAnimation *>(it.first);
  691. CReverseAnimation * anim2 = dynamic_cast<CReverseAnimation *>(it.first);
  692. if( (anim && anim->stack->ID == stack->ID) || (anim2 && anim2->stack->ID == stack->ID && anim2->priority ) )
  693. return;
  694. }
  695. CAttackAnimation::nextFrame();
  696. }
  697. void CShootingAnimation::endAnim()
  698. {
  699. // FIXME: is this possible? Animation is over but we're yet to fire projectile?
  700. owner->projectilesController->fireStackProjectile(attackingStack);
  701. // play wall hit/miss sound for catapult attack
  702. if(!attackedStack)
  703. {
  704. if(catapultDamage > 0)
  705. {
  706. CCS->soundh->playSound("WALLHIT");
  707. }
  708. else
  709. {
  710. CCS->soundh->playSound("WALLMISS");
  711. }
  712. }
  713. CAttackAnimation::endAnim();
  714. delete this;
  715. }
  716. CCastAnimation::CCastAnimation(CBattleInterface * owner_, const CStack * attacker, BattleHex dest_, const CStack * defender)
  717. : CRangedAttackAnimation(owner_, attacker, dest_, defender)
  718. {
  719. if(!dest_.isValid() && defender)
  720. dest = defender->getPosition();
  721. }
  722. bool CCastAnimation::init()
  723. {
  724. if(!CAttackAnimation::checkInitialConditions())
  725. return false;
  726. if(!attackingStack || myAnim->isDead())
  727. {
  728. endAnim();
  729. return false;
  730. }
  731. //reverse unit if necessary
  732. if(attackedStack)
  733. {
  734. if(owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->getPosition(), attackedStack->getPosition(), stackFacingRight(attackingStack), attackingStack->doubleWide(), stackFacingRight(attackedStack)))
  735. {
  736. owner->stacksController->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->getPosition(), true));
  737. return false;
  738. }
  739. }
  740. else
  741. {
  742. if(dest.isValid() && owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->getPosition(), dest, stackFacingRight(attackingStack), false, false))
  743. {
  744. owner->stacksController->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->getPosition(), true));
  745. return false;
  746. }
  747. }
  748. //TODO: display spell projectile here
  749. static const double straightAngle = 0.2;
  750. Point fromPos;
  751. Point destPos;
  752. // NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise
  753. fromPos = stackAnimation(attackingStack)->pos.topLeft();
  754. //xycoord = CClickableHex::getXYUnitAnim(shooter->getPosition(), true, shooter, owner);
  755. destPos = CClickableHex::getXYUnitAnim(dest, attackedStack, owner);
  756. double projectileAngle = atan2(fabs((double)destPos.y - fromPos.y), fabs((double)destPos.x - fromPos.x));
  757. if(attackingStack->getPosition() < dest)
  758. projectileAngle = -projectileAngle;
  759. if(projectileAngle > straightAngle)
  760. group = CCreatureAnim::VCMI_CAST_UP;
  761. else if(projectileAngle < -straightAngle)
  762. group = CCreatureAnim::VCMI_CAST_DOWN;
  763. else
  764. group = CCreatureAnim::VCMI_CAST_FRONT;
  765. //fall back to H3 cast/2hex
  766. //even if creature have 2hex attack instead of cast it is ok since we fall back to attack anyway
  767. if(myAnim->framesInGroup(group) == 0)
  768. {
  769. if(projectileAngle > straightAngle)
  770. group = CCreatureAnim::CAST_UP;
  771. else if(projectileAngle < -straightAngle)
  772. group = CCreatureAnim::CAST_DOWN;
  773. else
  774. group = CCreatureAnim::CAST_FRONT;
  775. }
  776. //fall back to ranged attack
  777. if(myAnim->framesInGroup(group) == 0)
  778. {
  779. if(projectileAngle > straightAngle)
  780. group = CCreatureAnim::SHOOT_UP;
  781. else if(projectileAngle < -straightAngle)
  782. group = CCreatureAnim::SHOOT_DOWN;
  783. else
  784. group = CCreatureAnim::SHOOT_FRONT;
  785. }
  786. //fall back to normal attack
  787. if(myAnim->framesInGroup(group) == 0)
  788. {
  789. if(projectileAngle > straightAngle)
  790. group = CCreatureAnim::ATTACK_UP;
  791. else if(projectileAngle < -straightAngle)
  792. group = CCreatureAnim::ATTACK_DOWN;
  793. else
  794. group = CCreatureAnim::ATTACK_FRONT;
  795. }
  796. return true;
  797. }
  798. void CCastAnimation::nextFrame()
  799. {
  800. for(auto & it : pendingAnimations())
  801. {
  802. CReverseAnimation * anim = dynamic_cast<CReverseAnimation *>(it.first);
  803. if(anim && anim->stack->ID == stack->ID && anim->priority)
  804. return;
  805. }
  806. if(myAnim->getType() != group)
  807. {
  808. myAnim->setType(group);
  809. myAnim->onAnimationReset += std::bind(&CAttackAnimation::endAnim, this);
  810. }
  811. CBattleAnimation::nextFrame();
  812. }
  813. void CCastAnimation::endAnim()
  814. {
  815. CAttackAnimation::endAnim();
  816. delete this;
  817. }
  818. CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx, int _dy, bool _Vflip, bool _alignToBottom)
  819. : CBattleAnimation(_owner),
  820. destTile(BattleHex::INVALID),
  821. x(_x),
  822. y(_y),
  823. dx(_dx),
  824. dy(_dy),
  825. Vflip(_Vflip),
  826. alignToBottom(_alignToBottom)
  827. {
  828. logAnim->debug("Created effect animation %s", _customAnim);
  829. customAnim = std::make_shared<CAnimation>(_customAnim);
  830. }
  831. CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::shared_ptr<CAnimation> _customAnim, int _x, int _y, int _dx, int _dy)
  832. : CBattleAnimation(_owner),
  833. destTile(BattleHex::INVALID),
  834. customAnim(_customAnim),
  835. x(_x),
  836. y(_y),
  837. dx(_dx),
  838. dy(_dy),
  839. Vflip(false),
  840. alignToBottom(false)
  841. {
  842. logAnim->debug("Created custom effect animation");
  843. }
  844. CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip, bool _alignToBottom)
  845. : CBattleAnimation(_owner),
  846. destTile(_destTile),
  847. x(-1),
  848. y(-1),
  849. dx(0),
  850. dy(0),
  851. Vflip(_Vflip),
  852. alignToBottom(_alignToBottom)
  853. {
  854. logAnim->debug("Created effect animation %s", _customAnim);
  855. customAnim = std::make_shared<CAnimation>(_customAnim);
  856. }
  857. bool CEffectAnimation::init()
  858. {
  859. if(!isEarliest(true))
  860. return false;
  861. const bool areaEffect = (!destTile.isValid() && x == -1 && y == -1);
  862. std::shared_ptr<CAnimation> animation = customAnim;
  863. animation->preload();
  864. if(Vflip)
  865. animation->verticalFlip();
  866. auto first = animation->getImage(0, 0, true);
  867. if(!first)
  868. {
  869. endAnim();
  870. return false;
  871. }
  872. if(areaEffect) //f.e. armageddon
  873. {
  874. for(int i=0; i * first->width() < owner->pos.w ; ++i)
  875. {
  876. for(int j=0; j * first->height() < owner->pos.h ; ++j)
  877. {
  878. BattleEffect be;
  879. be.effectID = ID;
  880. be.animation = animation;
  881. be.currentFrame = 0;
  882. be.x = i * first->width() + owner->pos.x;
  883. be.y = j * first->height() + owner->pos.y;
  884. be.position = BattleHex::INVALID;
  885. owner->battleEffects.push_back(be);
  886. }
  887. }
  888. }
  889. else // Effects targeted at a specific creature/hex.
  890. {
  891. const CStack * destStack = owner->getCurrentPlayerInterface()->cb->battleGetStackByPos(destTile, false);
  892. BattleEffect be;
  893. be.effectID = ID;
  894. be.animation = animation;
  895. be.currentFrame = 0;
  896. //todo: lightning anim frame count override
  897. // if(effect == 1)
  898. // be.maxFrame = 3;
  899. be.x = x;
  900. be.y = y;
  901. if(destTile.isValid())
  902. {
  903. Rect tilePos = owner->fieldController->hexPosition(destTile);
  904. if(x == -1)
  905. be.x = tilePos.x + tilePos.w/2 - first->width()/2;
  906. if(y == -1)
  907. {
  908. if(alignToBottom)
  909. be.y = tilePos.y + tilePos.h - first->height();
  910. else
  911. be.y = tilePos.y - first->height()/2;
  912. }
  913. // Correction for 2-hex creatures.
  914. if(destStack != nullptr && destStack->doubleWide())
  915. be.x += (destStack->side == BattleSide::ATTACKER ? -1 : 1)*tilePos.w/2;
  916. }
  917. assert(be.x != -1 && be.y != -1);
  918. //Indicate if effect should be drawn on top of everything or just on top of the hex
  919. be.position = destTile;
  920. owner->battleEffects.push_back(be);
  921. }
  922. //battleEffects
  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->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. std::vector<std::list<BattleEffect>::iterator> toDel;
  950. for(auto it = owner->battleEffects.begin(); it != owner->battleEffects.end(); ++it)
  951. {
  952. if(it->effectID == ID)
  953. {
  954. toDel.push_back(it);
  955. }
  956. }
  957. for(auto & elem : toDel)
  958. {
  959. owner->battleEffects.erase(elem);
  960. }
  961. delete this;
  962. }