CCursorHandler.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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 "CCursorHandler.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 CCursorHandler::clearBuffer()
  18. {
  19. Uint32 fillColor = SDL_MapRGBA(buffer->format, 0, 0, 0, 0);
  20. CSDL_Ext::fillRect(buffer, nullptr, fillColor);
  21. }
  22. void CCursorHandler::updateBuffer(CIntObject * payload)
  23. {
  24. payload->moveTo(Point(0,0));
  25. payload->showAll(buffer);
  26. needUpdate = true;
  27. }
  28. void CCursorHandler::replaceBuffer(CIntObject * payload)
  29. {
  30. clearBuffer();
  31. updateBuffer(payload);
  32. }
  33. CCursorHandler::CCursorHandler()
  34. : needUpdate(true)
  35. , buffer(nullptr)
  36. , cursorLayer(nullptr)
  37. , frameTime(0.f)
  38. , showing(false)
  39. {
  40. cursorLayer = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 40, 40);
  41. SDL_SetTextureBlendMode(cursorLayer, SDL_BLENDMODE_BLEND);
  42. xpos = ypos = 0;
  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 CCursorHandler::position() const
  59. {
  60. return Point(xpos, ypos);
  61. }
  62. void CCursorHandler::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 CCursorHandler::set(Cursor::Default index)
  80. {
  81. changeGraphic(Cursor::Type::DEFAULT, static_cast<size_t>(index));
  82. }
  83. void CCursorHandler::set(Cursor::Map index)
  84. {
  85. changeGraphic(Cursor::Type::ADVENTURE, static_cast<size_t>(index));
  86. }
  87. void CCursorHandler::set(Cursor::Combat index)
  88. {
  89. changeGraphic(Cursor::Type::COMBAT, static_cast<size_t>(index));
  90. }
  91. void CCursorHandler::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 CCursorHandler::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 CCursorHandler::cursorMove(const int & x, const int & y)
  105. {
  106. xpos = x;
  107. ypos = y;
  108. }
  109. void CCursorHandler::shiftPos( int &x, int &y )
  110. {
  111. if(( type == Cursor::Type::COMBAT && frame != static_cast<size_t>(Cursor::Combat::POINTER)) || type == Cursor::Type::SPELLBOOK)
  112. {
  113. x-=16;
  114. y-=16;
  115. // Properly align the melee attack cursors.
  116. if (type == Cursor::Type::COMBAT)
  117. {
  118. switch (static_cast<Cursor::Combat>(frame))
  119. {
  120. case Cursor::Combat::HIT_NORTHEAST:
  121. x -= 6;
  122. y += 16;
  123. break;
  124. case Cursor::Combat::HIT_EAST:
  125. x -= 16;
  126. y += 10;
  127. break;
  128. case Cursor::Combat::HIT_SOUTHEAST:
  129. x -= 6;
  130. y -= 6;
  131. break;
  132. case Cursor::Combat::HIT_SOUTHWEST:
  133. x += 16;
  134. y -= 6;
  135. break;
  136. case Cursor::Combat::HIT_WEST:
  137. x += 16;
  138. y += 11;
  139. break;
  140. case Cursor::Combat::HIT_NORTHWEST:
  141. x += 16;
  142. y += 16;
  143. break;
  144. case Cursor::Combat::HIT_NORTH:
  145. x += 9;
  146. y += 16;
  147. break;
  148. case Cursor::Combat::HIT_SOUTH:
  149. x += 9;
  150. y -= 15;
  151. break;
  152. }
  153. }
  154. }
  155. else if(type == Cursor::Type::ADVENTURE)
  156. {
  157. if (frame == 0)
  158. {
  159. //no-op
  160. }
  161. else if(frame == 2)
  162. {
  163. x -= 12;
  164. y -= 10;
  165. }
  166. else if(frame == 3)
  167. {
  168. x -= 12;
  169. y -= 12;
  170. }
  171. else if(frame < 27)
  172. {
  173. int hlpNum = (frame - 4)%6;
  174. if(hlpNum == 0)
  175. {
  176. x -= 15;
  177. y -= 13;
  178. }
  179. else if(hlpNum == 1)
  180. {
  181. x -= 13;
  182. y -= 13;
  183. }
  184. else if(hlpNum == 2)
  185. {
  186. x -= 20;
  187. y -= 20;
  188. }
  189. else if(hlpNum == 3)
  190. {
  191. x -= 13;
  192. y -= 16;
  193. }
  194. else if(hlpNum == 4)
  195. {
  196. x -= 8;
  197. y -= 9;
  198. }
  199. else if(hlpNum == 5)
  200. {
  201. x -= 14;
  202. y -= 16;
  203. }
  204. }
  205. else if(frame == 41)
  206. {
  207. x -= 14;
  208. y -= 16;
  209. }
  210. else if(frame < 31 || frame == 42)
  211. {
  212. x -= 20;
  213. y -= 20;
  214. }
  215. }
  216. }
  217. void CCursorHandler::centerCursor()
  218. {
  219. this->xpos = static_cast<int>((screen->w / 2.) - (currentCursor->pos.w / 2.));
  220. this->ypos = static_cast<int>((screen->h / 2.) - (currentCursor->pos.h / 2.));
  221. SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
  222. SDL_WarpMouse(this->xpos, this->ypos);
  223. SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
  224. }
  225. void CCursorHandler::render()
  226. {
  227. if(!showing)
  228. return;
  229. if (type == Cursor::Type::SPELLBOOK)
  230. {
  231. static const float frameDisplayDuration = 0.1f;
  232. frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
  233. size_t newFrame = frame;
  234. while (frameTime > frameDisplayDuration)
  235. {
  236. frameTime -= frameDisplayDuration;
  237. newFrame++;
  238. }
  239. auto & animation = cursors.at(static_cast<size_t>(type));
  240. while (newFrame > animation->size())
  241. newFrame -= animation->size();
  242. changeGraphic(Cursor::Type::SPELLBOOK, newFrame);
  243. }
  244. //the must update texture in the main (renderer) thread, but changes to cursor type may come from other threads
  245. updateTexture();
  246. int x = xpos;
  247. int y = ypos;
  248. shiftPos(x, y);
  249. if(dndObject)
  250. {
  251. x -= dndObject->pos.w/2;
  252. y -= dndObject->pos.h/2;
  253. }
  254. SDL_Rect destRect;
  255. destRect.x = x;
  256. destRect.y = y;
  257. destRect.w = 40;
  258. destRect.h = 40;
  259. SDL_RenderCopy(mainRenderer, cursorLayer, nullptr, &destRect);
  260. }
  261. void CCursorHandler::updateTexture()
  262. {
  263. if(needUpdate)
  264. {
  265. SDL_UpdateTexture(cursorLayer, nullptr, buffer->pixels, buffer->pitch);
  266. needUpdate = false;
  267. }
  268. }
  269. CCursorHandler::~CCursorHandler()
  270. {
  271. if(buffer)
  272. SDL_FreeSurface(buffer);
  273. if(cursorLayer)
  274. SDL_DestroyTexture(cursorLayer);
  275. }