CursorHandler.cpp 8.1 KB

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