BattleHero.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /*
  2. * BattleHero.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 "BattleHero.h"
  12. #include "BattleActionsController.h"
  13. #include "BattleFieldController.h"
  14. #include "BattleInterface.h"
  15. #include "BattleRenderer.h"
  16. #include "HeroInfoWindow.h"
  17. #include "../GameEngine.h"
  18. #include "../gui/CursorHandler.h"
  19. #include "../gui/WindowHandler.h"
  20. #include "../render/CAnimation.h"
  21. #include "../render/Canvas.h"
  22. #include "../render/IRenderHandler.h"
  23. #include "../windows/CSpellWindow.h"
  24. #include "../../lib/CConfigHandler.h"
  25. #include "../../lib/battle/CPlayerBattleCallback.h"
  26. #include "../../lib/entities/hero/CHero.h"
  27. #include "../../lib/entities/hero/CHeroClass.h"
  28. #include "../../lib/gameState/InfoAboutArmy.h"
  29. #include "../../lib/mapObjects/CGHeroInstance.h"
  30. const CGHeroInstance * BattleHero::instance() const
  31. {
  32. return hero;
  33. }
  34. void BattleHero::tick(uint32_t msPassed)
  35. {
  36. size_t groupIndex = static_cast<size_t>(phase);
  37. float timePassed = msPassed / 1000.f;
  38. flagCurrentFrame += currentSpeed * timePassed;
  39. currentFrame += currentSpeed * timePassed;
  40. if(flagCurrentFrame >= flagAnimation->size(0))
  41. flagCurrentFrame -= flagAnimation->size(0);
  42. if(currentFrame >= animation->size(groupIndex))
  43. {
  44. currentFrame -= animation->size(groupIndex);
  45. switchToNextPhase();
  46. }
  47. }
  48. void BattleHero::render(Canvas & canvas)
  49. {
  50. size_t groupIndex = static_cast<size_t>(phase);
  51. auto flagFrame = flagAnimation->getImage(flagCurrentFrame, 0, true);
  52. auto heroFrame = animation->getImage(currentFrame, groupIndex, true);
  53. Point heroPosition = pos.center() - parent->pos.topLeft() - heroFrame->dimensions() / 2;
  54. Point flagPosition = pos.center() - parent->pos.topLeft() - flagFrame->dimensions() / 2;
  55. if(defender)
  56. flagPosition += Point(-4, -41);
  57. else
  58. flagPosition += Point(4, -41);
  59. canvas.draw(flagFrame, flagPosition);
  60. canvas.draw(heroFrame, heroPosition);
  61. }
  62. void BattleHero::pause()
  63. {
  64. currentSpeed = 0.f;
  65. }
  66. void BattleHero::play()
  67. {
  68. //H3 speed: 10 fps ( 100 ms per frame)
  69. currentSpeed = 10.f;
  70. }
  71. float BattleHero::getFrame() const
  72. {
  73. return currentFrame;
  74. }
  75. void BattleHero::collectRenderableObjects(BattleRenderer & renderer)
  76. {
  77. auto hex = defender ? BattleHex(GameConstants::BFIELD_WIDTH-1) : BattleHex(0);
  78. renderer.insert(EBattleFieldLayer::HEROES, hex, [this](BattleRenderer::RendererRef canvas)
  79. {
  80. render(canvas);
  81. });
  82. }
  83. void BattleHero::onPhaseFinished(const std::function<void()> & callback)
  84. {
  85. phaseFinishedCallback = callback;
  86. }
  87. void BattleHero::setPhase(EHeroAnimType newPhase)
  88. {
  89. nextPhase = newPhase;
  90. switchToNextPhase(); //immediately switch to next phase and then restore idling phase
  91. nextPhase = EHeroAnimType::HOLDING;
  92. }
  93. void BattleHero::heroLeftClicked()
  94. {
  95. if(owner.actionsController->heroSpellcastingModeActive()) //we are casting a spell
  96. return;
  97. if(!hero || !owner.makingTurn())
  98. return;
  99. if(owner.getBattle()->battleCanCastSpell(hero, spells::Mode::HERO) == ESpellCastProblem::OK) //check conditions
  100. {
  101. ENGINE->cursor().set(Cursor::Map::POINTER);
  102. ENGINE->windows().createAndPushWindow<CSpellWindow>(hero, owner.getCurrentPlayerInterface());
  103. }
  104. }
  105. void BattleHero::heroRightClicked() const
  106. {
  107. if(settings["battle"]["stickyHeroInfoWindows"].Bool())
  108. return;
  109. Point windowPosition;
  110. if(ENGINE->screenDimensions().x < 1000)
  111. {
  112. windowPosition.x = (!defender) ? owner.fieldController->pos.left() + 1 : owner.fieldController->pos.right() - 79;
  113. windowPosition.y = owner.fieldController->pos.y + 135;
  114. }
  115. else
  116. {
  117. windowPosition.x = (!defender) ? owner.fieldController->pos.left() - 93 : owner.fieldController->pos.right() + 15;
  118. windowPosition.y = owner.fieldController->pos.y;
  119. }
  120. InfoAboutHero targetHero;
  121. if(owner.makingTurn() || settings["session"]["spectate"].Bool())
  122. {
  123. const auto * h = defender ? owner.defendingHeroInstance : owner.attackingHeroInstance;
  124. targetHero.initFromHero(h, InfoAboutHero::EInfoLevel::INBATTLE);
  125. ENGINE->windows().createAndPushWindow<HeroInfoWindow>(targetHero, &windowPosition);
  126. }
  127. }
  128. void BattleHero::switchToNextPhase()
  129. {
  130. phase = nextPhase;
  131. currentFrame = 0.f;
  132. auto copy = phaseFinishedCallback;
  133. phaseFinishedCallback.clear();
  134. copy();
  135. }
  136. BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * hero, bool defender)
  137. : defender(defender)
  138. , hero(hero)
  139. , owner(owner)
  140. , phase(EHeroAnimType::HOLDING)
  141. , nextPhase(EHeroAnimType::HOLDING)
  142. , currentSpeed(0.f)
  143. , currentFrame(0.f)
  144. , flagCurrentFrame(0.f)
  145. {
  146. AnimationPath animationPath;
  147. if(!hero->getHeroType()->battleImage.empty())
  148. animationPath = hero->getHeroType()->battleImage;
  149. else if(hero->gender == EHeroGender::FEMALE)
  150. animationPath = hero->getHeroClass()->imageBattleFemale;
  151. else
  152. animationPath = hero->getHeroClass()->imageBattleMale;
  153. animation = ENGINE->renderHandler().loadAnimation(animationPath, EImageBlitMode::WITH_SHADOW);
  154. pos.w = 64;
  155. pos.h = 136;
  156. pos.x = owner.fieldController->pos.x + (defender ? (owner.fieldController->pos.w - pos.w) : 0);
  157. pos.y = owner.fieldController->pos.y;
  158. if(defender)
  159. animation->verticalFlip();
  160. if(defender)
  161. flagAnimation = ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CMFLAGR"), EImageBlitMode::COLORKEY);
  162. else
  163. flagAnimation = ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CMFLAGL"), EImageBlitMode::COLORKEY);
  164. flagAnimation->playerColored(hero->tempOwner);
  165. switchToNextPhase();
  166. play();
  167. addUsedEvents(TIME);
  168. }