CursorHandler.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. * CCursorHandler.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 "CursorHandler.h"
  12. #include "GameEngine.h"
  13. #include "FramerateManager.h"
  14. #include "../renderSDL/CursorSoftware.h"
  15. #include "../renderSDL/CursorHardware.h"
  16. #include "../render/CAnimation.h"
  17. #include "../render/IImage.h"
  18. #include "../render/IScreenHandler.h"
  19. #include "../render/IRenderHandler.h"
  20. #include "../../lib/CConfigHandler.h"
  21. #include "../../lib/json/JsonUtils.h"
  22. std::unique_ptr<ICursor> CursorHandler::createCursor()
  23. {
  24. #if defined(VCMI_MOBILE) || defined(VCMI_PORTMASTER)
  25. if (settings["general"]["userRelativePointer"].Bool())
  26. return std::make_unique<CursorSoftware>();
  27. #endif
  28. if (settings["video"]["cursor"].String() == "hardware")
  29. return std::make_unique<CursorHardware>();
  30. assert(settings["video"]["cursor"].String() == "software");
  31. return std::make_unique<CursorSoftware>();
  32. }
  33. CursorHandler::CursorHandler()
  34. : cursor(createCursor())
  35. , frameTime(0.f)
  36. , showing(false)
  37. , pos(0,0)
  38. , dndObject(nullptr)
  39. {
  40. showType = dynamic_cast<CursorSoftware *>(cursor.get()) ? Cursor::ShowType::SOFTWARE : Cursor::ShowType::HARDWARE;
  41. }
  42. CursorHandler::~CursorHandler() = default;
  43. void CursorHandler::init()
  44. {
  45. JsonNode cursorConfig = JsonUtils::assembleFromFiles("config/cursors.json");
  46. std::vector<AnimationPath> animations;
  47. for (const auto & cursorEntry : cursorConfig.Struct())
  48. {
  49. CursorParameters parameters;
  50. parameters.cursorID = cursorEntry.first;
  51. parameters.image = ImagePath::fromJson(cursorEntry.second["image"]);
  52. parameters.animation = AnimationPath::fromJson(cursorEntry.second["animation"]);
  53. parameters.animationFrameIndex = cursorEntry.second["frame"].Integer();
  54. parameters.isAnimated = cursorEntry.second["animated"].Bool();
  55. parameters.pivot.x = cursorEntry.second["pivotX"].Integer();
  56. parameters.pivot.y = cursorEntry.second["pivotY"].Integer();
  57. cursors.push_back(parameters);
  58. }
  59. // TODO: Preload?
  60. set(Cursor::Map::POINTER);
  61. }
  62. void CursorHandler::set(const std::string & index)
  63. {
  64. assert(dndObject == nullptr);
  65. if (index == currentCursorID)
  66. return;
  67. currentCursorID = index;
  68. currentCursorIndex = 0;
  69. frameTime = 0;
  70. for (size_t i = 0; i < cursors.size(); ++i)
  71. {
  72. if (cursors[i].cursorID == index)
  73. {
  74. currentCursorIndex = i;
  75. break;
  76. }
  77. }
  78. const auto & currentCursor = cursors.at(currentCursorIndex);
  79. if (currentCursor.image.empty())
  80. {
  81. if (!loadedAnimations.count(currentCursor.animation))
  82. loadedAnimations[currentCursor.animation] = ENGINE->renderHandler().loadAnimation(currentCursor.animation, EImageBlitMode::COLORKEY);
  83. if (currentCursor.isAnimated)
  84. cursorImage = loadedAnimations[currentCursor.animation]->getImage(0);
  85. else
  86. cursorImage = loadedAnimations[currentCursor.animation]->getImage(currentCursor.animationFrameIndex);
  87. }
  88. else
  89. {
  90. if (!loadedImages.count(currentCursor.image))
  91. loadedImages[currentCursor.image] = ENGINE->renderHandler().loadImage(currentCursor.image, EImageBlitMode::COLORKEY);
  92. cursorImage = loadedImages[currentCursor.image];
  93. }
  94. cursor->setImage(getCurrentImage(), getPivotOffset());
  95. }
  96. void CursorHandler::set(Cursor::Map index)
  97. {
  98. constexpr std::array mapCursorNames =
  99. {
  100. "mapPointer",
  101. "mapHourglass",
  102. "mapHero",
  103. "mapTown",
  104. "mapTurn1Move",
  105. "mapTurn1Attack",
  106. "mapTurn1Sail",
  107. "mapTurn1Disembark",
  108. "mapTurn1Exchange",
  109. "mapTurn1Visit",
  110. "mapTurn2Move",
  111. "mapTurn2Attack",
  112. "mapTurn2Sail",
  113. "mapTurn2Disembark",
  114. "mapTurn2Exchange",
  115. "mapTurn2Visit",
  116. "mapTurn3Move",
  117. "mapTurn3Attack",
  118. "mapTurn3Sail",
  119. "mapTurn3Disembark",
  120. "mapTurn3Exchange",
  121. "mapTurn3Visit",
  122. "mapTurn4Move",
  123. "mapTurn4Attack",
  124. "mapTurn4Sail",
  125. "mapTurn4Disembark",
  126. "mapTurn4Exchange",
  127. "mapTurn4Visit",
  128. "mapTurn1SailVisit",
  129. "mapTurn2SailVisit",
  130. "mapTurn3SailVisit",
  131. "mapTurn4SailVisit",
  132. "mapScrollNorth",
  133. "mapScrollNorthEast",
  134. "mapScrollEast",
  135. "mapScrollSouthEast",
  136. "mapScrollSouth",
  137. "mapScrollSouthWest",
  138. "mapScrollWest",
  139. "mapScrollNorthWest",
  140. "UNUSED",
  141. "mapDimensionDoor",
  142. "mapScuttleBoat"
  143. };
  144. set(mapCursorNames.at(static_cast<int>(index)));
  145. }
  146. void CursorHandler::set(Cursor::Combat index)
  147. {
  148. constexpr std::array combatCursorNames =
  149. {
  150. "combatBlocked",
  151. "combatMove",
  152. "combatFly",
  153. "combatShoot",
  154. "combatHero",
  155. "combatQuery",
  156. "combatPointer",
  157. "combatHitNorthEast",
  158. "combatHitEast",
  159. "combatHitSouthEast",
  160. "combatHitSouthWest",
  161. "combatHitWest",
  162. "combatHitNorthWest",
  163. "combatHitNorth",
  164. "combatHitSouth",
  165. "combatShootPenalty",
  166. "combatShootCatapult",
  167. "combatHeal",
  168. "combatSacrifice",
  169. "combatTeleport"
  170. };
  171. set(combatCursorNames.at(static_cast<int>(index)));
  172. }
  173. void CursorHandler::set(Cursor::Spellcast index)
  174. {
  175. //Note: this is animated cursor, ignore requested frame and only change type
  176. set("castSpell");
  177. }
  178. void CursorHandler::dragAndDropCursor(std::shared_ptr<IImage> image)
  179. {
  180. dndObject = image;
  181. cursor->setImage(getCurrentImage(), getPivotOffset());
  182. }
  183. void CursorHandler::dragAndDropCursor (const AnimationPath & path, size_t index)
  184. {
  185. auto anim = ENGINE->renderHandler().loadAnimation(path, EImageBlitMode::COLORKEY);
  186. dragAndDropCursor(anim->getImage(index));
  187. }
  188. void CursorHandler::cursorMove(const int & x, const int & y)
  189. {
  190. pos.x = x;
  191. pos.y = y;
  192. cursor->setCursorPosition(pos);
  193. }
  194. Point CursorHandler::getPivotOffset()
  195. {
  196. if (dndObject)
  197. return dndObject->dimensions() / 2;
  198. return cursors.at(currentCursorIndex).pivot;
  199. }
  200. std::shared_ptr<IImage> CursorHandler::getCurrentImage()
  201. {
  202. if (dndObject)
  203. return dndObject;
  204. return cursorImage;
  205. }
  206. void CursorHandler::updateAnimatedCursor()
  207. {
  208. static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
  209. frameTime += ENGINE->framerate().getElapsedMilliseconds() / 1000.f;
  210. int32_t newFrame = currentFrame;
  211. const auto & animationName = cursors.at(currentCursorIndex).animation;
  212. const auto & animation = loadedAnimations.at(animationName);
  213. while (frameTime >= frameDisplayDuration)
  214. {
  215. frameTime -= frameDisplayDuration;
  216. newFrame++;
  217. }
  218. while (newFrame >= animation->size())
  219. newFrame -= animation->size();
  220. currentFrame = newFrame;
  221. cursorImage = animation->getImage(currentFrame);
  222. cursor->setImage(getCurrentImage(), getPivotOffset());
  223. }
  224. void CursorHandler::render()
  225. {
  226. cursor->render();
  227. }
  228. void CursorHandler::update()
  229. {
  230. if(!showing)
  231. return;
  232. if (cursors.at(currentCursorIndex).isAnimated)
  233. updateAnimatedCursor();
  234. cursor->update();
  235. }
  236. void CursorHandler::hide()
  237. {
  238. if (!showing)
  239. return;
  240. showing = false;
  241. cursor->setVisible(false);
  242. }
  243. void CursorHandler::show()
  244. {
  245. if (showing)
  246. return;
  247. showing = true;
  248. cursor->setVisible(true);
  249. }
  250. Cursor::ShowType CursorHandler::getShowType() const
  251. {
  252. return showType;
  253. }
  254. void CursorHandler::changeCursor(Cursor::ShowType newShowType)
  255. {
  256. if(newShowType == showType)
  257. return;
  258. switch(newShowType)
  259. {
  260. case Cursor::ShowType::SOFTWARE:
  261. cursor.reset(new CursorSoftware());
  262. showType = Cursor::ShowType::SOFTWARE;
  263. cursor->setImage(getCurrentImage(), getPivotOffset());
  264. break;
  265. case Cursor::ShowType::HARDWARE:
  266. cursor.reset(new CursorHardware());
  267. showType = Cursor::ShowType::HARDWARE;
  268. cursor->setImage(getCurrentImage(), getPivotOffset());
  269. break;
  270. }
  271. }
  272. void CursorHandler::onScreenResize()
  273. {
  274. cursor->setImage(getCurrentImage(), getPivotOffset());
  275. }