BattleObstacleController.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /*
  2. * BattleObstacleController.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 "BattleObstacleController.h"
  12. #include "BattleInterface.h"
  13. #include "BattleFieldController.h"
  14. #include "BattleAnimationClasses.h"
  15. #include "BattleStacksController.h"
  16. #include "BattleRenderer.h"
  17. #include "CreatureAnimation.h"
  18. #include "../CPlayerInterface.h"
  19. #include "../GameEngine.h"
  20. #include "../media/ISoundPlayer.h"
  21. #include "../render/CAnimation.h"
  22. #include "../render/Canvas.h"
  23. #include "../render/IRenderHandler.h"
  24. #include "../../lib/battle/CObstacleInstance.h"
  25. #include "../../lib/ObstacleHandler.h"
  26. #include "../../lib/battle/CPlayerBattleCallback.h"
  27. #include "../../lib/serializer/JsonDeserializer.h"
  28. BattleObstacleController::BattleObstacleController(BattleInterface & owner):
  29. owner(owner),
  30. timePassed(0.f)
  31. {
  32. auto obst = owner.getBattle()->battleGetAllObstacles();
  33. for(auto & elem : obst)
  34. {
  35. if ( elem->obstacleType == CObstacleInstance::MOAT )
  36. continue; // handled by siege controller;
  37. loadObstacleImage(*elem);
  38. }
  39. }
  40. void BattleObstacleController::loadObstacleImage(const CObstacleInstance & oi)
  41. {
  42. AnimationPath animationName = oi.getAnimation();
  43. if (oi.obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
  44. {
  45. // obstacle uses single bitmap image for animations
  46. obstacleImages[oi.uniqueID] = ENGINE->renderHandler().loadImage(animationName.toType<EResType::IMAGE>(), EImageBlitMode::SIMPLE);
  47. }
  48. else
  49. {
  50. obstacleAnimations[oi.uniqueID] = ENGINE->renderHandler().loadAnimation(animationName, EImageBlitMode::SIMPLE);
  51. obstacleImages[oi.uniqueID] = obstacleAnimations[oi.uniqueID]->getImage(0);
  52. }
  53. }
  54. void BattleObstacleController::obstacleRemoved(const std::vector<ObstacleChanges> & obstacles)
  55. {
  56. for(const auto & oi : obstacles)
  57. {
  58. auto & obstacle = oi.data["obstacle"];
  59. if (!obstacle.isStruct())
  60. {
  61. logGlobal->error("I don't know how to animate removal of this obstacle");
  62. continue;
  63. }
  64. AnimationPath animationPath;
  65. JsonDeserializer ser(nullptr, obstacle);
  66. ser.serializeStruct("appearAnimation", animationPath);
  67. if(animationPath.empty())
  68. continue;
  69. auto animation = ENGINE->renderHandler().loadAnimation(animationPath, EImageBlitMode::SIMPLE);
  70. auto first = animation->getImage(0, 0);
  71. if(!first)
  72. continue;
  73. //we assume here that effect graphics have the same size as the usual obstacle image
  74. // -> if we know how to blit obstacle, let's blit the effect in the same place
  75. Point whereTo = getObstaclePosition(first, obstacle);
  76. //AFAIK, in H3 there is no sound of obstacle removal
  77. owner.stacksController->addNewAnim(new EffectAnimation(owner, animationPath, whereTo, obstacle["position"].Integer(), 0, true));
  78. obstacleAnimations.erase(oi.id);
  79. obstacleImages.erase(oi.id);
  80. //so when multiple obstacles are removed, they show up one after another
  81. owner.waitForAnimations();
  82. }
  83. }
  84. void BattleObstacleController::obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> & obstacles)
  85. {
  86. for(const auto & oi : obstacles)
  87. {
  88. auto side = owner.getBattle()->playerToSide(owner.curInt->playerID);
  89. if(!oi->visibleForSide(side, owner.getBattle()->battleHasNativeStack(side)))
  90. continue;
  91. auto animation = ENGINE->renderHandler().loadAnimation(oi->getAppearAnimation(), EImageBlitMode::SIMPLE);
  92. auto first = animation->getImage(0, 0);
  93. if(!first)
  94. continue;
  95. //we assume here that effect graphics have the same size as the usual obstacle image
  96. // -> if we know how to blit obstacle, let's blit the effect in the same place
  97. Point whereTo = getObstaclePosition(first, *oi);
  98. ENGINE->sound().playSound( oi->getAppearSound() );
  99. owner.stacksController->addNewAnim(new EffectAnimation(owner, oi->getAppearAnimation(), whereTo, oi->pos));
  100. //so when multiple obstacles are added, they show up one after another
  101. owner.waitForAnimations();
  102. loadObstacleImage(*oi);
  103. }
  104. }
  105. void BattleObstacleController::showAbsoluteObstacles(Canvas & canvas)
  106. {
  107. //Blit absolute obstacles
  108. for(auto & obstacle : owner.getBattle()->battleGetAllObstacles())
  109. {
  110. if(obstacle->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
  111. {
  112. auto img = getObstacleImage(*obstacle);
  113. if(img)
  114. canvas.draw(img, Point(obstacle->getInfo().width, obstacle->getInfo().height));
  115. }
  116. if (obstacle->obstacleType == CObstacleInstance::USUAL)
  117. {
  118. if (obstacle->getInfo().isForegroundObstacle)
  119. continue;
  120. auto img = getObstacleImage(*obstacle);
  121. if(img)
  122. {
  123. Point p = getObstaclePosition(img, *obstacle);
  124. canvas.draw(img, p);
  125. }
  126. }
  127. }
  128. }
  129. void BattleObstacleController::collectRenderableObjects(BattleRenderer & renderer)
  130. {
  131. for (auto obstacle : owner.getBattle()->battleGetAllObstacles())
  132. {
  133. if (obstacle->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
  134. continue;
  135. if (obstacle->obstacleType == CObstacleInstance::MOAT)
  136. continue;
  137. if (obstacle->obstacleType == CObstacleInstance::USUAL && !obstacle->getInfo().isForegroundObstacle)
  138. continue;
  139. renderer.insert(EBattleFieldLayer::OBSTACLES, obstacle->pos, [this, obstacle]( BattleRenderer::RendererRef canvas ){
  140. auto img = getObstacleImage(*obstacle);
  141. if(img)
  142. {
  143. Point p = getObstaclePosition(img, *obstacle);
  144. canvas.draw(img, p);
  145. }
  146. });
  147. }
  148. }
  149. void BattleObstacleController::tick(uint32_t msPassed)
  150. {
  151. timePassed += msPassed / 1000.f;
  152. int framesCount = timePassed * AnimationControls::getObstaclesSpeed();
  153. for(auto & animation : obstacleAnimations)
  154. {
  155. int frameIndex = framesCount % animation.second->size(0);
  156. obstacleImages[animation.first] = animation.second->getImage(frameIndex, 0);
  157. }
  158. }
  159. std::shared_ptr<IImage> BattleObstacleController::getObstacleImage(const CObstacleInstance & oi)
  160. {
  161. // obstacle is not loaded yet, don't show anything
  162. if (obstacleImages.count(oi.uniqueID) == 0)
  163. return nullptr;
  164. return obstacleImages[oi.uniqueID];
  165. }
  166. Point BattleObstacleController::getObstaclePosition(std::shared_ptr<IImage> image, const CObstacleInstance & obstacle)
  167. {
  168. int offset = obstacle.getAnimationYOffset(image->height());
  169. Rect r = owner.fieldController->hexPositionLocal(obstacle.pos);
  170. r.y += 42 - image->height() + offset;
  171. return r.topLeft();
  172. }
  173. Point BattleObstacleController::getObstaclePosition(std::shared_ptr<IImage> image, const JsonNode & obstacle)
  174. {
  175. auto animationYOffset = obstacle["animationYOffset"].Integer();
  176. auto offset = image->height() % 42;
  177. if(obstacle["needAnimationOffsetFix"].Bool() && offset > 37)
  178. animationYOffset -= 42;
  179. offset += animationYOffset;
  180. Rect r = owner.fieldController->hexPositionLocal(obstacle["position"].Integer());
  181. r.y += 42 - image->height() + offset;
  182. return r.topLeft();
  183. }