CursorHandler.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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. std::unique_ptr<ICursor> CursorHandler::createCursor()
  22. {
  23. #if defined(VCMI_MOBILE) || defined(VCMI_PORTMASTER)
  24. if (settings["general"]["userRelativePointer"].Bool())
  25. return std::make_unique<CursorSoftware>();
  26. #endif
  27. if (settings["video"]["cursor"].String() == "hardware")
  28. return std::make_unique<CursorHardware>();
  29. assert(settings["video"]["cursor"].String() == "software");
  30. return std::make_unique<CursorSoftware>();
  31. }
  32. CursorHandler::CursorHandler()
  33. : cursor(createCursor())
  34. , frameTime(0.f)
  35. , showing(false)
  36. , pos(0,0)
  37. , dndObject(nullptr)
  38. , type(Cursor::Type::DEFAULT)
  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. cursors =
  46. {
  47. ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CRADVNTR"), EImageBlitMode::COLORKEY),
  48. ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT"), EImageBlitMode::COLORKEY),
  49. ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CRDEFLT"), EImageBlitMode::COLORKEY),
  50. ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL"), EImageBlitMode::COLORKEY)
  51. };
  52. set(Cursor::Map::POINTER);
  53. }
  54. void CursorHandler::changeGraphic(Cursor::Type type, size_t index)
  55. {
  56. assert(dndObject == nullptr);
  57. if (type == this->type && index == this->frame)
  58. return;
  59. this->type = type;
  60. this->frame = index;
  61. cursor->setImage(getCurrentImage(), getPivotOffset());
  62. }
  63. void CursorHandler::set(Cursor::Default index)
  64. {
  65. changeGraphic(Cursor::Type::DEFAULT, static_cast<size_t>(index));
  66. }
  67. void CursorHandler::set(Cursor::Map index)
  68. {
  69. changeGraphic(Cursor::Type::ADVENTURE, static_cast<size_t>(index));
  70. }
  71. void CursorHandler::set(Cursor::Combat index)
  72. {
  73. changeGraphic(Cursor::Type::COMBAT, static_cast<size_t>(index));
  74. }
  75. void CursorHandler::set(Cursor::Spellcast index)
  76. {
  77. //Note: this is animated cursor, ignore specified frame and only change type
  78. changeGraphic(Cursor::Type::SPELLBOOK, frame);
  79. }
  80. void CursorHandler::dragAndDropCursor(std::shared_ptr<IImage> image)
  81. {
  82. dndObject = image;
  83. cursor->setImage(getCurrentImage(), getPivotOffset());
  84. }
  85. void CursorHandler::dragAndDropCursor (const AnimationPath & path, size_t index)
  86. {
  87. auto anim = ENGINE->renderHandler().loadAnimation(path, EImageBlitMode::COLORKEY);
  88. dragAndDropCursor(anim->getImage(index));
  89. }
  90. void CursorHandler::cursorMove(const int & x, const int & y)
  91. {
  92. pos.x = x;
  93. pos.y = y;
  94. cursor->setCursorPosition(pos);
  95. }
  96. Point CursorHandler::getPivotOffsetDefault(size_t index)
  97. {
  98. return {0, 0};
  99. }
  100. Point CursorHandler::getPivotOffsetMap(size_t index)
  101. {
  102. static const std::array<Point, 43> offsets = {{
  103. { 0, 0}, // POINTER = 0,
  104. { 0, 0}, // HOURGLASS = 1,
  105. { 12, 10}, // HERO = 2,
  106. { 12, 12}, // TOWN = 3,
  107. { 15, 13}, // T1_MOVE = 4,
  108. { 13, 13}, // T1_ATTACK = 5,
  109. { 16, 32}, // T1_SAIL = 6,
  110. { 13, 20}, // T1_DISEMBARK = 7,
  111. { 8, 9}, // T1_EXCHANGE = 8,
  112. { 14, 16}, // T1_VISIT = 9,
  113. { 15, 13}, // T2_MOVE = 10,
  114. { 13, 13}, // T2_ATTACK = 11,
  115. { 16, 32}, // T2_SAIL = 12,
  116. { 13, 20}, // T2_DISEMBARK = 13,
  117. { 8, 9}, // T2_EXCHANGE = 14,
  118. { 14, 16}, // T2_VISIT = 15,
  119. { 15, 13}, // T3_MOVE = 16,
  120. { 13, 13}, // T3_ATTACK = 17,
  121. { 16, 32}, // T3_SAIL = 18,
  122. { 13, 20}, // T3_DISEMBARK = 19,
  123. { 8, 9}, // T3_EXCHANGE = 20,
  124. { 14, 16}, // T3_VISIT = 21,
  125. { 15, 13}, // T4_MOVE = 22,
  126. { 13, 13}, // T4_ATTACK = 23,
  127. { 16, 32}, // T4_SAIL = 24,
  128. { 13, 20}, // T4_DISEMBARK = 25,
  129. { 8, 9}, // T4_EXCHANGE = 26,
  130. { 14, 16}, // T4_VISIT = 27,
  131. { 16, 32}, // T1_SAIL_VISIT = 28,
  132. { 16, 32}, // T2_SAIL_VISIT = 29,
  133. { 16, 32}, // T3_SAIL_VISIT = 30,
  134. { 16, 32}, // T4_SAIL_VISIT = 31,
  135. { 6, 1}, // SCROLL_NORTH = 32,
  136. { 16, 2}, // SCROLL_NORTHEAST = 33,
  137. { 21, 6}, // SCROLL_EAST = 34,
  138. { 16, 16}, // SCROLL_SOUTHEAST = 35,
  139. { 6, 21}, // SCROLL_SOUTH = 36,
  140. { 1, 16}, // SCROLL_SOUTHWEST = 37,
  141. { 1, 5}, // SCROLL_WEST = 38,
  142. { 2, 1}, // SCROLL_NORTHWEST = 39,
  143. { 0, 0}, // POINTER_COPY = 40,
  144. { 14, 16}, // TELEPORT = 41,
  145. { 20, 20}, // SCUTTLE_BOAT = 42
  146. }};
  147. assert(offsets.size() == size_t(Cursor::Map::COUNT)); //Invalid number of pivot offsets for cursor
  148. assert(index < offsets.size());
  149. return offsets[index] * ENGINE->screenHandler().getScalingFactor();
  150. }
  151. Point CursorHandler::getPivotOffsetCombat(size_t index)
  152. {
  153. static const std::array<Point, 20> offsets = {{
  154. { 12, 12 }, // BLOCKED = 0,
  155. { 10, 14 }, // MOVE = 1,
  156. { 14, 14 }, // FLY = 2,
  157. { 12, 12 }, // SHOOT = 3,
  158. { 12, 12 }, // HERO = 4,
  159. { 8, 12 }, // QUERY = 5,
  160. { 0, 0 }, // POINTER = 6,
  161. { 21, 0 }, // HIT_NORTHEAST = 7,
  162. { 31, 5 }, // HIT_EAST = 8,
  163. { 21, 21 }, // HIT_SOUTHEAST = 9,
  164. { 0, 21 }, // HIT_SOUTHWEST = 10,
  165. { 0, 5 }, // HIT_WEST = 11,
  166. { 0, 0 }, // HIT_NORTHWEST = 12,
  167. { 6, 0 }, // HIT_NORTH = 13,
  168. { 6, 31 }, // HIT_SOUTH = 14,
  169. { 14, 0 }, // SHOOT_PENALTY = 15,
  170. { 12, 12 }, // SHOOT_CATAPULT = 16,
  171. { 12, 12 }, // HEAL = 17,
  172. { 12, 12 }, // SACRIFICE = 18,
  173. { 14, 20 }, // TELEPORT = 19
  174. }};
  175. assert(offsets.size() == size_t(Cursor::Combat::COUNT)); //Invalid number of pivot offsets for cursor
  176. assert(index < offsets.size());
  177. return offsets[index] * ENGINE->screenHandler().getScalingFactor();
  178. }
  179. Point CursorHandler::getPivotOffsetSpellcast()
  180. {
  181. return Point(18, 28) * ENGINE->screenHandler().getScalingFactor();
  182. }
  183. Point CursorHandler::getPivotOffset()
  184. {
  185. if (dndObject)
  186. return dndObject->dimensions() / 2;
  187. switch (type) {
  188. case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame);
  189. case Cursor::Type::COMBAT: return getPivotOffsetCombat(frame);
  190. case Cursor::Type::DEFAULT: return getPivotOffsetDefault(frame);
  191. case Cursor::Type::SPELLBOOK: return getPivotOffsetSpellcast();
  192. };
  193. assert(0);
  194. return {0, 0};
  195. }
  196. std::shared_ptr<IImage> CursorHandler::getCurrentImage()
  197. {
  198. if (dndObject)
  199. return dndObject;
  200. return cursors[static_cast<size_t>(type)]->getImage(frame);
  201. }
  202. void CursorHandler::updateSpellcastCursor()
  203. {
  204. static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
  205. frameTime += ENGINE->framerate().getElapsedMilliseconds() / 1000.f;
  206. size_t newFrame = frame;
  207. while (frameTime >= frameDisplayDuration)
  208. {
  209. frameTime -= frameDisplayDuration;
  210. newFrame++;
  211. }
  212. auto & animation = cursors.at(static_cast<size_t>(type));
  213. while (newFrame >= animation->size())
  214. newFrame -= animation->size();
  215. changeGraphic(Cursor::Type::SPELLBOOK, newFrame);
  216. }
  217. void CursorHandler::render()
  218. {
  219. cursor->render();
  220. }
  221. void CursorHandler::update()
  222. {
  223. if(!showing)
  224. return;
  225. if (type == Cursor::Type::SPELLBOOK)
  226. updateSpellcastCursor();
  227. cursor->update();
  228. }
  229. void CursorHandler::hide()
  230. {
  231. if (!showing)
  232. return;
  233. showing = false;
  234. cursor->setVisible(false);
  235. }
  236. void CursorHandler::show()
  237. {
  238. if (showing)
  239. return;
  240. showing = true;
  241. cursor->setVisible(true);
  242. }
  243. Cursor::ShowType CursorHandler::getShowType() const
  244. {
  245. return showType;
  246. }
  247. void CursorHandler::changeCursor(Cursor::ShowType newShowType)
  248. {
  249. if(newShowType == showType)
  250. return;
  251. switch(newShowType)
  252. {
  253. case Cursor::ShowType::SOFTWARE:
  254. cursor.reset(new CursorSoftware());
  255. showType = Cursor::ShowType::SOFTWARE;
  256. cursor->setImage(getCurrentImage(), getPivotOffset());
  257. break;
  258. case Cursor::ShowType::HARDWARE:
  259. cursor.reset(new CursorHardware());
  260. showType = Cursor::ShowType::HARDWARE;
  261. cursor->setImage(getCurrentImage(), getPivotOffset());
  262. break;
  263. }
  264. }
  265. void CursorHandler::onScreenResize()
  266. {
  267. cursor->setImage(getCurrentImage(), getPivotOffset());
  268. }