CBattleAnimations.cpp 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  1. #include "StdInc.h"
  2. #include "CBattleAnimations.h"
  3. #include <boost/math/constants/constants.hpp>
  4. #include "../CMusicHandler.h"
  5. #include "../CGameInfo.h"
  6. #include "CBattleInterface.h"
  7. #include "CBattleInterfaceClasses.h"
  8. #include "CCreatureAnimation.h"
  9. #include "../../lib/BattleState.h"
  10. #include "../CPlayerInterface.h"
  11. #include "../../CCallback.h"
  12. #include "../gui/SDL_Extensions.h"
  13. #include "../Graphics.h"
  14. #include "../gui/CCursorHandler.h"
  15. #include "../../lib/CTownHandler.h"
  16. CBattleAnimation::CBattleAnimation(CBattleInterface * _owner)
  17. : owner(_owner), ID(_owner->animIDhelper++)
  18. {}
  19. void CBattleAnimation::endAnim()
  20. {
  21. for(std::list<std::pair<CBattleAnimation *, bool> >::iterator it = owner->pendingAnims.begin(); it != owner->pendingAnims.end(); ++it)
  22. {
  23. if(it->first == this)
  24. {
  25. it->first = nullptr;
  26. }
  27. }
  28. }
  29. bool CBattleAnimation::isEarliest(bool perStackConcurrency)
  30. {
  31. int lowestMoveID = owner->animIDhelper + 5;
  32. CBattleStackAnimation * thAnim = dynamic_cast<CBattleStackAnimation *>(this);
  33. CSpellEffectAnimation * thSen = dynamic_cast<CSpellEffectAnimation *>(this);
  34. for(std::list<std::pair<CBattleAnimation *, bool> >::iterator it = owner->pendingAnims.begin(); it != owner->pendingAnims.end(); ++it)
  35. {
  36. CBattleStackAnimation * stAnim = dynamic_cast<CBattleStackAnimation *>(it->first);
  37. CSpellEffectAnimation * sen = dynamic_cast<CSpellEffectAnimation *>(it->first);
  38. if(perStackConcurrency && stAnim && thAnim && stAnim->stack->ID != thAnim->stack->ID)
  39. continue;
  40. if(sen && thSen && sen != thSen && perStackConcurrency)
  41. continue;
  42. CReverseAnimation * revAnim = dynamic_cast<CReverseAnimation *>(stAnim);
  43. if(revAnim && thAnim && stAnim && stAnim->stack->ID == thAnim->stack->ID && revAnim->priority)
  44. return false;
  45. if(it->first)
  46. vstd::amin(lowestMoveID, it->first->ID);
  47. }
  48. return (ID == lowestMoveID) || (lowestMoveID == (owner->animIDhelper + 5));
  49. }
  50. CBattleStackAnimation::CBattleStackAnimation(CBattleInterface * _owner, const CStack * _stack)
  51. : CBattleAnimation(_owner), stack(_stack)
  52. {}
  53. CCreatureAnimation* CBattleStackAnimation::myAnim()
  54. {
  55. return owner->creAnims[stack->ID];
  56. }
  57. void CAttackAnimation::nextFrame()
  58. {
  59. if(myAnim()->getType() != group)
  60. myAnim()->setType(group);
  61. if(myAnim()->onFirstFrameInGroup())
  62. {
  63. if(shooting)
  64. CCS->soundh->playSound(battle_sound(attackingStack->getCreature(), shoot));
  65. else
  66. CCS->soundh->playSound(battle_sound(attackingStack->getCreature(), attack));
  67. }
  68. else if(myAnim()->onLastFrameInGroup())
  69. {
  70. myAnim()->setType(CCreatureAnim::HOLDING);
  71. endAnim();
  72. return; //execution of endAnim deletes this !!!
  73. }
  74. }
  75. bool CAttackAnimation::checkInitialConditions()
  76. {
  77. return isEarliest(false);
  78. }
  79. CAttackAnimation::CAttackAnimation(CBattleInterface *_owner, const CStack *attacker, BattleHex _dest, const CStack *defender)
  80. : CBattleStackAnimation(_owner, attacker), dest(_dest), attackedStack(defender), attackingStack(attacker)
  81. {
  82. assert(attackingStack && "attackingStack is nullptr in CBattleAttack::CBattleAttack !\n");
  83. bool isCatapultAttack = attackingStack->hasBonusOfType(Bonus::CATAPULT)
  84. && owner->curInt->cb->battleHexToWallPart(_dest) >= 0;
  85. assert(attackedStack || isCatapultAttack);
  86. attackingStackPosBeforeReturn = attackingStack->position;
  87. }
  88. CDefenceAnimation::CDefenceAnimation(StackAttackedInfo _attackedInfo, CBattleInterface * _owner)
  89. : CBattleStackAnimation(_owner, _attackedInfo.defender),
  90. attacker(_attackedInfo.attacker), byShooting(_attackedInfo.byShooting),
  91. killed(_attackedInfo.killed)
  92. {}
  93. bool CDefenceAnimation::init()
  94. {
  95. //checking initial conditions
  96. //if(owner->creAnims[stackID]->getType() != 2)
  97. //{
  98. // return false;
  99. //}
  100. if(attacker == nullptr && owner->battleEffects.size() > 0)
  101. return false;
  102. ui32 lowestMoveID = owner->animIDhelper + 5;
  103. for(std::list<std::pair<CBattleAnimation *, bool> >::iterator it = owner->pendingAnims.begin(); it != owner->pendingAnims.end(); ++it)
  104. {
  105. CDefenceAnimation * defAnim = dynamic_cast<CDefenceAnimation *>(it->first);
  106. if(defAnim && defAnim->stack->ID != stack->ID)
  107. continue;
  108. CAttackAnimation * attAnim = dynamic_cast<CAttackAnimation *>(it->first);
  109. if(attAnim && attAnim->stack->ID != stack->ID)
  110. continue;
  111. if(attacker != nullptr)
  112. {
  113. int attackerAnimType = owner->creAnims[attacker->ID]->getType();
  114. if( ( attackerAnimType == CCreatureAnim::ATTACK_UP ||
  115. attackerAnimType == CCreatureAnim::ATTACK_FRONT ||
  116. attackerAnimType == CCreatureAnim::ATTACK_DOWN ) )
  117. return false;
  118. }
  119. CReverseAnimation * animAsRev = dynamic_cast<CReverseAnimation *>(it->first);
  120. if(animAsRev && animAsRev->priority)
  121. return false;
  122. if(it->first)
  123. vstd::amin(lowestMoveID, it->first->ID);
  124. }
  125. if(ID > lowestMoveID)
  126. return false;
  127. //reverse unit if necessary
  128. if (attacker && owner->curInt->cb->isToReverse(stack->position, attacker->position, owner->creDir[stack->ID], attacker->doubleWide(), owner->creDir[attacker->ID]))
  129. {
  130. owner->addNewAnim(new CReverseAnimation(owner, stack, stack->position, true));
  131. return false;
  132. }
  133. //unit reversed
  134. if(byShooting) //delay hit animation
  135. {
  136. for(std::list<ProjectileInfo>::const_iterator it = owner->projectiles.begin(); it != owner->projectiles.end(); ++it)
  137. {
  138. if(it->creID == attacker->getCreature()->idNumber)
  139. {
  140. return false;
  141. }
  142. }
  143. }
  144. //initializing
  145. if(killed)
  146. {
  147. CCS->soundh->playSound(battle_sound(stack->getCreature(), killed));
  148. myAnim()->setType(CCreatureAnim::DEATH); //death
  149. }
  150. else
  151. {
  152. // TODO: this block doesn't seems correct if the unit is defending.
  153. CCS->soundh->playSound(battle_sound(stack->getCreature(), wince));
  154. myAnim()->setType(CCreatureAnim::HITTED); //getting hit
  155. }
  156. return true; //initialized successfuly
  157. }
  158. void CDefenceAnimation::nextFrame()
  159. {
  160. if(!killed && myAnim()->getType() != CCreatureAnim::HITTED)
  161. {
  162. myAnim()->setType(CCreatureAnim::HITTED);
  163. }
  164. if(!myAnim()->onLastFrameInGroup())
  165. {
  166. if( myAnim()->getType() == CCreatureAnim::DEATH && (owner->animCount+1)%(4/owner->getAnimSpeed())==0
  167. && !myAnim()->onLastFrameInGroup() )
  168. {
  169. myAnim()->incrementFrame();
  170. }
  171. }
  172. else
  173. {
  174. endAnim();
  175. }
  176. }
  177. void CDefenceAnimation::endAnim()
  178. {
  179. //restoring animType
  180. if(myAnim()->getType() == CCreatureAnim::HITTED)
  181. myAnim()->setType(CCreatureAnim::HOLDING);
  182. //printing info to console
  183. //if(attacker!=nullptr)
  184. // owner->printConsoleAttacked(stack, dmg, amountKilled, attacker);
  185. //const CStack * attacker = owner->curInt->cb->battleGetStackByID(IDby, false);
  186. //const CStack * attacked = owner->curInt->cb->battleGetStackByID(stackID, false);
  187. CBattleAnimation::endAnim();
  188. delete this;
  189. }
  190. CDummyAnimation::CDummyAnimation(CBattleInterface * _owner, int howManyFrames)
  191. : CBattleAnimation(_owner), counter(0), howMany(howManyFrames)
  192. {}
  193. bool CDummyAnimation::init()
  194. {
  195. return true;
  196. }
  197. void CDummyAnimation::nextFrame()
  198. {
  199. counter++;
  200. if(counter > howMany)
  201. endAnim();
  202. }
  203. void CDummyAnimation::endAnim()
  204. {
  205. CBattleAnimation::endAnim();
  206. delete this;
  207. }
  208. bool CMeleeAttackAnimation::init()
  209. {
  210. if( !CAttackAnimation::checkInitialConditions() )
  211. return false;
  212. //if(owner->creAnims[stackID]->getType()!=2)
  213. //{
  214. // return false;
  215. //}
  216. if(!attackingStack || myAnim()->getType() == 5)
  217. {
  218. endAnim();
  219. return false;
  220. }
  221. bool toReverse = owner->curInt->cb->isToReverse(attackingStackPosBeforeReturn, dest, owner->creDir[stack->ID], attackedStack->doubleWide(), owner->creDir[attackedStack->ID]);
  222. if (toReverse)
  223. {
  224. owner->addNewAnim(new CReverseAnimation(owner, stack, attackingStackPosBeforeReturn, true));
  225. return false;
  226. }
  227. //reversed
  228. shooting = false;
  229. static const CCreatureAnim::EAnimType mutPosToGroup[] = {CCreatureAnim::ATTACK_UP, CCreatureAnim::ATTACK_UP,
  230. CCreatureAnim::ATTACK_FRONT, CCreatureAnim::ATTACK_DOWN, CCreatureAnim::ATTACK_DOWN, CCreatureAnim::ATTACK_FRONT};
  231. int revShiftattacker = (attackingStack->attackerOwned ? -1 : 1);
  232. int mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn, dest);
  233. if(mutPos == -1 && attackingStack->doubleWide())
  234. {
  235. mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn + revShiftattacker, attackedStack->position);
  236. }
  237. if (mutPos == -1 && attackedStack->doubleWide())
  238. {
  239. mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn, attackedStack->occupiedHex());
  240. }
  241. if (mutPos == -1 && attackedStack->doubleWide() && attackingStack->doubleWide())
  242. {
  243. mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn + revShiftattacker, attackedStack->occupiedHex());
  244. }
  245. switch(mutPos) //attack direction
  246. {
  247. case 0: case 1: case 2: case 3: case 4: case 5:
  248. group = mutPosToGroup[mutPos];
  249. break;
  250. default:
  251. logGlobal->errorStream()<<"Critical Error! Wrong dest in stackAttacking! dest: "<<dest<<" attacking stack pos: "<<attackingStackPosBeforeReturn<<" mutual pos: "<<mutPos;
  252. group = CCreatureAnim::ATTACK_FRONT;
  253. break;
  254. }
  255. return true;
  256. }
  257. CMeleeAttackAnimation::CMeleeAttackAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked)
  258. : CAttackAnimation(_owner, attacker, _dest, _attacked)
  259. {}
  260. void CMeleeAttackAnimation::nextFrame()
  261. {
  262. /*for(std::list<std::pair<CBattleAnimation *, bool> >::const_iterator it = owner->pendingAnims.begin(); it != owner->pendingAnims.end(); ++it)
  263. {
  264. CBattleMoveStart * anim = dynamic_cast<CBattleMoveStart *>(it->first);
  265. CReverseAnim * anim2 = dynamic_cast<CReverseAnim *>(it->first);
  266. if( (anim && anim->stackID == stackID) || (anim2 && anim2->stackID == stackID ) )
  267. return;
  268. }*/
  269. CAttackAnimation::nextFrame();
  270. }
  271. void CMeleeAttackAnimation::endAnim()
  272. {
  273. CBattleAnimation::endAnim();
  274. delete this;
  275. }
  276. bool CMovementAnimation::init()
  277. {
  278. if( !isEarliest(false) )
  279. return false;
  280. //a few useful variables
  281. steps = static_cast<int>(myAnim()->framesInGroup(CCreatureAnim::MOVING) * owner->getAnimSpeedMultiplier() - 1);
  282. const CStack * movedStack = stack;
  283. if(!movedStack || myAnim()->getType() == 5)
  284. {
  285. endAnim();
  286. return false;
  287. }
  288. Point begPosition = CClickableHex::getXYUnitAnim(curStackPos, movedStack->attackerOwned, movedStack, owner);
  289. Point endPosition = CClickableHex::getXYUnitAnim(nextHex, movedStack->attackerOwned, movedStack, owner);
  290. if(steps < 0 || stack->hasBonus(Selector::typeSubtype(Bonus::FLYING, 1))) //no movement or teleport
  291. {
  292. //this creature seems to have no move animation so we can end it immediately
  293. endAnim();
  294. return false;
  295. }
  296. whichStep = 0;
  297. int hexWbase = 44, hexHbase = 42;
  298. //bool twoTiles = movedStack->doubleWide();
  299. int mutPos = BattleHex::mutualPosition(curStackPos, nextHex);
  300. //reverse unit if necessary
  301. if((begPosition.x > endPosition.x) && owner->creDir[stack->ID] == true)
  302. {
  303. owner->addNewAnim(new CReverseAnimation(owner, stack, curStackPos, true));
  304. return false;
  305. }
  306. else if ((begPosition.x < endPosition.x) && owner->creDir[stack->ID] == false)
  307. {
  308. owner->addNewAnim(new CReverseAnimation(owner, stack, curStackPos, true));
  309. return false;
  310. }
  311. if(myAnim()->getType() != CCreatureAnim::MOVING)
  312. {
  313. myAnim()->setType(CCreatureAnim::MOVING);
  314. }
  315. //unit reversed
  316. if (owner->moveSh == -1)
  317. {
  318. owner->moveSh = CCS->soundh->playSound(battle_sound(movedStack->getCreature(), move), -1);
  319. }
  320. //step shift calculation
  321. posX = myAnim()->pos.x, posY = myAnim()->pos.y; // for precise calculations ;]
  322. if(mutPos == -1 && movedStack->hasBonusOfType(Bonus::FLYING))
  323. {
  324. steps *= distance;
  325. steps /= 2; //to make animation faster
  326. stepX = (endPosition.x - begPosition.x) / static_cast<double>(steps);
  327. stepY = (endPosition.y - begPosition.y) / static_cast<double>(steps);
  328. }
  329. else
  330. {
  331. switch(mutPos)
  332. {
  333. case 0:
  334. stepX = -1.0 * (hexWbase / (2.0 * steps));
  335. stepY = -1.0 * (hexHbase / (static_cast<double>(steps)));
  336. break;
  337. case 1:
  338. stepX = hexWbase / (2.0 * steps);
  339. stepY = -1.0 * hexHbase / (static_cast<double>(steps));
  340. break;
  341. case 2:
  342. stepX = hexWbase / static_cast<double>(steps);
  343. stepY = 0.0;
  344. break;
  345. case 3:
  346. stepX = hexWbase / (2.0 * steps);
  347. stepY = hexHbase / static_cast<double>(steps);
  348. break;
  349. case 4:
  350. stepX = -1.0 * hexWbase / (2.0 * steps);
  351. stepY = hexHbase / static_cast<double>(steps);
  352. break;
  353. case 5:
  354. stepX = -1.0 * hexWbase / static_cast<double>(steps);
  355. stepY = 0.0;
  356. break;
  357. }
  358. }
  359. //step shifts calculated
  360. return true;
  361. }
  362. void CMovementAnimation::nextFrame()
  363. {
  364. //moving instructions
  365. posX += stepX;
  366. myAnim()->pos.x = static_cast<Sint16>(posX);
  367. posY += stepY;
  368. myAnim()->pos.y = static_cast<Sint16>(posY);
  369. // Increments step count and check if we are finished with current animation
  370. ++whichStep;
  371. if(whichStep == steps)
  372. {
  373. // Sets the position of the creature animation sprites
  374. Point coords = CClickableHex::getXYUnitAnim(nextHex, owner->creDir[stack->ID], stack, owner);
  375. myAnim()->pos = coords;
  376. // true if creature haven't reached the final destination hex
  377. if ((nextPos + 1) < destTiles.size())
  378. {
  379. // update the next hex field which has to be reached by the stack
  380. nextPos++;
  381. curStackPos = nextHex;
  382. nextHex = destTiles[nextPos];
  383. // update position of double wide creatures
  384. bool twoTiles = stack->doubleWide();
  385. if(twoTiles && bool(stack->attackerOwned) && (owner->creDir[stack->ID] != bool(stack->attackerOwned) )) //big attacker creature is reversed
  386. myAnim()->pos.x -= 44;
  387. else if(twoTiles && (! bool(stack->attackerOwned) ) && (owner->creDir[stack->ID] != bool(stack->attackerOwned) )) //big defender creature is reversed
  388. myAnim()->pos.x += 44;
  389. // re-init animation
  390. for(std::list<std::pair<CBattleAnimation *, bool> >::iterator it = owner->pendingAnims.begin(); it != owner->pendingAnims.end(); ++it)
  391. {
  392. if (it->first == this)
  393. {
  394. it->second = false;
  395. break;
  396. }
  397. }
  398. }
  399. else
  400. endAnim();
  401. }
  402. }
  403. void CMovementAnimation::endAnim()
  404. {
  405. const CStack * movedStack = stack;
  406. myAnim()->pos = CClickableHex::getXYUnitAnim(nextHex, owner->creDir[stack->ID], movedStack, owner);
  407. CBattleAnimation::endAnim();
  408. if(movedStack)
  409. owner->addNewAnim(new CMovementEndAnimation(owner, stack, nextHex));
  410. if(owner->moveSh >= 0)
  411. {
  412. CCS->soundh->stopSound(owner->moveSh);
  413. owner->moveSh = -1;
  414. }
  415. delete this;
  416. }
  417. CMovementAnimation::CMovementAnimation(CBattleInterface *_owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance)
  418. : CBattleStackAnimation(_owner, _stack), destTiles(_destTiles), nextPos(0), distance(_distance), stepX(0.0), stepY(0.0)
  419. {
  420. curStackPos = stack->position;
  421. nextHex = destTiles.front();
  422. }
  423. CMovementEndAnimation::CMovementEndAnimation(CBattleInterface * _owner, const CStack * _stack, BattleHex destTile)
  424. : CBattleStackAnimation(_owner, _stack), destinationTile(destTile)
  425. {}
  426. bool CMovementEndAnimation::init()
  427. {
  428. if( !isEarliest(true) )
  429. return false;
  430. if(!stack || myAnim()->framesInGroup(CCreatureAnim::MOVE_END) == 0 ||
  431. myAnim()->getType() == CCreatureAnim::DEATH)
  432. {
  433. endAnim();
  434. return false;
  435. }
  436. CCS->soundh->playSound(battle_sound(stack->getCreature(), endMoving));
  437. myAnim()->setType(CCreatureAnim::MOVE_END);
  438. return true;
  439. }
  440. void CMovementEndAnimation::nextFrame()
  441. {
  442. if(myAnim()->onLastFrameInGroup())
  443. {
  444. endAnim();
  445. }
  446. }
  447. void CMovementEndAnimation::endAnim()
  448. {
  449. CBattleAnimation::endAnim();
  450. if(myAnim()->getType() != CCreatureAnim::DEATH)
  451. myAnim()->setType(CCreatureAnim::HOLDING); //resetting to default
  452. CCS->curh->show();
  453. delete this;
  454. }
  455. CMovementStartAnimation::CMovementStartAnimation(CBattleInterface * _owner, const CStack * _stack)
  456. : CBattleStackAnimation(_owner, _stack)
  457. {}
  458. bool CMovementStartAnimation::init()
  459. {
  460. if( !isEarliest(false) )
  461. return false;
  462. if(!stack || myAnim()->getType() == CCreatureAnim::DEATH)
  463. {
  464. CMovementStartAnimation::endAnim();
  465. return false;
  466. }
  467. CCS->soundh->playSound(battle_sound(stack->getCreature(), startMoving));
  468. myAnim()->setType(CCreatureAnim::MOVE_START);
  469. return true;
  470. }
  471. void CMovementStartAnimation::nextFrame()
  472. {
  473. if(myAnim()->onLastFrameInGroup())
  474. {
  475. endAnim();
  476. }
  477. else
  478. {
  479. if((owner->animCount+1)%(4/owner->getAnimSpeed())==0)
  480. myAnim()->incrementFrame();
  481. }
  482. }
  483. void CMovementStartAnimation::endAnim()
  484. {
  485. CBattleAnimation::endAnim();
  486. delete this;
  487. }
  488. CReverseAnimation::CReverseAnimation(CBattleInterface * _owner, const CStack * stack, BattleHex dest, bool _priority)
  489. : CBattleStackAnimation(_owner, stack), partOfAnim(1), secondPartSetup(false), hex(dest), priority(_priority)
  490. {}
  491. bool CReverseAnimation::init()
  492. {
  493. if(myAnim() == nullptr || myAnim()->getType() == CCreatureAnim::DEATH)
  494. {
  495. endAnim();
  496. return false; //there is no such creature
  497. }
  498. if(!priority && !isEarliest(false))
  499. return false;
  500. //myAnim()->pos = CClickableHex::getXYUnitAnim(hex, owner->creDir[stack->ID], stack, owner);
  501. if(myAnim()->framesInGroup(CCreatureAnim::TURN_L))
  502. myAnim()->setType(CCreatureAnim::TURN_L);
  503. else
  504. setupSecondPart();
  505. return true;
  506. }
  507. void CReverseAnimation::nextFrame()
  508. {
  509. if(partOfAnim == 1) //first part of animation
  510. {
  511. if(myAnim()->onLastFrameInGroup())
  512. {
  513. partOfAnim = 2;
  514. }
  515. }
  516. else if(partOfAnim == 2)
  517. {
  518. if(!secondPartSetup)
  519. {
  520. setupSecondPart();
  521. }
  522. if(myAnim()->onLastFrameInGroup())
  523. {
  524. endAnim();
  525. }
  526. }
  527. }
  528. void CReverseAnimation::endAnim()
  529. {
  530. CBattleAnimation::endAnim();
  531. if( stack->alive() )//don't do that if stack is dead
  532. myAnim()->setType(CCreatureAnim::HOLDING);
  533. delete this;
  534. }
  535. void CReverseAnimation::setupSecondPart()
  536. {
  537. if(!stack)
  538. {
  539. endAnim();
  540. return;
  541. }
  542. owner->creDir[stack->ID] = !owner->creDir[stack->ID];
  543. myAnim()->pos = CClickableHex::getXYUnitAnim(hex, owner->creDir[stack->ID], stack, owner);
  544. if(stack->doubleWide())
  545. {
  546. if(stack->attackerOwned)
  547. {
  548. if(!owner->creDir[stack->ID])
  549. myAnim()->pos.x -= 44;
  550. }
  551. else
  552. {
  553. if(owner->creDir[stack->ID])
  554. myAnim()->pos.x += 44;
  555. }
  556. }
  557. secondPartSetup = true;
  558. if(myAnim()->framesInGroup(CCreatureAnim::TURN_R))
  559. myAnim()->setType(CCreatureAnim::TURN_R);
  560. else
  561. endAnim();
  562. }
  563. CShootingAnimation::CShootingAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool _catapult, int _catapultDmg)
  564. : CAttackAnimation(_owner, attacker, _dest, _attacked), catapultDamage(_catapultDmg)
  565. {}
  566. bool CShootingAnimation::init()
  567. {
  568. if( !CAttackAnimation::checkInitialConditions() )
  569. return false;
  570. const CStack * shooter = attackingStack;
  571. if(!shooter || myAnim()->getType() == CCreatureAnim::DEATH)
  572. {
  573. endAnim();
  574. return false;
  575. }
  576. //reverse unit if necessary
  577. if (attackingStack && attackedStack && owner->curInt->cb->isToReverse(attackingStack->position, attackedStack->position, owner->creDir[attackingStack->ID], attackingStack->doubleWide(), owner->creDir[attackedStack->ID]))
  578. {
  579. owner->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->position, true));
  580. return false;
  581. }
  582. // Create the projectile animation
  583. //maximal angle in radians between straight horizontal line and shooting line for which shot is considered to be straight (absoulte value)
  584. static const double straightAngle = 0.2;
  585. // Get further info about the shooter e.g. relative pos of projectile to unit.
  586. // If the creature id is 149 then it's a arrow tower which has no additional info so get the
  587. // actual arrow tower shooter instead.
  588. const CCreature *shooterInfo = shooter->getCreature();
  589. if (shooterInfo->idNumber == CreatureID::ARROW_TOWERS)
  590. {
  591. int creID = owner->siegeH->town->town->clientInfo.siegeShooter;
  592. shooterInfo = CGI->creh->creatures[creID];
  593. }
  594. ProjectileInfo spi;
  595. spi.creID = shooter->getCreature()->idNumber;
  596. spi.stackID = shooter->ID;
  597. // reverse if creature is facing right OR this is non-existing stack that is not tower (war machines)
  598. spi.reverse = attackingStack ? !owner->creDir[attackingStack->ID] : shooter->getCreature()->idNumber != CreatureID::ARROW_TOWERS ;
  599. spi.step = 0;
  600. spi.frameNum = 0;
  601. Point fromPos;
  602. Point destPos;
  603. // NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise
  604. fromPos = owner->creAnims[spi.stackID]->pos.topLeft();
  605. //xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner);
  606. destPos = CClickableHex::getXYUnitAnim(dest, false, attackedStack, owner);
  607. // to properly translate coordinates when shooter is rotated
  608. int multiplier = spi.reverse ? -1 : 1;
  609. double projectileAngle = atan2(fabs((double)destPos.y - fromPos.y), fabs((double)destPos.x - fromPos.x));
  610. if(shooter->position < dest)
  611. projectileAngle = -projectileAngle;
  612. // Calculate projectile start position. Offsets are read out of the CRANIM.TXT.
  613. if (projectileAngle > straightAngle)
  614. {
  615. //upper shot
  616. spi.x = fromPos.x + 222 + ( -25 + shooterInfo->animation.upperRightMissleOffsetX ) * multiplier;
  617. spi.y = fromPos.y + 265 + shooterInfo->animation.upperRightMissleOffsetY;
  618. }
  619. else if (projectileAngle < -straightAngle)
  620. {
  621. //lower shot
  622. spi.x = fromPos.x + 222 + ( -25 + shooterInfo->animation.lowerRightMissleOffsetX ) * multiplier;
  623. spi.y = fromPos.y + 265 + shooterInfo->animation.lowerRightMissleOffsetY;
  624. }
  625. else
  626. {
  627. //straight shot
  628. spi.x = fromPos.x + 222 + ( -25 + shooterInfo->animation.rightMissleOffsetX ) * multiplier;
  629. spi.y = fromPos.y + 265 + shooterInfo->animation.rightMissleOffsetY;
  630. }
  631. destPos += Point(225, 225);
  632. // recalculate angle taking in account offsets
  633. //projectileAngle = atan2(fabs(destPos.y - spi.y), fabs(destPos.x - spi.x));
  634. //if(shooter->position < dest)
  635. // projectileAngle = -projectileAngle;
  636. if (attackedStack)
  637. {
  638. double animSpeed = 23.0 * owner->getAnimSpeed(); // flight speed of projectile
  639. spi.lastStep = static_cast<int>(sqrt(static_cast<double>((destPos.x - spi.x) * (destPos.x - spi.x) + (destPos.y - spi.y) * (destPos.y - spi.y))) / animSpeed);
  640. if(spi.lastStep == 0)
  641. spi.lastStep = 1;
  642. spi.dx = (destPos.x - spi.x) / spi.lastStep;
  643. spi.dy = (destPos.y - spi.y) / spi.lastStep;
  644. }
  645. else
  646. {
  647. // Catapult attack
  648. spi.catapultInfo.reset(new CatapultProjectileInfo(Point(spi.x, spi.y), destPos));
  649. double animSpeed = 3.318 * owner->getAnimSpeed();
  650. spi.lastStep = abs((destPos.x - spi.x) / animSpeed);
  651. spi.dx = animSpeed;
  652. spi.dy = 0;
  653. SDL_Surface * img = owner->idToProjectile[spi.creID]->ourImages[0].bitmap;
  654. // Add explosion anim
  655. Point animPos(destPos.x - 126 + img->w / 2,
  656. destPos.y - 105 + img->h / 2);
  657. owner->addNewAnim( new CSpellEffectAnimation(owner, catapultDamage ? "SGEXPL.DEF" : "CSGRCK.DEF", animPos.x, animPos.y));
  658. }
  659. auto & angles = shooterInfo->animation.missleFrameAngles;
  660. double pi = boost::math::constants::pi<double>();
  661. // only frames below maxFrame are usable: anything higher is either no present or we don't know when it should be used
  662. size_t maxFrame = std::min<size_t>(angles.size(), owner->idToProjectile[spi.creID]->ourImages.size());
  663. assert(maxFrame > 0);
  664. // values in angles array indicate position from which this frame was rendered, in degrees.
  665. // find frame that has closest angle to one that we need for this shot
  666. size_t bestID = 0;
  667. double bestDiff = fabs( angles[0] / 180 * pi - projectileAngle );
  668. for (size_t i=1; i<maxFrame; i++)
  669. {
  670. double currentDiff = fabs( angles[i] / 180 * pi - projectileAngle );
  671. if (currentDiff < bestDiff)
  672. {
  673. bestID = i;
  674. bestDiff = currentDiff;
  675. }
  676. }
  677. spi.frameNum = bestID;
  678. // Set projectile animation start delay which is specified in frames
  679. spi.animStartDelay = shooterInfo->animation.attackClimaxFrame;
  680. owner->projectiles.push_back(spi);
  681. //attack animation
  682. shooting = true;
  683. if(projectileAngle > straightAngle) //upper shot
  684. group = CCreatureAnim::SHOOT_UP;
  685. else if(projectileAngle < -straightAngle) //lower shot
  686. group = CCreatureAnim::SHOOT_DOWN;
  687. else //straight shot
  688. group = CCreatureAnim::SHOOT_FRONT;
  689. return true;
  690. }
  691. void CShootingAnimation::nextFrame()
  692. {
  693. for(std::list<std::pair<CBattleAnimation *, bool> >::const_iterator it = owner->pendingAnims.begin(); it != owner->pendingAnims.end(); ++it)
  694. {
  695. CMovementStartAnimation * anim = dynamic_cast<CMovementStartAnimation *>(it->first);
  696. CReverseAnimation * anim2 = dynamic_cast<CReverseAnimation *>(it->first);
  697. if( (anim && anim->stack->ID == stack->ID) || (anim2 && anim2->stack->ID == stack->ID && anim2->priority ) )
  698. return;
  699. }
  700. CAttackAnimation::nextFrame();
  701. }
  702. void CShootingAnimation::endAnim()
  703. {
  704. CBattleAnimation::endAnim();
  705. delete this;
  706. }
  707. CSpellEffectAnimation::CSpellEffectAnimation(CBattleInterface * _owner, ui32 _effect, BattleHex _destTile, int _dx, int _dy, bool _Vflip)
  708. :CBattleAnimation(_owner), effect(_effect), destTile(_destTile), customAnim(""), x(0), y(0), dx(_dx), dy(_dy), Vflip(_Vflip)
  709. {}
  710. CSpellEffectAnimation::CSpellEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx, int _dy, bool _Vflip)
  711. :CBattleAnimation(_owner), effect(-1), destTile(0), customAnim(_customAnim), x(_x), y(_y), dx(_dx), dy(_dy), Vflip(_Vflip)
  712. {}
  713. bool CSpellEffectAnimation::init()
  714. {
  715. if(!isEarliest(true))
  716. return false;
  717. if(effect == 12) //armageddon
  718. {
  719. if(effect == -1 || graphics->battleACToDef[effect].size() != 0)
  720. {
  721. CDefHandler * anim;
  722. if(customAnim.size())
  723. anim = CDefHandler::giveDef(customAnim);
  724. else
  725. anim = CDefHandler::giveDef(graphics->battleACToDef[effect][0]);
  726. if (Vflip)
  727. {
  728. for (size_t v = 0; v < anim->ourImages.size(); ++v)
  729. {
  730. CSDL_Ext::VflipSurf(anim->ourImages[v].bitmap);
  731. }
  732. }
  733. for(int i=0; i * anim->width < owner->pos.w ; ++i)
  734. {
  735. for(int j=0; j * anim->height < owner->pos.h ; ++j)
  736. {
  737. BattleEffect be;
  738. be.effectID = ID;
  739. be.anim = CDefHandler::giveDef(graphics->battleACToDef[effect][0]);
  740. if (Vflip)
  741. {
  742. for (size_t v = 0; v < be.anim->ourImages.size(); ++v)
  743. {
  744. CSDL_Ext::VflipSurf(be.anim->ourImages[v].bitmap);
  745. }
  746. }
  747. be.frame = 0;
  748. be.maxFrame = be.anim->ourImages.size();
  749. be.x = i * anim->width + owner->pos.x;
  750. be.y = j * anim->height + owner->pos.y;
  751. owner->battleEffects.push_back(be);
  752. }
  753. }
  754. }
  755. else //there is nothing to play
  756. {
  757. endAnim();
  758. return false;
  759. }
  760. }
  761. else // Effects targeted at a specific creature/hex.
  762. {
  763. if(effect == -1 || graphics->battleACToDef[effect].size() != 0)
  764. {
  765. const CStack* destStack = owner->curInt->cb->battleGetStackByPos(destTile, false);
  766. Rect &tilePos = owner->bfield[destTile]->pos;
  767. BattleEffect be;
  768. be.effectID = ID;
  769. if(customAnim.size())
  770. be.anim = CDefHandler::giveDef(customAnim);
  771. else
  772. be.anim = CDefHandler::giveDef(graphics->battleACToDef[effect][0]);
  773. if (Vflip)
  774. {
  775. for (size_t v = 0; v < be.anim->ourImages.size(); ++v)
  776. {
  777. CSDL_Ext::VflipSurf(be.anim->ourImages[v].bitmap);
  778. }
  779. }
  780. be.frame = 0;
  781. be.maxFrame = be.anim->ourImages.size();
  782. if(effect == 1)
  783. be.maxFrame = 3;
  784. switch (effect)
  785. {
  786. case ui32(-1):
  787. be.x = x;
  788. be.y = y;
  789. break;
  790. case 0: // Prayer and Lightning Bolt.
  791. case 1:
  792. // Position effect with it's bottom center touching the bottom center of affected tile(s).
  793. be.x = tilePos.x + tilePos.w/2 - be.anim->width/2;
  794. be.y = tilePos.y + tilePos.h - be.anim->height;
  795. break;
  796. default:
  797. // Position effect with it's center touching the top center of affected tile(s).
  798. be.x = tilePos.x + tilePos.w/2 - be.anim->width/2;
  799. be.y = tilePos.y - be.anim->height/2;
  800. break;
  801. }
  802. // Correction for 2-hex creatures.
  803. if (destStack != nullptr && destStack->doubleWide())
  804. be.x += (destStack->attackerOwned ? -1 : 1)*tilePos.w/2;
  805. owner->battleEffects.push_back(be);
  806. }
  807. else //there is nothing to play
  808. {
  809. endAnim();
  810. return false;
  811. }
  812. }
  813. //battleEffects
  814. return true;
  815. }
  816. void CSpellEffectAnimation::nextFrame()
  817. {
  818. //notice: there may be more than one effect in owner->battleEffects correcponding to this animation (ie. armageddon)
  819. for(std::list<BattleEffect>::iterator it = owner->battleEffects.begin(); it != owner->battleEffects.end(); ++it)
  820. {
  821. if(it->effectID == ID)
  822. {
  823. ++(it->frame);
  824. if(it->frame == it->maxFrame)
  825. {
  826. endAnim();
  827. break;
  828. }
  829. else
  830. {
  831. it->x += dx;
  832. it->y += dy;
  833. }
  834. }
  835. }
  836. }
  837. void CSpellEffectAnimation::endAnim()
  838. {
  839. CBattleAnimation::endAnim();
  840. std::vector<std::list<BattleEffect>::iterator> toDel;
  841. for(std::list<BattleEffect>::iterator it = owner->battleEffects.begin(); it != owner->battleEffects.end(); ++it)
  842. {
  843. if(it->effectID == ID)
  844. {
  845. toDel.push_back(it);
  846. }
  847. }
  848. for(size_t b = 0; b < toDel.size(); ++b)
  849. {
  850. delete toDel[b]->anim;
  851. owner->battleEffects.erase(toDel[b]);
  852. }
  853. delete this;
  854. }