CTerrainRect.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /*
  2. * CTerrainRect.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 "CAdvmapInterface.h"
  12. #include "CCastleInterface.h"
  13. #include "CHeroWindow.h"
  14. #include "CKingdomInterface.h"
  15. #include "CSpellWindow.h"
  16. #include "CTradeWindow.h"
  17. #include "GUIClasses.h"
  18. #include "InfoWindows.h"
  19. #include "../CBitmapHandler.h"
  20. #include "../CGameInfo.h"
  21. #include "../CMessage.h"
  22. #include "../CMusicHandler.h"
  23. #include "../CPlayerInterface.h"
  24. #include "../mainmenu/CMainMenu.h"
  25. #include "../lobby/CSelectionBase.h"
  26. #include "../lobby/CCampaignInfoScreen.h"
  27. #include "../lobby/CSavingScreen.h"
  28. #include "../lobby/CScenarioInfoScreen.h"
  29. #include "../Graphics.h"
  30. #include "../mapHandler.h"
  31. #include "../gui/CAnimation.h"
  32. #include "../gui/CursorHandler.h"
  33. #include "../gui/CGuiHandler.h"
  34. #include "../gui/SDL_Extensions.h"
  35. #include "../widgets/MiscWidgets.h"
  36. #include "../../CCallback.h"
  37. #include "../../lib/CConfigHandler.h"
  38. #include "../../lib/CGameState.h"
  39. #include "../../lib/CGeneralTextHandler.h"
  40. #include "../../lib/CHeroHandler.h"
  41. #include "../../lib/CSoundBase.h"
  42. #include "../../lib/spells/CSpellHandler.h"
  43. #include "../../lib/CTownHandler.h"
  44. #include "../../lib/JsonNode.h"
  45. #include "../../lib/mapObjects/CGHeroInstance.h"
  46. #include "../../lib/mapping/CMap.h"
  47. #include "../../lib/UnlockGuard.h"
  48. #include "../../lib/VCMI_Lib.h"
  49. #include "../../lib/StartInfo.h"
  50. #include "../../lib/mapping/CMapInfo.h"
  51. #include "../../lib/TerrainHandler.h"
  52. #include <SDL_surface.h>
  53. #include <SDL_events.h>
  54. #define ADVOPT (conf.go()->ac)
  55. using namespace CSDL_Ext;
  56. std::shared_ptr<CAdvMapInt> adventureInt;
  57. static void setScrollingCursor(ui8 direction)
  58. {
  59. if(direction & CAdvMapInt::RIGHT)
  60. {
  61. if(direction & CAdvMapInt::UP)
  62. CCS->curh->set(Cursor::Map::SCROLL_NORTHEAST);
  63. else if(direction & CAdvMapInt::DOWN)
  64. CCS->curh->set(Cursor::Map::SCROLL_SOUTHEAST);
  65. else
  66. CCS->curh->set(Cursor::Map::SCROLL_EAST);
  67. }
  68. else if(direction & CAdvMapInt::LEFT)
  69. {
  70. if(direction & CAdvMapInt::UP)
  71. CCS->curh->set(Cursor::Map::SCROLL_NORTHWEST);
  72. else if(direction & CAdvMapInt::DOWN)
  73. CCS->curh->set(Cursor::Map::SCROLL_SOUTHWEST);
  74. else
  75. CCS->curh->set(Cursor::Map::SCROLL_WEST);
  76. }
  77. else if(direction & CAdvMapInt::UP)
  78. CCS->curh->set(Cursor::Map::SCROLL_NORTH);
  79. else if(direction & CAdvMapInt::DOWN)
  80. CCS->curh->set(Cursor::Map::SCROLL_SOUTH);
  81. }
  82. CTerrainRect::CTerrainRect()
  83. : fadeSurface(nullptr),
  84. lastRedrawStatus(EMapAnimRedrawStatus::OK),
  85. fadeAnim(std::make_shared<CFadeAnimation>()),
  86. curHoveredTile(-1,-1,-1),
  87. currentPath(nullptr)
  88. {
  89. tilesw=(ADVOPT.advmapW+31)/32;
  90. tilesh=(ADVOPT.advmapH+31)/32;
  91. pos.x=ADVOPT.advmapX;
  92. pos.y=ADVOPT.advmapY;
  93. pos.w=ADVOPT.advmapW;
  94. pos.h=ADVOPT.advmapH;
  95. moveX = moveY = 0;
  96. addUsedEvents(LCLICK | RCLICK | MCLICK | HOVER | MOVE);
  97. }
  98. CTerrainRect::~CTerrainRect()
  99. {
  100. if(fadeSurface)
  101. SDL_FreeSurface(fadeSurface);
  102. }
  103. void CTerrainRect::deactivate()
  104. {
  105. CIntObject::deactivate();
  106. curHoveredTile = int3(-1,-1,-1); //we lost info about hovered tile when disabling
  107. }
  108. void CTerrainRect::clickLeft(tribool down, bool previousState)
  109. {
  110. if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
  111. return;
  112. if(indeterminate(down))
  113. return;
  114. #if defined(VCMI_ANDROID) || defined(VCMI_IOS)
  115. if(adventureInt->swipeEnabled)
  116. {
  117. if(handleSwipeStateChange((bool)down == true))
  118. {
  119. return; // if swipe is enabled, we don't process "down" events and wait for "up" (to make sure this wasn't a swiping gesture)
  120. }
  121. }
  122. else
  123. {
  124. #endif
  125. if(down == false)
  126. return;
  127. #if defined(VCMI_ANDROID) || defined(VCMI_IOS)
  128. }
  129. #endif
  130. int3 mp = whichTileIsIt();
  131. if(mp.x < 0 || mp.y < 0 || mp.x >= LOCPLINT->cb->getMapSize().x || mp.y >= LOCPLINT->cb->getMapSize().y)
  132. return;
  133. adventureInt->tileLClicked(mp);
  134. }
  135. void CTerrainRect::clickRight(tribool down, bool previousState)
  136. {
  137. #if defined(VCMI_ANDROID) || defined(VCMI_IOS)
  138. if(adventureInt->swipeEnabled && isSwiping)
  139. return;
  140. #endif
  141. if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
  142. return;
  143. int3 mp = whichTileIsIt();
  144. if(CGI->mh->map->isInTheMap(mp) && down)
  145. adventureInt->tileRClicked(mp);
  146. }
  147. void CTerrainRect::clickMiddle(tribool down, bool previousState)
  148. {
  149. handleSwipeStateChange((bool)down == true);
  150. }
  151. void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent)
  152. {
  153. handleHover(sEvent);
  154. if(!adventureInt->swipeEnabled)
  155. return;
  156. handleSwipeMove(sEvent);
  157. }
  158. void CTerrainRect::handleSwipeMove(const SDL_MouseMotionEvent & sEvent)
  159. {
  160. #if defined(VCMI_ANDROID) || defined(VCMI_IOS)
  161. if(sEvent.state == 0 || GH.multifinger) // any "button" is enough on mobile
  162. #else
  163. if((sEvent.state & SDL_BUTTON_MMASK) == 0) // swipe only works with middle mouse on other platforms
  164. #endif
  165. {
  166. return;
  167. }
  168. if(!isSwiping)
  169. {
  170. // try to distinguish if this touch was meant to be a swipe or just fat-fingering press
  171. if(abs(sEvent.x - swipeInitialRealPos.x) > SwipeTouchSlop ||
  172. abs(sEvent.y - swipeInitialRealPos.y) > SwipeTouchSlop)
  173. {
  174. isSwiping = true;
  175. }
  176. }
  177. if(isSwiping)
  178. {
  179. adventureInt->swipeTargetPosition.x =
  180. swipeInitialMapPos.x + static_cast<si32>(swipeInitialRealPos.x - sEvent.x) / 32;
  181. adventureInt->swipeTargetPosition.y =
  182. swipeInitialMapPos.y + static_cast<si32>(swipeInitialRealPos.y - sEvent.y) / 32;
  183. adventureInt->swipeMovementRequested = true;
  184. }
  185. }
  186. bool CTerrainRect::handleSwipeStateChange(bool btnPressed)
  187. {
  188. if(btnPressed)
  189. {
  190. swipeInitialRealPos = int3(GH.getCursorPosition().x, GH.getCursorPosition().y, 0);
  191. swipeInitialMapPos = int3(adventureInt->position);
  192. return true;
  193. }
  194. else if(isSwiping) // only accept this touch if it wasn't a swipe
  195. {
  196. isSwiping = false;
  197. return true;
  198. }
  199. return false;
  200. }
  201. void CTerrainRect::handleHover(const SDL_MouseMotionEvent &sEvent)
  202. {
  203. int3 tHovered = whichTileIsIt(sEvent.x, sEvent.y);
  204. int3 pom = adventureInt->verifyPos(tHovered);
  205. if(tHovered != pom) //tile outside the map
  206. {
  207. CCS->curh->set(Cursor::Map::POINTER);
  208. return;
  209. }
  210. if (pom != curHoveredTile)
  211. {
  212. curHoveredTile = pom;
  213. adventureInt->tileHovered(pom);
  214. }
  215. }
  216. void CTerrainRect::hover(bool on)
  217. {
  218. if (!on)
  219. {
  220. adventureInt->statusbar->clear();
  221. CCS->curh->set(Cursor::Map::POINTER);
  222. }
  223. //Hoverable::hover(on);
  224. }
  225. void CTerrainRect::showPath(const Rect & extRect, SDL_Surface * to)
  226. {
  227. const static int pns[9][9] = {
  228. {16, 17, 18, 7, -1, 19, 6, 5, -1},
  229. { 8, 9, 18, 7, -1, 19, 6, -1, 20},
  230. { 8, 1, 10, 7, -1, 19, -1, 21, 20},
  231. {24, 17, 18, 15, -1, -1, 6, 5, 4},
  232. {-1, -1, -1, -1, -1, -1, -1, -1, -1},
  233. { 8, 1, 2, -1, -1, 11, 22, 21, 20},
  234. {24, 17, -1, 23, -1, 3, 14, 5, 4},
  235. {24, -1, 2, 23, -1, 3, 22, 13, 4},
  236. {-1, 1, 2, 23, -1, 3, 22, 21, 12}
  237. }; //table of magic values TODO meaning, change variable name
  238. for (int i = 0; i < -1 + (int)currentPath->nodes.size(); ++i)
  239. {
  240. const int3 &curPos = currentPath->nodes[i].coord, &nextPos = currentPath->nodes[i+1].coord;
  241. if(curPos.z != adventureInt->position.z)
  242. continue;
  243. int pn=-1;//number of picture
  244. if (i==0) //last tile
  245. {
  246. int x = 32*(curPos.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x,
  247. y = 32*(curPos.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y;
  248. if (x<0 || y<0 || x>pos.w || y>pos.h)
  249. continue;
  250. pn=0;
  251. }
  252. else
  253. {
  254. const int3 &prevPos = currentPath->nodes[i-1].coord;
  255. std::vector<CGPathNode> & cv = currentPath->nodes;
  256. /* Vector directions
  257. * 0 1 2
  258. * \ | /
  259. * 3 - 4 - 5
  260. * / | \
  261. * 6 7 8
  262. *For example:
  263. * |
  264. * |__\
  265. * /
  266. * is id1=7, id2=5 (pns[7][5])
  267. */
  268. bool pathContinuous = curPos.areNeighbours(nextPos) && curPos.areNeighbours(prevPos);
  269. if(pathContinuous && cv[i].action != CGPathNode::EMBARK && cv[i].action != CGPathNode::DISEMBARK)
  270. {
  271. int id1=(curPos.x-nextPos.x+1)+3*(curPos.y-nextPos.y+1); //Direction of entering vector
  272. int id2=(cv[i-1].coord.x-curPos.x+1)+3*(cv[i-1].coord.y-curPos.y+1); //Direction of exiting vector
  273. pn=pns[id1][id2];
  274. }
  275. else //path discontinuity or sea/land transition (eg. when moving through Subterranean Gate or Boat)
  276. {
  277. pn = 0;
  278. }
  279. }
  280. if (currentPath->nodes[i].turns)
  281. pn+=25;
  282. if (pn>=0)
  283. {
  284. const auto arrow = graphics->heroMoveArrows->getImage(pn);
  285. int x = 32*(curPos.x-adventureInt->position.x)+CGI->mh->offsetX + pos.x,
  286. y = 32*(curPos.y-adventureInt->position.y)+CGI->mh->offsetY + pos.y;
  287. if (x< -32 || y< -32 || x>pos.w || y>pos.h)
  288. continue;
  289. int hvx = (x + arrow->width()) - (pos.x + pos.w),
  290. hvy = (y + arrow->height()) - (pos.y + pos.h);
  291. Rect prevClip;
  292. CSDL_Ext::getClipRect(to, prevClip);
  293. CSDL_Ext::setClipRect(to, extRect); //preventing blitting outside of that rect
  294. if(ADVOPT.smoothMove) //version for smooth hero move, with pos shifts
  295. {
  296. if (hvx<0 && hvy<0)
  297. {
  298. arrow->draw(to, x + moveX, y + moveY);
  299. }
  300. else if(hvx<0)
  301. {
  302. Rect srcRect = genRect(arrow->height() - hvy, arrow->width(), 0, 0);
  303. arrow->draw(to, x + moveX, y + moveY, &srcRect);
  304. }
  305. else if (hvy<0)
  306. {
  307. Rect srcRect = genRect(arrow->height(), arrow->width() - hvx, 0, 0);
  308. arrow->draw(to, x + moveX, y + moveY, &srcRect);
  309. }
  310. else
  311. {
  312. Rect srcRect = genRect(arrow->height() - hvy, arrow->width() - hvx, 0, 0);
  313. arrow->draw(to, x + moveX, y + moveY, &srcRect);
  314. }
  315. }
  316. else //standard version
  317. {
  318. if (hvx<0 && hvy<0)
  319. {
  320. arrow->draw(to, x, y);
  321. }
  322. else if(hvx<0)
  323. {
  324. Rect srcRect = genRect(arrow->height() - hvy, arrow->width(), 0, 0);
  325. arrow->draw(to, x, y, &srcRect);
  326. }
  327. else if (hvy<0)
  328. {
  329. Rect srcRect = genRect(arrow->height(), arrow->width() - hvx, 0, 0);
  330. arrow->draw(to, x, y, &srcRect);
  331. }
  332. else
  333. {
  334. Rect srcRect = genRect(arrow->height() - hvy, arrow->width() - hvx, 0, 0);
  335. arrow->draw(to, x, y, &srcRect);
  336. }
  337. }
  338. CSDL_Ext::setClipRect(to, prevClip);
  339. }
  340. } //for (int i=0;i<currentPath->nodes.size()-1;i++)
  341. }
  342. void CTerrainRect::show(SDL_Surface * to)
  343. {
  344. if (adventureInt->mode == EAdvMapMode::NORMAL)
  345. {
  346. MapDrawingInfo info(adventureInt->position, LOCPLINT->cb->getVisibilityMap(), pos);
  347. info.otherheroAnim = true;
  348. info.anim = adventureInt->anim;
  349. info.heroAnim = adventureInt->heroAnim;
  350. if (ADVOPT.smoothMove)
  351. info.movement = int3(moveX, moveY, 0);
  352. lastRedrawStatus = CGI->mh->drawTerrainRectNew(to, &info);
  353. if (fadeAnim->isFading())
  354. {
  355. Rect r(pos);
  356. fadeAnim->update();
  357. fadeAnim->draw(to, r.topLeft());
  358. }
  359. if (currentPath/* && adventureInt->position.z==currentPath->startPos().z*/) //drawing path
  360. {
  361. showPath(pos, to);
  362. }
  363. }
  364. }
  365. void CTerrainRect::showAll(SDL_Surface * to)
  366. {
  367. // world view map is static and doesn't need redraw every frame
  368. if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
  369. {
  370. MapDrawingInfo info(adventureInt->position, LOCPLINT->cb->getVisibilityMap(), pos, adventureInt->worldViewIcons);
  371. info.scaled = true;
  372. info.scale = adventureInt->worldViewScale;
  373. adventureInt->worldViewOptions.adjustDrawingInfo(info);
  374. CGI->mh->drawTerrainRectNew(to, &info);
  375. }
  376. }
  377. void CTerrainRect::showAnim(SDL_Surface * to)
  378. {
  379. if (fadeAnim->isFading())
  380. show(to);
  381. else if (lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED)
  382. show(to); // currently the same; maybe we should pass some flag to map handler so it redraws ONLY tiles that need redraw instead of full
  383. }
  384. int3 CTerrainRect::whichTileIsIt(const int x, const int y)
  385. {
  386. int3 ret;
  387. ret.x = adventureInt->position.x + ((x-CGI->mh->offsetX-pos.x)/32);
  388. ret.y = adventureInt->position.y + ((y-CGI->mh->offsetY-pos.y)/32);
  389. ret.z = adventureInt->position.z;
  390. return ret;
  391. }
  392. int3 CTerrainRect::whichTileIsIt()
  393. {
  394. return whichTileIsIt(GH.getCursorPosition().x, GH.getCursorPosition().y);
  395. }
  396. int3 CTerrainRect::tileCountOnScreen()
  397. {
  398. switch (adventureInt->mode)
  399. {
  400. default:
  401. logGlobal->error("Unknown map mode %d", (int)adventureInt->mode);
  402. return int3();
  403. case EAdvMapMode::NORMAL:
  404. return int3(tilesw, tilesh, 1);
  405. case EAdvMapMode::WORLD_VIEW:
  406. return int3((si32)(tilesw / adventureInt->worldViewScale), (si32)(tilesh / adventureInt->worldViewScale), 1);
  407. }
  408. }
  409. void CTerrainRect::fadeFromCurrentView()
  410. {
  411. if (!ADVOPT.screenFading)
  412. return;
  413. if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
  414. return;
  415. if (!fadeSurface)
  416. fadeSurface = CSDL_Ext::newSurface(pos.w, pos.h);
  417. CSDL_Ext::blitSurface(screen, fadeSurface, Point(0,0));
  418. fadeAnim->init(CFadeAnimation::EMode::OUT, fadeSurface);
  419. }
  420. bool CTerrainRect::needsAnimUpdate()
  421. {
  422. return fadeAnim->isFading() || lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED;
  423. }