CursorHandler.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  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 <SDL.h>
  13. #include "SDL_Extensions.h"
  14. #include "CGuiHandler.h"
  15. #include "CAnimation.h"
  16. #include "../CMT.h"
  17. CursorHandler::CursorHandler()
  18. : cursorSW(new CursorSoftware())
  19. , frameTime(0.f)
  20. , showing(false)
  21. , pos(0,0)
  22. {
  23. type = Cursor::Type::DEFAULT;
  24. dndObject = nullptr;
  25. cursors =
  26. {
  27. std::make_unique<CAnimation>("CRADVNTR"),
  28. std::make_unique<CAnimation>("CRCOMBAT"),
  29. std::make_unique<CAnimation>("CRDEFLT"),
  30. std::make_unique<CAnimation>("CRSPELL")
  31. };
  32. for (auto & cursor : cursors)
  33. cursor->preload();
  34. set(Cursor::Map::POINTER);
  35. }
  36. Point CursorHandler::position() const
  37. {
  38. return pos;
  39. }
  40. void CursorHandler::changeGraphic(Cursor::Type type, size_t index)
  41. {
  42. assert(dndObject == nullptr);
  43. this->type = type;
  44. this->frame = index;
  45. cursorSW->setImage(getCurrentImage(), getPivotOffset());
  46. }
  47. void CursorHandler::set(Cursor::Default index)
  48. {
  49. changeGraphic(Cursor::Type::DEFAULT, static_cast<size_t>(index));
  50. }
  51. void CursorHandler::set(Cursor::Map index)
  52. {
  53. changeGraphic(Cursor::Type::ADVENTURE, static_cast<size_t>(index));
  54. }
  55. void CursorHandler::set(Cursor::Combat index)
  56. {
  57. changeGraphic(Cursor::Type::COMBAT, static_cast<size_t>(index));
  58. }
  59. void CursorHandler::set(Cursor::Spellcast index)
  60. {
  61. //Note: this is animated cursor, ignore specified frame and only change type
  62. changeGraphic(Cursor::Type::SPELLBOOK, frame);
  63. }
  64. void CursorHandler::dragAndDropCursor(std::shared_ptr<IImage> image)
  65. {
  66. dndObject = image;
  67. cursorSW->setImage(getCurrentImage(), getPivotOffset());
  68. }
  69. void CursorHandler::dragAndDropCursor (std::string path, size_t index)
  70. {
  71. CAnimation anim(path);
  72. anim.load(index);
  73. dragAndDropCursor(anim.getImage(index));
  74. }
  75. void CursorHandler::cursorMove(const int & x, const int & y)
  76. {
  77. pos.x = x;
  78. pos.y = y;
  79. cursorSW->setCursorPosition(pos);
  80. }
  81. Point CursorHandler::getPivotOffsetDefault(size_t index)
  82. {
  83. return {0, 0};
  84. }
  85. Point CursorHandler::getPivotOffsetMap(size_t index)
  86. {
  87. static const std::array<Point, 43> offsets = {{
  88. { 0, 0}, // POINTER = 0,
  89. { 0, 0}, // HOURGLASS = 1,
  90. { 12, 10}, // HERO = 2,
  91. { 12, 12}, // TOWN = 3,
  92. { 15, 13}, // T1_MOVE = 4,
  93. { 13, 13}, // T1_ATTACK = 5,
  94. { 20, 20}, // T1_SAIL = 6,
  95. { 13, 16}, // T1_DISEMBARK = 7,
  96. { 8, 9}, // T1_EXCHANGE = 8,
  97. { 14, 16}, // T1_VISIT = 9,
  98. { 15, 13}, // T2_MOVE = 10,
  99. { 13, 13}, // T2_ATTACK = 11,
  100. { 20, 20}, // T2_SAIL = 12,
  101. { 13, 16}, // T2_DISEMBARK = 13,
  102. { 8, 9}, // T2_EXCHANGE = 14,
  103. { 14, 16}, // T2_VISIT = 15,
  104. { 15, 13}, // T3_MOVE = 16,
  105. { 13, 13}, // T3_ATTACK = 17,
  106. { 20, 20}, // T3_SAIL = 18,
  107. { 13, 16}, // T3_DISEMBARK = 19,
  108. { 8, 9}, // T3_EXCHANGE = 20,
  109. { 14, 16}, // T3_VISIT = 21,
  110. { 15, 13}, // T4_MOVE = 22,
  111. { 13, 13}, // T4_ATTACK = 23,
  112. { 20, 20}, // T4_SAIL = 24,
  113. { 13, 16}, // T4_DISEMBARK = 25,
  114. { 8, 9}, // T4_EXCHANGE = 26,
  115. { 14, 16}, // T4_VISIT = 27,
  116. { 20, 20}, // T1_SAIL_VISIT = 28,
  117. { 20, 20}, // T2_SAIL_VISIT = 29,
  118. { 20, 20}, // T3_SAIL_VISIT = 30,
  119. { 20, 20}, // T4_SAIL_VISIT = 31,
  120. { 6, 1}, // SCROLL_NORTH = 32,
  121. { 16, 2}, // SCROLL_NORTHEAST = 33,
  122. { 21, 6}, // SCROLL_EAST = 34,
  123. { 16, 16}, // SCROLL_SOUTHEAST = 35,
  124. { 6, 21}, // SCROLL_SOUTH = 36,
  125. { 1, 16}, // SCROLL_SOUTHWEST = 37,
  126. { 1, 5}, // SCROLL_WEST = 38,
  127. { 2, 1}, // SCROLL_NORTHWEST = 39,
  128. { 0, 0}, // POINTER_COPY = 40,
  129. { 14, 16}, // TELEPORT = 41,
  130. { 20, 20}, // SCUTTLE_BOAT = 42
  131. }};
  132. static_assert (offsets.size() == size_t(Cursor::Map::COUNT), "Invalid number of pivot offsets for cursor" );
  133. assert(index < offsets.size());
  134. return offsets[index];
  135. }
  136. Point CursorHandler::getPivotOffsetCombat(size_t index)
  137. {
  138. static const std::array<Point, 20> offsets = {{
  139. { 12, 12 }, // BLOCKED = 0,
  140. { 10, 14 }, // MOVE = 1,
  141. { 14, 14 }, // FLY = 2,
  142. { 12, 12 }, // SHOOT = 3,
  143. { 12, 12 }, // HERO = 4,
  144. { 8, 12 }, // QUERY = 5,
  145. { 0, 0 }, // POINTER = 6,
  146. { 21, 0 }, // HIT_NORTHEAST = 7,
  147. { 31, 5 }, // HIT_EAST = 8,
  148. { 21, 21 }, // HIT_SOUTHEAST = 9,
  149. { 0, 21 }, // HIT_SOUTHWEST = 10,
  150. { 0, 5 }, // HIT_WEST = 11,
  151. { 0, 0 }, // HIT_NORTHWEST = 12,
  152. { 6, 0 }, // HIT_NORTH = 13,
  153. { 6, 31 }, // HIT_SOUTH = 14,
  154. { 14, 0 }, // SHOOT_PENALTY = 15,
  155. { 12, 12 }, // SHOOT_CATAPULT = 16,
  156. { 12, 12 }, // HEAL = 17,
  157. { 12, 12 }, // SACRIFICE = 18,
  158. { 14, 20 }, // TELEPORT = 19
  159. }};
  160. static_assert (offsets.size() == size_t(Cursor::Combat::COUNT), "Invalid number of pivot offsets for cursor" );
  161. assert(index < offsets.size());
  162. return offsets[index];
  163. }
  164. Point CursorHandler::getPivotOffsetSpellcast()
  165. {
  166. return { 18, 28};
  167. }
  168. Point CursorHandler::getPivotOffset()
  169. {
  170. if (dndObject)
  171. return dndObject->dimensions();
  172. switch (type) {
  173. case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame);
  174. case Cursor::Type::COMBAT: return getPivotOffsetCombat(frame);
  175. case Cursor::Type::DEFAULT: return getPivotOffsetDefault(frame);
  176. case Cursor::Type::SPELLBOOK: return getPivotOffsetSpellcast();
  177. };
  178. assert(0);
  179. return {0, 0};
  180. }
  181. std::shared_ptr<IImage> CursorHandler::getCurrentImage()
  182. {
  183. if (dndObject)
  184. return dndObject;
  185. return cursors[static_cast<size_t>(type)]->getImage(frame);
  186. }
  187. void CursorHandler::centerCursor()
  188. {
  189. Point screenSize {screen->w, screen->h};
  190. pos = screenSize / 2 - getPivotOffset();
  191. SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
  192. SDL_WarpMouse(pos.x, pos.y);
  193. SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
  194. cursorSW->setCursorPosition(pos);
  195. }
  196. void CursorHandler::updateSpellcastCursor()
  197. {
  198. static const float frameDisplayDuration = 0.1f;
  199. frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
  200. size_t newFrame = frame;
  201. while (frameTime >= frameDisplayDuration)
  202. {
  203. frameTime -= frameDisplayDuration;
  204. newFrame++;
  205. }
  206. auto & animation = cursors.at(static_cast<size_t>(type));
  207. while (newFrame >= animation->size())
  208. newFrame -= animation->size();
  209. changeGraphic(Cursor::Type::SPELLBOOK, newFrame);
  210. }
  211. void CursorHandler::render()
  212. {
  213. if(!showing)
  214. return;
  215. if (type == Cursor::Type::SPELLBOOK)
  216. updateSpellcastCursor();
  217. cursorSW->render();
  218. }
  219. void CursorSoftware::render()
  220. {
  221. //texture must be updated in the main (renderer) thread, but changes to cursor type may come from other threads
  222. if (needUpdate)
  223. updateTexture();
  224. Point renderPos = pos - pivot;
  225. SDL_Rect destRect;
  226. destRect.x = renderPos.x;
  227. destRect.y = renderPos.y;
  228. destRect.w = 40;
  229. destRect.h = 40;
  230. SDL_RenderCopy(mainRenderer, cursorTexture, nullptr, &destRect);
  231. }
  232. void CursorSoftware::createTexture(const Point & dimensions)
  233. {
  234. if(cursorTexture)
  235. SDL_DestroyTexture(cursorTexture);
  236. if (cursorSurface)
  237. SDL_FreeSurface(cursorSurface);
  238. cursorSurface = CSDL_Ext::newSurface(dimensions.x, dimensions.y);
  239. cursorTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, dimensions.x, dimensions.y);
  240. SDL_SetSurfaceBlendMode(cursorSurface, SDL_BLENDMODE_NONE);
  241. SDL_SetTextureBlendMode(cursorTexture, SDL_BLENDMODE_BLEND);
  242. }
  243. void CursorSoftware::updateTexture()
  244. {
  245. Point dimensions(-1, -1);
  246. if (!cursorSurface || Point(cursorSurface->w, cursorSurface->h) != cursorImage->dimensions())
  247. createTexture(cursorImage->dimensions());
  248. Uint32 fillColor = SDL_MapRGBA(cursorSurface->format, 0, 0, 0, 0);
  249. CSDL_Ext::fillRect(cursorSurface, nullptr, fillColor);
  250. cursorImage->draw(cursorSurface);
  251. SDL_UpdateTexture(cursorTexture, NULL, cursorSurface->pixels, cursorSurface->pitch);
  252. needUpdate = false;
  253. }
  254. void CursorSoftware::setImage(std::shared_ptr<IImage> image, const Point & pivotOffset)
  255. {
  256. assert(image != nullptr);
  257. cursorImage = image;
  258. pivot = pivotOffset;
  259. needUpdate = true;
  260. }
  261. void CursorSoftware::setCursorPosition( const Point & newPos )
  262. {
  263. pos = newPos;
  264. }
  265. CursorSoftware::CursorSoftware():
  266. cursorTexture(nullptr),
  267. cursorSurface(nullptr),
  268. needUpdate(false),
  269. pivot(0,0)
  270. {
  271. SDL_ShowCursor(SDL_DISABLE);
  272. }
  273. CursorSoftware::~CursorSoftware()
  274. {
  275. if(cursorTexture)
  276. SDL_DestroyTexture(cursorTexture);
  277. if (cursorSurface)
  278. SDL_FreeSurface(cursorSurface);
  279. }