CAdvmapInterface.cpp 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956
  1. /*
  2. * CAdvmapInterface.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 "GUIClasses.h"
  17. #include "CTradeWindow.h"
  18. #include "../CBitmapHandler.h"
  19. #include "../CGameInfo.h"
  20. #include "../CMessage.h"
  21. #include "../CMusicHandler.h"
  22. #include "../CPlayerInterface.h"
  23. #include "../CPreGame.h"
  24. #include "../Graphics.h"
  25. #include "../mapHandler.h"
  26. #include "../gui/CAnimation.h"
  27. #include "../gui/CCursorHandler.h"
  28. #include "../gui/CGuiHandler.h"
  29. #include "../gui/SDL_Extensions.h"
  30. #include "../widgets/MiscWidgets.h"
  31. #include "../windows/InfoWindows.h"
  32. #include "../../CCallback.h"
  33. #include "../../lib/CConfigHandler.h"
  34. #include "../../lib/CGameState.h"
  35. #include "../../lib/CGeneralTextHandler.h"
  36. #include "../../lib/CHeroHandler.h"
  37. #include "../../lib/CSoundBase.h"
  38. #include "../../lib/spells/CSpellHandler.h"
  39. #include "../../lib/CTownHandler.h"
  40. #include "../../lib/JsonNode.h"
  41. #include "../../lib/mapObjects/CGHeroInstance.h"
  42. #include "../../lib/mapping/CMap.h"
  43. #include "../../lib/UnlockGuard.h"
  44. #include "../../lib/VCMI_Lib.h"
  45. #ifdef _MSC_VER
  46. #pragma warning (disable : 4355)
  47. #endif
  48. #define ADVOPT (conf.go()->ac)
  49. using namespace CSDL_Ext;
  50. CAdvMapInt * adventureInt;
  51. static void setScrollingCursor(ui8 direction)
  52. {
  53. if(direction & CAdvMapInt::RIGHT)
  54. {
  55. if(direction & CAdvMapInt::UP)
  56. CCS->curh->changeGraphic(ECursor::ADVENTURE, 33);
  57. else if(direction & CAdvMapInt::DOWN)
  58. CCS->curh->changeGraphic(ECursor::ADVENTURE, 35);
  59. else
  60. CCS->curh->changeGraphic(ECursor::ADVENTURE, 34);
  61. }
  62. else if(direction & CAdvMapInt::LEFT)
  63. {
  64. if(direction & CAdvMapInt::UP)
  65. CCS->curh->changeGraphic(ECursor::ADVENTURE, 39);
  66. else if(direction & CAdvMapInt::DOWN)
  67. CCS->curh->changeGraphic(ECursor::ADVENTURE, 37);
  68. else
  69. CCS->curh->changeGraphic(ECursor::ADVENTURE, 38);
  70. }
  71. else if(direction & CAdvMapInt::UP)
  72. CCS->curh->changeGraphic(ECursor::ADVENTURE, 32);
  73. else if(direction & CAdvMapInt::DOWN)
  74. CCS->curh->changeGraphic(ECursor::ADVENTURE, 36);
  75. }
  76. CTerrainRect::CTerrainRect()
  77. : fadeSurface(nullptr), lastRedrawStatus(EMapAnimRedrawStatus::OK), fadeAnim(new CFadeAnimation()), curHoveredTile(-1, -1, -1), currentPath(nullptr)
  78. {
  79. tilesw = (ADVOPT.advmapW + 31) / 32;
  80. tilesh = (ADVOPT.advmapH + 31) / 32;
  81. pos.x = ADVOPT.advmapX;
  82. pos.y = ADVOPT.advmapY;
  83. pos.w = ADVOPT.advmapW;
  84. pos.h = ADVOPT.advmapH;
  85. moveX = moveY = 0;
  86. addUsedEvents(LCLICK | RCLICK | MCLICK | HOVER | MOVE);
  87. }
  88. CTerrainRect::~CTerrainRect()
  89. {
  90. if(fadeSurface)
  91. SDL_FreeSurface(fadeSurface);
  92. delete fadeAnim;
  93. }
  94. void CTerrainRect::deactivate()
  95. {
  96. CIntObject::deactivate();
  97. curHoveredTile = int3(-1, -1, -1); //we lost info about hovered tile when disabling
  98. }
  99. void CTerrainRect::clickLeft(tribool down, bool previousState)
  100. {
  101. if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
  102. return;
  103. if(indeterminate(down))
  104. return;
  105. #ifdef VCMI_ANDROID
  106. if(adventureInt->swipeEnabled)
  107. {
  108. if(handleSwipeStateChange(down == true))
  109. {
  110. return; // if swipe is enabled, we don't process "down" events and wait for "up" (to make sure this wasn't a swiping gesture)
  111. }
  112. }
  113. else
  114. {
  115. #endif
  116. if(down == false)
  117. return;
  118. #ifdef VCMI_ANDROID
  119. }
  120. #endif
  121. int3 mp = whichTileIsIt();
  122. if(mp.x < 0 || mp.y < 0 || mp.x >= LOCPLINT->cb->getMapSize().x || mp.y >= LOCPLINT->cb->getMapSize().y)
  123. return;
  124. adventureInt->tileLClicked(mp);
  125. }
  126. void CTerrainRect::clickRight(tribool down, bool previousState)
  127. {
  128. #ifdef VCMI_ANDROID
  129. if(adventureInt->swipeEnabled && isSwiping)
  130. return;
  131. #endif
  132. if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
  133. return;
  134. int3 mp = whichTileIsIt();
  135. if(CGI->mh->map->isInTheMap(mp) && down)
  136. adventureInt->tileRClicked(mp);
  137. }
  138. void CTerrainRect::clickMiddle(tribool down, bool previousState)
  139. {
  140. handleSwipeStateChange(down == true);
  141. }
  142. void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent)
  143. {
  144. handleHover(sEvent);
  145. if(!adventureInt->swipeEnabled)
  146. return;
  147. handleSwipeMove(sEvent);
  148. }
  149. void CTerrainRect::handleSwipeMove(const SDL_MouseMotionEvent & sEvent)
  150. {
  151. #ifdef VCMI_ANDROID
  152. if(sEvent.state == 0) // any "button" is enough on android
  153. #else //!VCMI_ANDROID
  154. if((sEvent.state & SDL_BUTTON_MMASK) == 0) // swipe only works with middle mouse on other platforms
  155. #endif //!VCMI_ANDROID
  156. {
  157. return;
  158. }
  159. if(!isSwiping)
  160. {
  161. // try to distinguish if this touch was meant to be a swipe or just fat-fingering press
  162. if(abs(sEvent.x - swipeInitialRealPos.x) > SwipeTouchSlop ||
  163. abs(sEvent.y - swipeInitialRealPos.y) > SwipeTouchSlop)
  164. {
  165. isSwiping = true;
  166. }
  167. }
  168. if(isSwiping)
  169. {
  170. adventureInt->swipeTargetPosition.x =
  171. swipeInitialMapPos.x + static_cast<si32>(swipeInitialRealPos.x - sEvent.x) / 32;
  172. adventureInt->swipeTargetPosition.y =
  173. swipeInitialMapPos.y + static_cast<si32>(swipeInitialRealPos.y - sEvent.y) / 32;
  174. adventureInt->swipeMovementRequested = true;
  175. }
  176. }
  177. bool CTerrainRect::handleSwipeStateChange(bool btnPressed)
  178. {
  179. if(btnPressed)
  180. {
  181. swipeInitialRealPos = int3(GH.current->motion.x, GH.current->motion.y, 0);
  182. swipeInitialMapPos = int3(adventureInt->position);
  183. return true;
  184. }
  185. else if(isSwiping) // only accept this touch if it wasn't a swipe
  186. {
  187. isSwiping = false;
  188. return true;
  189. }
  190. return false;
  191. }
  192. void CTerrainRect::handleHover(const SDL_MouseMotionEvent & sEvent)
  193. {
  194. int3 tHovered = whichTileIsIt(sEvent.x, sEvent.y);
  195. int3 pom = adventureInt->verifyPos(tHovered);
  196. if(tHovered != pom) //tile outside the map
  197. {
  198. CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
  199. return;
  200. }
  201. if(pom != curHoveredTile)
  202. {
  203. curHoveredTile = pom;
  204. adventureInt->tileHovered(pom);
  205. }
  206. }
  207. void CTerrainRect::hover(bool on)
  208. {
  209. if(!on)
  210. {
  211. adventureInt->statusbar.clear();
  212. CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
  213. }
  214. //Hoverable::hover(on);
  215. }
  216. void CTerrainRect::showPath(const SDL_Rect * extRect, SDL_Surface * to)
  217. {
  218. const static int pns[9][9] =
  219. {
  220. {16, 17, 18, 7, -1, 19, 6, 5, -1},
  221. { 8, 9, 18, 7, -1, 19, 6, -1, 20},
  222. { 8, 1, 10, 7, -1, 19, -1, 21, 20},
  223. {24, 17, 18, 15, -1, -1, 6, 5, 4},
  224. {-1, -1, -1, -1, -1, -1, -1, -1, -1},
  225. { 8, 1, 2, -1, -1, 11, 22, 21, 20},
  226. {24, 17, -1, 23, -1, 3, 14, 5, 4},
  227. {24, -1, 2, 23, -1, 3, 22, 13, 4},
  228. {-1, 1, 2, 23, -1, 3, 22, 21, 12}
  229. }; //table of magic values TODO meaning, change variable name
  230. for(int i = 0; i < (int)currentPath->nodes.size() - 1; ++i)
  231. {
  232. const int3 & curPos = currentPath->nodes[i].coord, & nextPos = currentPath->nodes[i + 1].coord;
  233. if(curPos.z != adventureInt->position.z)
  234. continue;
  235. int pn = -1; //number of picture
  236. if(i == 0) //last tile
  237. {
  238. int x = 32 * (curPos.x - adventureInt->position.x) + CGI->mh->offsetX + pos.x,
  239. y = 32 * (curPos.y - adventureInt->position.y) + CGI->mh->offsetY + pos.y;
  240. if(x < 0 || y < 0 || x > pos.w || y > pos.h)
  241. continue;
  242. pn = 0;
  243. }
  244. else
  245. {
  246. const int3 & prevPos = currentPath->nodes[i - 1].coord;
  247. std::vector<CGPathNode> & cv = currentPath->nodes;
  248. /* Vector directions
  249. * 0 1 2
  250. * \ | /
  251. * 3 - 4 - 5
  252. * / | \
  253. * 6 7 8
  254. *For example:
  255. * |
  256. * |__\
  257. * /
  258. * is id1=7, id2=5 (pns[7][5])
  259. */
  260. bool pathContinuous = curPos.areNeighbours(nextPos) && curPos.areNeighbours(prevPos);
  261. if(pathContinuous && cv[i].action != CGPathNode::EMBARK && cv[i].action != CGPathNode::DISEMBARK)
  262. {
  263. int id1 = (curPos.x - nextPos.x + 1) + 3 * (curPos.y - nextPos.y + 1); //Direction of entering vector
  264. int id2 = (cv[i - 1].coord.x - curPos.x + 1) + 3 * (cv[i - 1].coord.y - curPos.y + 1); //Direction of exiting vector
  265. pn = pns[id1][id2];
  266. }
  267. else //path discontinuity or sea/land transition (eg. when moving through Subterranean Gate or Boat)
  268. {
  269. pn = 0;
  270. }
  271. }
  272. if(currentPath->nodes[i].turns)
  273. pn += 25;
  274. if(pn >= 0)
  275. {
  276. const IImage * arrow = graphics->heroMoveArrows->getImage(pn);
  277. int x = 32 * (curPos.x - adventureInt->position.x) + CGI->mh->offsetX + pos.x,
  278. y = 32 * (curPos.y - adventureInt->position.y) + CGI->mh->offsetY + pos.y;
  279. if(x < -32 || y < -32 || x > pos.w || y > pos.h)
  280. continue;
  281. int hvx = (x + arrow->width()) - (pos.x + pos.w),
  282. hvy = (y + arrow->height()) - (pos.y + pos.h);
  283. SDL_Rect prevClip;
  284. SDL_GetClipRect(to, &prevClip);
  285. SDL_SetClipRect(to, extRect); //preventing blitting outside of that rect
  286. if(ADVOPT.smoothMove) //version for smooth hero move, with pos shifts
  287. {
  288. if(hvx < 0 && hvy < 0)
  289. {
  290. arrow->draw(to, x + moveX, y + moveY);
  291. }
  292. else if(hvx < 0)
  293. {
  294. Rect srcRect = genRect(arrow->height() - hvy, arrow->width(), 0, 0);
  295. arrow->draw(to, x + moveX, y + moveY, &srcRect);
  296. }
  297. else if(hvy < 0)
  298. {
  299. Rect srcRect = genRect(arrow->height(), arrow->width() - hvx, 0, 0);
  300. arrow->draw(to, x + moveX, y + moveY, &srcRect);
  301. }
  302. else
  303. {
  304. Rect srcRect = genRect(arrow->height() - hvy, arrow->width() - hvx, 0, 0);
  305. arrow->draw(to, x + moveX, y + moveY, &srcRect);
  306. }
  307. }
  308. else //standard version
  309. {
  310. if(hvx < 0 && hvy < 0)
  311. {
  312. arrow->draw(to, x, y);
  313. }
  314. else if(hvx < 0)
  315. {
  316. Rect srcRect = genRect(arrow->height() - hvy, arrow->width(), 0, 0);
  317. arrow->draw(to, x, y, &srcRect);
  318. }
  319. else if(hvy < 0)
  320. {
  321. Rect srcRect = genRect(arrow->height(), arrow->width() - hvx, 0, 0);
  322. arrow->draw(to, x, y, &srcRect);
  323. }
  324. else
  325. {
  326. Rect srcRect = genRect(arrow->height() - hvy, arrow->width() - hvx, 0, 0);
  327. arrow->draw(to, x, y, &srcRect);
  328. }
  329. }
  330. SDL_SetClipRect(to, &prevClip);
  331. }
  332. } //for (int i=0;i<currentPath->nodes.size()-1;i++)
  333. }
  334. void CTerrainRect::show(SDL_Surface * to)
  335. {
  336. if(adventureInt->mode == EAdvMapMode::NORMAL)
  337. {
  338. MapDrawingInfo info(adventureInt->position, &LOCPLINT->cb->getVisibilityMap(), &pos);
  339. info.otherheroAnim = true;
  340. info.anim = adventureInt->anim;
  341. info.heroAnim = adventureInt->heroAnim;
  342. if(ADVOPT.smoothMove)
  343. info.movement = int3(moveX, moveY, 0);
  344. lastRedrawStatus = CGI->mh->drawTerrainRectNew(to, &info);
  345. if(fadeAnim->isFading())
  346. {
  347. Rect r(pos);
  348. fadeAnim->update();
  349. fadeAnim->draw(to, nullptr, &r);
  350. }
  351. if(currentPath /* && adventureInt->position.z==currentPath->startPos().z*/) //drawing path
  352. {
  353. showPath(&pos, to);
  354. }
  355. }
  356. }
  357. void CTerrainRect::showAll(SDL_Surface * to)
  358. {
  359. // world view map is static and doesn't need redraw every frame
  360. if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
  361. {
  362. MapDrawingInfo info(adventureInt->position, &LOCPLINT->cb->getVisibilityMap(), &pos, adventureInt->worldViewIcons);
  363. info.scaled = true;
  364. info.scale = adventureInt->worldViewScale;
  365. adventureInt->worldViewOptions.adjustDrawingInfo(info);
  366. CGI->mh->drawTerrainRectNew(to, &info);
  367. }
  368. }
  369. void CTerrainRect::showAnim(SDL_Surface * to)
  370. {
  371. if(fadeAnim->isFading())
  372. show(to);
  373. else if(lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED)
  374. 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
  375. }
  376. int3 CTerrainRect::whichTileIsIt(const int x, const int y)
  377. {
  378. int3 ret;
  379. ret.x = adventureInt->position.x + ((x - CGI->mh->offsetX - pos.x) / 32);
  380. ret.y = adventureInt->position.y + ((y - CGI->mh->offsetY - pos.y) / 32);
  381. ret.z = adventureInt->position.z;
  382. return ret;
  383. }
  384. int3 CTerrainRect::whichTileIsIt()
  385. {
  386. if(GH.current)
  387. return whichTileIsIt(GH.current->motion.x, GH.current->motion.y);
  388. else
  389. return int3(-1);
  390. }
  391. int3 CTerrainRect::tileCountOnScreen()
  392. {
  393. switch(adventureInt->mode)
  394. {
  395. default:
  396. logGlobal->errorStream() << "Unhandled map mode " << (int)adventureInt->mode;
  397. return int3();
  398. case EAdvMapMode::NORMAL:
  399. return int3(tilesw, tilesh, 1);
  400. case EAdvMapMode::WORLD_VIEW:
  401. return int3(tilesw / adventureInt->worldViewScale, tilesh / adventureInt->worldViewScale, 1);
  402. }
  403. }
  404. void CTerrainRect::fadeFromCurrentView()
  405. {
  406. if(!ADVOPT.screenFading)
  407. return;
  408. if(adventureInt->mode == EAdvMapMode::WORLD_VIEW)
  409. return;
  410. if(!fadeSurface)
  411. fadeSurface = CSDL_Ext::newSurface(pos.w, pos.h);
  412. SDL_BlitSurface(screen, &pos, fadeSurface, nullptr);
  413. fadeAnim->init(CFadeAnimation::EMode::OUT, fadeSurface);
  414. }
  415. bool CTerrainRect::needsAnimUpdate()
  416. {
  417. return fadeAnim->isFading() || lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED;
  418. }
  419. void CResDataBar::clickRight(tribool down, bool previousState)
  420. {
  421. }
  422. CResDataBar::CResDataBar(const std::string & defname, int x, int y, int offx, int offy, int resdist, int datedist)
  423. {
  424. bg = BitmapHandler::loadBitmap(defname);
  425. CSDL_Ext::setDefaultColorKey(bg);
  426. graphics->blueToPlayersAdv(bg, LOCPLINT->playerID);
  427. pos = genRect(bg->h, bg->w, pos.x + x, pos.y + y);
  428. txtpos.resize(8);
  429. for(int i = 0; i < 8; i++)
  430. {
  431. txtpos[i].first = pos.x + offx + resdist * i;
  432. txtpos[i].second = pos.y + offy;
  433. }
  434. txtpos[7].first = txtpos[6].first + datedist;
  435. datetext = CGI->generaltexth->allTexts[62] + ": %s, " + CGI->generaltexth->allTexts[63]
  436. + ": %s, " + CGI->generaltexth->allTexts[64] + ": %s";
  437. addUsedEvents(RCLICK);
  438. }
  439. CResDataBar::CResDataBar()
  440. {
  441. bg = BitmapHandler::loadBitmap(ADVOPT.resdatabarG);
  442. CSDL_Ext::setDefaultColorKey(bg);
  443. graphics->blueToPlayersAdv(bg, LOCPLINT->playerID);
  444. pos = genRect(bg->h, bg->w, ADVOPT.resdatabarX, ADVOPT.resdatabarY);
  445. txtpos.resize(8);
  446. for(int i = 0; i < 8; i++)
  447. {
  448. txtpos[i].first = pos.x + ADVOPT.resOffsetX + ADVOPT.resDist * i;
  449. txtpos[i].second = pos.y + ADVOPT.resOffsetY;
  450. }
  451. txtpos[7].first = txtpos[6].first + ADVOPT.resDateDist;
  452. datetext = CGI->generaltexth->allTexts[62] + ": %s, " + CGI->generaltexth->allTexts[63]
  453. + ": %s, " + CGI->generaltexth->allTexts[64] + ": %s";
  454. }
  455. CResDataBar::~CResDataBar()
  456. {
  457. SDL_FreeSurface(bg);
  458. }
  459. void CResDataBar::draw(SDL_Surface * to)
  460. {
  461. blitAt(bg, pos.x, pos.y, to);
  462. for(auto i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
  463. {
  464. std::string text = boost::lexical_cast<std::string>(LOCPLINT->cb->getResourceAmount(i));
  465. graphics->fonts[FONT_SMALL]->renderTextLeft(to, text, Colors::WHITE, Point(txtpos[i].first, txtpos[i].second));
  466. }
  467. std::vector<std::string> temp;
  468. temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::MONTH)));
  469. temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::WEEK)));
  470. temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::DAY_OF_WEEK)));
  471. graphics->fonts[FONT_SMALL]->renderTextLeft(to, processStr(datetext, temp), Colors::WHITE, Point(txtpos[7].first, txtpos[7].second));
  472. }
  473. void CResDataBar::show(SDL_Surface * to)
  474. {
  475. }
  476. void CResDataBar::showAll(SDL_Surface * to)
  477. {
  478. draw(to);
  479. }
  480. CAdvMapInt::CAdvMapInt()
  481. : mode(EAdvMapMode::NORMAL), worldViewScale(0.0f), //actual init later in changeMode
  482. minimap(Rect(ADVOPT.minimapX, ADVOPT.minimapY, ADVOPT.minimapW, ADVOPT.minimapH)), statusbar(ADVOPT.statusbarX, ADVOPT.statusbarY, ADVOPT.statusbarG), heroList(ADVOPT.hlistSize, Point(ADVOPT.hlistX, ADVOPT.hlistY), ADVOPT.hlistAU, ADVOPT.hlistAD), townList(ADVOPT.tlistSize, Point(ADVOPT.tlistX, ADVOPT.tlistY), ADVOPT.tlistAU, ADVOPT.tlistAD), infoBar(Rect(ADVOPT.infoboxX, ADVOPT.infoboxY, 192, 192)), state(NA), spellBeingCasted(nullptr), position(int3(0, 0, 0)), selection(nullptr), updateScreen(false), anim(0), animValHitCount(0), heroAnim(0), heroAnimValHitCount(0), activeMapPanel(nullptr), duringAITurn(false), scrollingDir(0), scrollingState(false), swipeEnabled(settings["general"]["swipe"].Bool()), swipeMovementRequested(false), swipeTargetPosition(int3(-1, -1, -1))
  483. {
  484. adventureInt = this;
  485. pos.x = pos.y = 0;
  486. pos.w = screen->w;
  487. pos.h = screen->h;
  488. townList.onSelect = std::bind(&CAdvMapInt::selectionChanged, this);
  489. bg = BitmapHandler::loadBitmap(ADVOPT.mainGraphic);
  490. if(ADVOPT.worldViewGraphic != "")
  491. {
  492. bgWorldView = BitmapHandler::loadBitmap(ADVOPT.worldViewGraphic);
  493. }
  494. else
  495. {
  496. bgWorldView = nullptr;
  497. logGlobal->warn("ADVOPT.worldViewGraphic is empty => bitmap not loaded");
  498. }
  499. if(!bgWorldView)
  500. {
  501. logGlobal->warn("bgWorldView not defined in resolution config; fallback to VWorld.bmp");
  502. bgWorldView = BitmapHandler::loadBitmap("VWorld.bmp");
  503. }
  504. worldViewIcons = std::make_shared<CAnimation>("VwSymbol"); //todo: customize with ADVOPT
  505. //preload all for faster map drawing
  506. worldViewIcons->load(); //TODO: make special method in CAnimation fro that
  507. for(int g = 0; g < ADVOPT.gemG.size(); ++g)
  508. {
  509. gems.push_back(new CAnimImage(ADVOPT.gemG[g], 0, 0, ADVOPT.gemX[g], ADVOPT.gemY[g]));
  510. }
  511. auto makeButton = [&](int textID, std::function<void()> callback, config::ButtonInfo info, int key) -> CButton *
  512. {
  513. auto button = new CButton(Point(info.x, info.y), info.defName, CGI->generaltexth->zelp[textID], callback, key, info.playerColoured);
  514. for(auto image : info.additionalDefs)
  515. button->addImage(image);
  516. return button;
  517. };
  518. kingOverview = makeButton(293, std::bind(&CAdvMapInt::fshowOverview, this), ADVOPT.kingOverview, SDLK_k);
  519. underground = makeButton(294, std::bind(&CAdvMapInt::fswitchLevel, this), ADVOPT.underground, SDLK_u);
  520. questlog = makeButton(295, std::bind(&CAdvMapInt::fshowQuestlog, this), ADVOPT.questlog, SDLK_q);
  521. sleepWake = makeButton(296, std::bind(&CAdvMapInt::fsleepWake, this), ADVOPT.sleepWake, SDLK_w);
  522. moveHero = makeButton(297, std::bind(&CAdvMapInt::fmoveHero, this), ADVOPT.moveHero, SDLK_m);
  523. spellbook = makeButton(298, std::bind(&CAdvMapInt::fshowSpellbok, this), ADVOPT.spellbook, SDLK_c);
  524. advOptions = makeButton(299, std::bind(&CAdvMapInt::fadventureOPtions, this), ADVOPT.advOptions, SDLK_a);
  525. sysOptions = makeButton(300, std::bind(&CAdvMapInt::fsystemOptions, this), ADVOPT.sysOptions, SDLK_o);
  526. nextHero = makeButton(301, std::bind(&CAdvMapInt::fnextHero, this), ADVOPT.nextHero, SDLK_h);
  527. endTurn = makeButton(302, std::bind(&CAdvMapInt::fendTurn, this), ADVOPT.endTurn, SDLK_e);
  528. int panelSpaceBottom = screen->h - resdatabar.pos.h - 4;
  529. panelMain = new CAdvMapPanel(nullptr, Point(0, 0));
  530. // TODO correct drawing position
  531. panelWorldView = new CAdvMapWorldViewPanel(worldViewIcons, bgWorldView, Point(heroList.pos.x - 2, 195), panelSpaceBottom, LOCPLINT->playerID);
  532. panelMain->addChildColorableButton(kingOverview);
  533. panelMain->addChildColorableButton(underground);
  534. panelMain->addChildColorableButton(questlog);
  535. panelMain->addChildColorableButton(sleepWake);
  536. panelMain->addChildColorableButton(moveHero);
  537. panelMain->addChildColorableButton(spellbook);
  538. panelMain->addChildColorableButton(advOptions);
  539. panelMain->addChildColorableButton(sysOptions);
  540. panelMain->addChildColorableButton(nextHero);
  541. panelMain->addChildColorableButton(endTurn);
  542. // TODO move configs to resolutions.json, similarly to previous buttons
  543. config::ButtonInfo worldViewBackConfig = config::ButtonInfo();
  544. worldViewBackConfig.defName = "IOK6432.DEF";
  545. worldViewBackConfig.x = screen->w - 73;
  546. worldViewBackConfig.y = 343 + 195;
  547. worldViewBackConfig.playerColoured = false;
  548. panelWorldView->addChildToPanel(
  549. makeButton(288, std::bind(&CAdvMapInt::fworldViewBack, this), worldViewBackConfig, SDLK_ESCAPE), ACTIVATE | DEACTIVATE);
  550. config::ButtonInfo worldViewPuzzleConfig = config::ButtonInfo();
  551. worldViewPuzzleConfig.defName = "VWPUZ.DEF";
  552. worldViewPuzzleConfig.x = screen->w - 188;
  553. worldViewPuzzleConfig.y = 343 + 195;
  554. worldViewPuzzleConfig.playerColoured = false;
  555. panelWorldView->addChildToPanel( // no help text for this one
  556. new CButton(Point(worldViewPuzzleConfig.x, worldViewPuzzleConfig.y), worldViewPuzzleConfig.defName, std::pair<std::string, std::string>(),
  557. std::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT), SDLK_p, worldViewPuzzleConfig.playerColoured), ACTIVATE | DEACTIVATE);
  558. config::ButtonInfo worldViewScale1xConfig = config::ButtonInfo();
  559. worldViewScale1xConfig.defName = "VWMAG1.DEF";
  560. worldViewScale1xConfig.x = screen->w - 191;
  561. worldViewScale1xConfig.y = 23 + 195;
  562. worldViewScale1xConfig.playerColoured = false;
  563. panelWorldView->addChildToPanel( // help text is wrong for this button
  564. makeButton(291, std::bind(&CAdvMapInt::fworldViewScale1x, this), worldViewScale1xConfig, SDLK_1), ACTIVATE | DEACTIVATE);
  565. config::ButtonInfo worldViewScale2xConfig = config::ButtonInfo();
  566. worldViewScale2xConfig.defName = "VWMAG2.DEF";
  567. worldViewScale2xConfig.x = screen->w - 191 + 63;
  568. worldViewScale2xConfig.y = 23 + 195;
  569. worldViewScale2xConfig.playerColoured = false;
  570. panelWorldView->addChildToPanel( // help text is wrong for this button
  571. makeButton(291, std::bind(&CAdvMapInt::fworldViewScale2x, this), worldViewScale2xConfig, SDLK_2), ACTIVATE | DEACTIVATE);
  572. config::ButtonInfo worldViewScale4xConfig = config::ButtonInfo();
  573. worldViewScale4xConfig.defName = "VWMAG4.DEF";
  574. worldViewScale4xConfig.x = screen->w - 191 + 126;
  575. worldViewScale4xConfig.y = 23 + 195;
  576. worldViewScale4xConfig.playerColoured = false;
  577. panelWorldView->addChildToPanel( // help text is wrong for this button
  578. makeButton(291, std::bind(&CAdvMapInt::fworldViewScale4x, this), worldViewScale4xConfig, SDLK_4), ACTIVATE | DEACTIVATE);
  579. config::ButtonInfo worldViewUndergroundConfig = config::ButtonInfo();
  580. worldViewUndergroundConfig.defName = "IAM010.DEF";
  581. worldViewUndergroundConfig.additionalDefs.push_back("IAM003.DEF");
  582. worldViewUndergroundConfig.x = screen->w - 115;
  583. worldViewUndergroundConfig.y = 343 + 195;
  584. worldViewUndergroundConfig.playerColoured = true;
  585. worldViewUnderground = makeButton(294, std::bind(&CAdvMapInt::fswitchLevel, this), worldViewUndergroundConfig, SDLK_u);
  586. panelWorldView->addChildColorableButton(worldViewUnderground);
  587. setPlayer(LOCPLINT->playerID);
  588. int iconColorMultiplier = player.getNum() * 19;
  589. int wvLeft = heroList.pos.x - 2; // TODO correct drawing position
  590. //int wvTop = 195;
  591. for(int i = 0; i < 5; ++i)
  592. {
  593. panelWorldView->addChildIcon(std::pair<int, Point>(i, Point(5, 58 + i * 20)), iconColorMultiplier);
  594. panelWorldView->addChildToPanel(new CLabel(wvLeft + 45, 263 + i * 20, EFonts::FONT_SMALL, EAlignment::TOPLEFT,
  595. Colors::WHITE, CGI->generaltexth->allTexts[612 + i]));
  596. }
  597. for(int i = 0; i < 7; ++i)
  598. {
  599. panelWorldView->addChildIcon(std::pair<int, Point>(i + 5, Point(5, 182 + i * 20)), iconColorMultiplier);
  600. panelWorldView->addChildIcon(std::pair<int, Point>(i + 12, Point(160, 182 + i * 20)), iconColorMultiplier);
  601. panelWorldView->addChildToPanel(new CLabel(wvLeft + 45, 387 + i * 20, EFonts::FONT_SMALL, EAlignment::TOPLEFT,
  602. Colors::WHITE, CGI->generaltexth->allTexts[619 + i]));
  603. }
  604. panelWorldView->addChildToPanel(new CLabel(wvLeft + 5, 367, EFonts::FONT_SMALL, EAlignment::TOPLEFT,
  605. Colors::WHITE, CGI->generaltexth->allTexts[617]));
  606. panelWorldView->addChildToPanel(new CLabel(wvLeft + 185, 387, EFonts::FONT_SMALL, EAlignment::BOTTOMRIGHT,
  607. Colors::WHITE, CGI->generaltexth->allTexts[618]));
  608. activeMapPanel = panelMain;
  609. changeMode(EAdvMapMode::NORMAL);
  610. underground->block(!CGI->mh->map->twoLevel);
  611. questlog->block(!CGI->mh->map->quests.size());
  612. worldViewUnderground->block(!CGI->mh->map->twoLevel);
  613. addUsedEvents(MOVE);
  614. }
  615. CAdvMapInt::~CAdvMapInt()
  616. {
  617. SDL_FreeSurface(bg);
  618. worldViewIcons->unload();
  619. }
  620. void CAdvMapInt::fshowOverview()
  621. {
  622. GH.pushInt(new CKingdomInterface());
  623. }
  624. void CAdvMapInt::fworldViewBack()
  625. {
  626. changeMode(EAdvMapMode::NORMAL);
  627. CGI->mh->discardWorldViewCache();
  628. auto hero = curHero();
  629. if(hero)
  630. centerOn(hero);
  631. }
  632. void CAdvMapInt::fworldViewScale1x()
  633. {
  634. // TODO set corresponding scale button to "selected" mode
  635. changeMode(EAdvMapMode::WORLD_VIEW, 0.22f);
  636. }
  637. void CAdvMapInt::fworldViewScale2x()
  638. {
  639. changeMode(EAdvMapMode::WORLD_VIEW, 0.36f);
  640. }
  641. void CAdvMapInt::fworldViewScale4x()
  642. {
  643. changeMode(EAdvMapMode::WORLD_VIEW, 0.5f);
  644. }
  645. void CAdvMapInt::fswitchLevel()
  646. {
  647. // with support for future multi-level maps :)
  648. int maxLevels = CGI->mh->map->twoLevel ? 2 : 1;
  649. if(maxLevels < 2)
  650. return;
  651. position.z = (position.z + 1) % maxLevels;
  652. underground->setIndex(position.z, true);
  653. underground->redraw();
  654. worldViewUnderground->setIndex(position.z, true);
  655. worldViewUnderground->redraw();
  656. updateScreen = true;
  657. minimap.setLevel(position.z);
  658. if(mode == EAdvMapMode::WORLD_VIEW)
  659. terrain.redraw();
  660. }
  661. void CAdvMapInt::fshowQuestlog()
  662. {
  663. LOCPLINT->showQuestLog();
  664. }
  665. void CAdvMapInt::fsleepWake()
  666. {
  667. const CGHeroInstance * h = curHero();
  668. if(!h)
  669. return;
  670. bool newSleep = !isHeroSleeping(h);
  671. setHeroSleeping(h, newSleep);
  672. updateSleepWake(h);
  673. if(newSleep)
  674. {
  675. fnextHero();
  676. //moveHero.block(true);
  677. //uncomment to enable original HoMM3 behaviour:
  678. //move button is disabled for hero going to sleep, even though it's enabled when you reselect him
  679. }
  680. }
  681. void CAdvMapInt::fmoveHero()
  682. {
  683. const CGHeroInstance * h = curHero();
  684. if(!h || !terrain.currentPath || !CGI->mh->canStartHeroMovement())
  685. return;
  686. LOCPLINT->moveHero(h, *terrain.currentPath);
  687. }
  688. void CAdvMapInt::fshowSpellbok()
  689. {
  690. if(!curHero()) //checking necessary values
  691. return;
  692. centerOn(selection);
  693. GH.pushInt(new CSpellWindow(curHero(), LOCPLINT, false));
  694. }
  695. void CAdvMapInt::fadventureOPtions()
  696. {
  697. GH.pushInt(new CAdventureOptions());
  698. }
  699. void CAdvMapInt::fsystemOptions()
  700. {
  701. GH.pushInt(new CSystemOptionsWindow());
  702. }
  703. void CAdvMapInt::fnextHero()
  704. {
  705. auto hero = dynamic_cast<const CGHeroInstance *>(selection);
  706. int next = getNextHeroIndex(vstd::find_pos(LOCPLINT->wanderingHeroes, hero));
  707. if(next < 0)
  708. return;
  709. select(LOCPLINT->wanderingHeroes[next], true);
  710. }
  711. void CAdvMapInt::fendTurn()
  712. {
  713. if(!LOCPLINT->makingTurn)
  714. return;
  715. if(settings["adventure"]["heroReminder"].Bool())
  716. {
  717. for(int i = 0; i < LOCPLINT->wanderingHeroes.size(); i++)
  718. if(!isHeroSleeping(LOCPLINT->wanderingHeroes[i]) && (LOCPLINT->wanderingHeroes[i]->movement > 0))
  719. {
  720. LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[55], std::bind(&CAdvMapInt::endingTurn, this), 0, false);
  721. return;
  722. }
  723. }
  724. endingTurn();
  725. }
  726. void CAdvMapInt::updateSleepWake(const CGHeroInstance * h)
  727. {
  728. sleepWake->block(!h);
  729. if(!h)
  730. return;
  731. bool state = isHeroSleeping(h);
  732. sleepWake->setIndex(state ? 1 : 0, true);
  733. sleepWake->assignedKeys.clear();
  734. sleepWake->assignedKeys.insert(state ? SDLK_w : SDLK_z);
  735. }
  736. void CAdvMapInt::updateMoveHero(const CGHeroInstance * h, tribool hasPath)
  737. {
  738. if(!h)
  739. {
  740. moveHero->block(true);
  741. return;
  742. }
  743. //default value is for everywhere but CPlayerInterface::moveHero, because paths are not updated from there immediately
  744. if(boost::logic::indeterminate(hasPath))
  745. hasPath = LOCPLINT->paths[h].nodes.size() ? true : false;
  746. moveHero->block(!hasPath || (h->movement == 0));
  747. }
  748. void CAdvMapInt::updateSpellbook(const CGHeroInstance * h)
  749. {
  750. spellbook->block(!h);
  751. }
  752. int CAdvMapInt::getNextHeroIndex(int startIndex)
  753. {
  754. if(LOCPLINT->wanderingHeroes.size() == 0)
  755. return -1;
  756. if(startIndex < 0)
  757. startIndex = 0;
  758. int i = startIndex;
  759. do
  760. {
  761. i++;
  762. if(i >= LOCPLINT->wanderingHeroes.size())
  763. i = 0;
  764. }
  765. while(((LOCPLINT->wanderingHeroes[i]->movement == 0) || isHeroSleeping(LOCPLINT->wanderingHeroes[i])) && (i != startIndex));
  766. if((LOCPLINT->wanderingHeroes[i]->movement != 0) && !isHeroSleeping(LOCPLINT->wanderingHeroes[i]))
  767. return i;
  768. else
  769. return -1;
  770. }
  771. void CAdvMapInt::updateNextHero(const CGHeroInstance * h)
  772. {
  773. int start = vstd::find_pos(LOCPLINT->wanderingHeroes, h);
  774. int next = getNextHeroIndex(start);
  775. if(next < 0)
  776. {
  777. nextHero->block(true);
  778. return;
  779. }
  780. const CGHeroInstance * nextH = LOCPLINT->wanderingHeroes[next];
  781. bool noActiveHeroes = (next == start) && ((nextH->movement == 0) || isHeroSleeping(nextH));
  782. nextHero->block(noActiveHeroes);
  783. }
  784. void CAdvMapInt::activate()
  785. {
  786. CIntObject::activate();
  787. if(!(active & KEYBOARD))
  788. CIntObject::activate(KEYBOARD);
  789. screenBuf = screen;
  790. GH.statusbar = &statusbar;
  791. if(!duringAITurn)
  792. {
  793. activeMapPanel->activate();
  794. if(mode == EAdvMapMode::NORMAL)
  795. {
  796. heroList.activate();
  797. townList.activate();
  798. infoBar.activate();
  799. }
  800. minimap.activate();
  801. terrain.activate();
  802. LOCPLINT->cingconsole->activate();
  803. GH.fakeMouseMove(); //to restore the cursor
  804. }
  805. }
  806. void CAdvMapInt::deactivate()
  807. {
  808. CIntObject::deactivate();
  809. if(!duringAITurn)
  810. {
  811. scrollingDir = 0;
  812. CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
  813. activeMapPanel->deactivate();
  814. if(mode == EAdvMapMode::NORMAL)
  815. {
  816. heroList.deactivate();
  817. townList.deactivate();
  818. infoBar.deactivate();
  819. }
  820. minimap.deactivate();
  821. terrain.deactivate();
  822. if(LOCPLINT)
  823. LOCPLINT->cingconsole->deactivate();
  824. }
  825. }
  826. void CAdvMapInt::showAll(SDL_Surface * to)
  827. {
  828. blitAt(bg, 0, 0, to);
  829. if(state != INGAME)
  830. return;
  831. switch(mode)
  832. {
  833. case EAdvMapMode::NORMAL:
  834. heroList.showAll(to);
  835. townList.showAll(to);
  836. infoBar.showAll(to);
  837. break;
  838. case EAdvMapMode::WORLD_VIEW:
  839. terrain.showAll(to);
  840. break;
  841. }
  842. activeMapPanel->showAll(to);
  843. updateScreen = true;
  844. minimap.showAll(to);
  845. show(to);
  846. resdatabar.draw(to);
  847. statusbar.show(to);
  848. LOCPLINT->cingconsole->show(to);
  849. }
  850. bool CAdvMapInt::isHeroSleeping(const CGHeroInstance * hero)
  851. {
  852. if(!hero)
  853. return false;
  854. return vstd::contains(LOCPLINT->sleepingHeroes, hero);
  855. }
  856. void CAdvMapInt::setHeroSleeping(const CGHeroInstance * hero, bool sleep)
  857. {
  858. if(sleep)
  859. LOCPLINT->sleepingHeroes.push_back(hero); //FIXME: should we check for existence?
  860. else
  861. LOCPLINT->sleepingHeroes -= hero;
  862. updateNextHero(nullptr);
  863. }
  864. void CAdvMapInt::show(SDL_Surface * to)
  865. {
  866. if(state != INGAME)
  867. return;
  868. ++animValHitCount; //for animations
  869. if(animValHitCount == 8)
  870. {
  871. CGI->mh->updateWater();
  872. animValHitCount = 0;
  873. ++anim;
  874. updateScreen = true;
  875. }
  876. ++heroAnim;
  877. if(swipeEnabled)
  878. {
  879. handleSwipeUpdate();
  880. }
  881. #ifdef VCMI_ANDROID // on android, map-moving mode is exclusive (TODO technically it might work with both enabled; to be checked)
  882. else
  883. #endif // VCMI_ANDROID
  884. {
  885. handleMapScrollingUpdate();
  886. }
  887. for(int i = 0; i < 4; i++)
  888. {
  889. if(settings["session"]["spectate"].Bool())
  890. gems[i]->setFrame(PlayerColor(1).getNum());
  891. else
  892. gems[i]->setFrame(LOCPLINT->playerID.getNum());
  893. }
  894. if(updateScreen)
  895. {
  896. int3 betterPos = LOCPLINT->repairScreenPos(position);
  897. if(betterPos != position)
  898. {
  899. logGlobal->warnStream() << "Incorrect position for adventure map!";
  900. position = betterPos;
  901. }
  902. terrain.show(to);
  903. for(int i = 0; i < 4; i++)
  904. gems[i]->showAll(to);
  905. updateScreen = false;
  906. LOCPLINT->cingconsole->show(to);
  907. }
  908. else if(terrain.needsAnimUpdate())
  909. {
  910. terrain.showAnim(to);
  911. for(int i = 0; i < 4; i++)
  912. gems[i]->showAll(to);
  913. }
  914. infoBar.show(to);
  915. statusbar.showAll(to);
  916. }
  917. void CAdvMapInt::handleMapScrollingUpdate()
  918. {
  919. int scrollSpeed = settings["adventure"]["scrollSpeed"].Float();
  920. //if advmap needs updating AND (no dialog is shown OR ctrl is pressed)
  921. if((animValHitCount % (4 / scrollSpeed)) == 0
  922. && ((GH.topInt() == this) || isCtrlKeyDown()))
  923. {
  924. if((scrollingDir & LEFT) && (position.x > -CGI->mh->frameW))
  925. position.x--;
  926. if((scrollingDir & RIGHT) && (position.x < CGI->mh->map->width - CGI->mh->tilesW + CGI->mh->frameW))
  927. position.x++;
  928. if((scrollingDir & UP) && (position.y > -CGI->mh->frameH))
  929. position.y--;
  930. if((scrollingDir & DOWN) && (position.y < CGI->mh->map->height - CGI->mh->tilesH + CGI->mh->frameH))
  931. position.y++;
  932. if(scrollingDir)
  933. {
  934. setScrollingCursor(scrollingDir);
  935. scrollingState = true;
  936. updateScreen = true;
  937. minimap.redraw();
  938. if(mode == EAdvMapMode::WORLD_VIEW)
  939. terrain.redraw();
  940. }
  941. else if(scrollingState)
  942. {
  943. CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
  944. scrollingState = false;
  945. }
  946. }
  947. }
  948. void CAdvMapInt::handleSwipeUpdate()
  949. {
  950. if(swipeMovementRequested)
  951. {
  952. auto fixedPos = LOCPLINT->repairScreenPos(swipeTargetPosition);
  953. position.x = fixedPos.x;
  954. position.y = fixedPos.y;
  955. CCS->curh->changeGraphic(ECursor::DEFAULT, 0);
  956. updateScreen = true;
  957. minimap.redraw();
  958. swipeMovementRequested = false;
  959. }
  960. }
  961. void CAdvMapInt::selectionChanged()
  962. {
  963. const CGTownInstance * to = LOCPLINT->towns[townList.getSelectedIndex()];
  964. if(selection != to)
  965. select(to);
  966. }
  967. void CAdvMapInt::centerOn(int3 on, bool fade)
  968. {
  969. bool switchedLevels = on.z != position.z;
  970. if(fade)
  971. {
  972. terrain.fadeFromCurrentView();
  973. }
  974. switch(mode)
  975. {
  976. default:
  977. case EAdvMapMode::NORMAL:
  978. on.x -= CGI->mh->frameW; // is this intentional? frame size doesn't really have to correspond to camera size...
  979. on.y -= CGI->mh->frameH;
  980. break;
  981. case EAdvMapMode::WORLD_VIEW:
  982. on.x -= CGI->mh->tilesW / 2 / worldViewScale;
  983. on.y -= CGI->mh->tilesH / 2 / worldViewScale;
  984. break;
  985. }
  986. on = LOCPLINT->repairScreenPos(on);
  987. position = on;
  988. updateScreen = true;
  989. underground->setIndex(on.z, true); //change underground switch button image
  990. underground->redraw();
  991. worldViewUnderground->setIndex(on.z, true);
  992. worldViewUnderground->redraw();
  993. if(switchedLevels)
  994. minimap.setLevel(position.z);
  995. minimap.redraw();
  996. if(mode == EAdvMapMode::WORLD_VIEW)
  997. terrain.redraw();
  998. }
  999. void CAdvMapInt::centerOn(const CGObjectInstance * obj, bool fade)
  1000. {
  1001. centerOn(obj->getSightCenter(), fade);
  1002. }
  1003. void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
  1004. {
  1005. if(mode == EAdvMapMode::WORLD_VIEW)
  1006. return;
  1007. ui8 Dir = 0;
  1008. SDL_Keycode k = key.keysym.sym;
  1009. const CGHeroInstance * h = curHero(); //selected hero
  1010. const CGTownInstance * t = curTown(); //selected town
  1011. switch(k)
  1012. {
  1013. case SDLK_g:
  1014. if(key.state != SDL_PRESSED || GH.topInt()->type & BLOCK_ADV_HOTKEYS)
  1015. return;
  1016. {
  1017. //find first town with tavern
  1018. auto itr = range::find_if(LOCPLINT->towns, [](const CGTownInstance * town)
  1019. {
  1020. return town->hasBuilt(BuildingID::TAVERN);
  1021. });
  1022. if(itr != LOCPLINT->towns.end())
  1023. LOCPLINT->showThievesGuildWindow(*itr);
  1024. else
  1025. LOCPLINT->showInfoDialog("No available town with tavern!");
  1026. }
  1027. return;
  1028. case SDLK_i:
  1029. if(isActive())
  1030. CAdventureOptions::showScenarioInfo();
  1031. return;
  1032. case SDLK_l:
  1033. if(isActive())
  1034. LOCPLINT->proposeLoadingGame();
  1035. return;
  1036. case SDLK_s:
  1037. if(isActive() && key.type == SDL_KEYUP)
  1038. GH.pushInt(new CSavingScreen(CPlayerInterface::howManyPeople > 1));
  1039. return;
  1040. case SDLK_d:
  1041. {
  1042. if(h && isActive() && key.state == SDL_PRESSED)
  1043. LOCPLINT->tryDiggging(h);
  1044. return;
  1045. }
  1046. case SDLK_p:
  1047. if(isActive())
  1048. LOCPLINT->showPuzzleMap();
  1049. return;
  1050. case SDLK_v:
  1051. if(isActive())
  1052. LOCPLINT->viewWorldMap();
  1053. return;
  1054. case SDLK_r:
  1055. if(isActive() && LOCPLINT->ctrlPressed())
  1056. {
  1057. LOCPLINT->showYesNoDialog("Are you sure you want to restart game?",
  1058. [](){ LOCPLINT->sendCustomEvent(RESTART_GAME); },
  1059. [](){}, true);
  1060. }
  1061. return;
  1062. case SDLK_SPACE: //space - try to revisit current object with selected hero
  1063. {
  1064. if(!isActive())
  1065. return;
  1066. if(h && key.state == SDL_PRESSED)
  1067. {
  1068. auto unlockPim = vstd::makeUnlockGuard(*CPlayerInterface::pim);
  1069. //TODO!!!!!!! possible freeze, when GS mutex is locked and network thread can't apply package
  1070. //this thread leaves scope and tries to lock pim while holding gs,
  1071. //network thread tries to lock gs (appluy cl) while holding pim
  1072. //this thread should first lock pim, however gs locking/unlocking is done inside cb
  1073. LOCPLINT->cb->moveHero(h, h->pos);
  1074. }
  1075. }
  1076. return;
  1077. case SDLK_RETURN:
  1078. {
  1079. if(!isActive() || !selection || key.state != SDL_PRESSED)
  1080. return;
  1081. if(h)
  1082. LOCPLINT->openHeroWindow(h);
  1083. else if(t)
  1084. LOCPLINT->openTownWindow(t);
  1085. return;
  1086. }
  1087. case SDLK_ESCAPE:
  1088. {
  1089. if(isActive() || GH.topInt() != this || !spellBeingCasted || key.state != SDL_PRESSED)
  1090. return;
  1091. leaveCastingMode();
  1092. return;
  1093. }
  1094. case SDLK_t:
  1095. {
  1096. //act on key down if marketplace windows is not already opened
  1097. if(key.state != SDL_PRESSED || GH.topInt()->type & BLOCK_ADV_HOTKEYS)
  1098. return;
  1099. if(LOCPLINT->ctrlPressed()) //CTRL + T => open marketplace
  1100. {
  1101. //check if we have any marketplace
  1102. const CGTownInstance * townWithMarket = nullptr;
  1103. for(const CGTownInstance * t : LOCPLINT->cb->getTownsInfo())
  1104. {
  1105. if(t->hasBuilt(BuildingID::MARKETPLACE))
  1106. {
  1107. townWithMarket = t;
  1108. break;
  1109. }
  1110. }
  1111. if(townWithMarket) //if any town has marketplace, open window
  1112. GH.pushInt(new CMarketplaceWindow(townWithMarket));
  1113. else //if not - complain
  1114. LOCPLINT->showInfoDialog("No available marketplace!");
  1115. }
  1116. else if(isActive()) //no ctrl, advmapint is on the top => switch to town
  1117. {
  1118. townList.selectNext();
  1119. }
  1120. return;
  1121. }
  1122. default:
  1123. {
  1124. static const int3 directions[] =
  1125. {
  1126. int3(-1, +1, 0), int3(0, +1, 0), int3(+1, +1, 0),
  1127. int3(-1, 0, 0), int3(0, 0, 0), int3(+1, 0, 0),
  1128. int3(-1, -1, 0), int3(0, -1, 0), int3(+1, -1, 0)
  1129. };
  1130. //numpad arrow
  1131. if(CGuiHandler::isArrowKey(k))
  1132. k = CGuiHandler::arrowToNum(k);
  1133. k -= SDLK_KP_1;
  1134. if(k < 0 || k > 8)
  1135. return;
  1136. if(!CGI->mh->canStartHeroMovement())
  1137. return;
  1138. int3 dir = directions[k];
  1139. if(!isActive() || LOCPLINT->ctrlPressed()) //ctrl makes arrow move screen, not hero
  1140. {
  1141. Dir = (dir.x < 0 ? LEFT : 0) |
  1142. (dir.x > 0 ? RIGHT : 0) |
  1143. (dir.y < 0 ? UP : 0) |
  1144. (dir.y > 0 ? DOWN : 0);
  1145. break;
  1146. }
  1147. if(!h || key.state != SDL_PRESSED)
  1148. break;
  1149. if(k == 4)
  1150. {
  1151. centerOn(h);
  1152. return;
  1153. }
  1154. CGPath & path = LOCPLINT->paths[h];
  1155. terrain.currentPath = &path;
  1156. int3 dst = h->getPosition(false) + dir;
  1157. if(dst != verifyPos(dst) || !LOCPLINT->cb->getPathsInfo(h)->getPath(path, dst))
  1158. {
  1159. terrain.currentPath = nullptr;
  1160. return;
  1161. }
  1162. if(path.nodes.size() > 2)
  1163. updateMoveHero(h);
  1164. else if(!path.nodes[0].turns)
  1165. LOCPLINT->moveHero(h, path);
  1166. }
  1167. return;
  1168. }
  1169. if(Dir && key.state == SDL_PRESSED //arrow is pressed
  1170. && LOCPLINT->ctrlPressed())
  1171. scrollingDir |= Dir;
  1172. else
  1173. scrollingDir &= ~Dir;
  1174. }
  1175. void CAdvMapInt::handleRightClick(std::string text, tribool down)
  1176. {
  1177. if(down)
  1178. {
  1179. CRClickPopup::createAndPush(text);
  1180. }
  1181. }
  1182. int3 CAdvMapInt::verifyPos(int3 ver)
  1183. {
  1184. if(ver.x < 0)
  1185. ver.x = 0;
  1186. if(ver.y < 0)
  1187. ver.y = 0;
  1188. if(ver.z < 0)
  1189. ver.z = 0;
  1190. if(ver.x >= CGI->mh->sizes.x)
  1191. ver.x = CGI->mh->sizes.x - 1;
  1192. if(ver.y >= CGI->mh->sizes.y)
  1193. ver.y = CGI->mh->sizes.y - 1;
  1194. if(ver.z >= CGI->mh->sizes.z)
  1195. ver.z = CGI->mh->sizes.z - 1;
  1196. return ver;
  1197. }
  1198. void CAdvMapInt::select(const CArmedInstance * sel, bool centerView)
  1199. {
  1200. assert(sel);
  1201. LOCPLINT->setSelection(sel);
  1202. selection = sel;
  1203. if(LOCPLINT->battleInt == nullptr && LOCPLINT->makingTurn)
  1204. {
  1205. auto pos = sel->visitablePos();
  1206. auto tile = LOCPLINT->cb->getTile(pos);
  1207. if(tile)
  1208. CCS->musich->playMusicFromSet("terrain", tile->terType, true);
  1209. }
  1210. if(centerView)
  1211. centerOn(sel);
  1212. terrain.currentPath = nullptr;
  1213. if(sel->ID == Obj::TOWN)
  1214. {
  1215. auto town = dynamic_cast<const CGTownInstance *>(sel);
  1216. infoBar.showTownSelection(town);
  1217. townList.select(town);
  1218. heroList.select(nullptr);
  1219. updateSleepWake(nullptr);
  1220. updateMoveHero(nullptr);
  1221. updateSpellbook(nullptr);
  1222. }
  1223. else //hero selected
  1224. {
  1225. auto hero = dynamic_cast<const CGHeroInstance *>(sel);
  1226. infoBar.showHeroSelection(hero);
  1227. heroList.select(hero);
  1228. townList.select(nullptr);
  1229. terrain.currentPath = LOCPLINT->getAndVerifyPath(hero);
  1230. updateSleepWake(hero);
  1231. updateMoveHero(hero);
  1232. updateSpellbook(hero);
  1233. }
  1234. townList.redraw();
  1235. heroList.redraw();
  1236. }
  1237. void CAdvMapInt::mouseMoved(const SDL_MouseMotionEvent & sEvent)
  1238. {
  1239. #ifdef VCMI_ANDROID
  1240. if(swipeEnabled)
  1241. return;
  1242. #endif
  1243. // adventure map scrolling with mouse
  1244. // currently disabled in world view mode (as it is in OH3), but should work correctly if mode check is removed
  1245. if(!isCtrlKeyDown() && isActive() && mode == EAdvMapMode::NORMAL)
  1246. {
  1247. if(sEvent.x < 15)
  1248. {
  1249. scrollingDir |= LEFT;
  1250. }
  1251. else
  1252. {
  1253. scrollingDir &= ~LEFT;
  1254. }
  1255. if(sEvent.x > screen->w - 15)
  1256. {
  1257. scrollingDir |= RIGHT;
  1258. }
  1259. else
  1260. {
  1261. scrollingDir &= ~RIGHT;
  1262. }
  1263. if(sEvent.y < 15)
  1264. {
  1265. scrollingDir |= UP;
  1266. }
  1267. else
  1268. {
  1269. scrollingDir &= ~UP;
  1270. }
  1271. if(sEvent.y > screen->h - 15)
  1272. {
  1273. scrollingDir |= DOWN;
  1274. }
  1275. else
  1276. {
  1277. scrollingDir &= ~DOWN;
  1278. }
  1279. }
  1280. }
  1281. bool CAdvMapInt::isActive()
  1282. {
  1283. return active & ~CIntObject::KEYBOARD;
  1284. }
  1285. void CAdvMapInt::startHotSeatWait(PlayerColor Player)
  1286. {
  1287. state = WAITING;
  1288. }
  1289. void CAdvMapInt::setPlayer(PlayerColor Player)
  1290. {
  1291. player = Player;
  1292. graphics->blueToPlayersAdv(bg, player);
  1293. panelMain->setPlayerColor(player);
  1294. panelWorldView->setPlayerColor(player);
  1295. panelWorldView->recolorIcons(player, player.getNum() * 19);
  1296. graphics->blueToPlayersAdv(resdatabar.bg, player);
  1297. }
  1298. void CAdvMapInt::startTurn()
  1299. {
  1300. state = INGAME;
  1301. if(LOCPLINT->cb->getCurrentPlayer() == LOCPLINT->playerID
  1302. || settings["session"]["spectate"].Bool())
  1303. {
  1304. adjustActiveness(false);
  1305. minimap.setAIRadar(false);
  1306. }
  1307. }
  1308. void CAdvMapInt::endingTurn()
  1309. {
  1310. if(settings["session"]["spectate"].Bool())
  1311. return;
  1312. if(LOCPLINT->cingconsole->active)
  1313. LOCPLINT->cingconsole->deactivate();
  1314. LOCPLINT->makingTurn = false;
  1315. LOCPLINT->cb->endTurn();
  1316. }
  1317. const CGObjectInstance * CAdvMapInt::getActiveObject(const int3 & mapPos)
  1318. {
  1319. std::vector<const CGObjectInstance *> bobjs = LOCPLINT->cb->getBlockingObjs(mapPos); //blocking objects at tile
  1320. if(bobjs.empty())
  1321. return nullptr;
  1322. return *boost::range::max_element(bobjs, &CMapHandler::compareObjectBlitOrder);
  1323. /*
  1324. if (bobjs.back()->ID == Obj::HERO)
  1325. return bobjs.back();
  1326. else
  1327. return bobjs.front();*/
  1328. }
  1329. void CAdvMapInt::tileLClicked(const int3 & mapPos)
  1330. {
  1331. if(mode != EAdvMapMode::NORMAL)
  1332. return;
  1333. if(!LOCPLINT->cb->isVisible(mapPos) || !LOCPLINT->makingTurn)
  1334. return;
  1335. const TerrainTile * tile = LOCPLINT->cb->getTile(mapPos);
  1336. const CGObjectInstance * topBlocking = getActiveObject(mapPos);
  1337. int3 selPos = selection->getSightCenter();
  1338. if(spellBeingCasted && isInScreenRange(selPos, mapPos))
  1339. {
  1340. const TerrainTile * heroTile = LOCPLINT->cb->getTile(selPos);
  1341. switch(spellBeingCasted->id)
  1342. {
  1343. case SpellID::SCUTTLE_BOAT: //Scuttle Boat
  1344. if(topBlocking && topBlocking->ID == Obj::BOAT)
  1345. leaveCastingMode(true, mapPos);
  1346. break;
  1347. case SpellID::DIMENSION_DOOR:
  1348. if(!tile || tile->isClear(heroTile))
  1349. leaveCastingMode(true, mapPos);
  1350. break;
  1351. }
  1352. return;
  1353. }
  1354. //check if we can select this object
  1355. bool canSelect = topBlocking && topBlocking->ID == Obj::HERO && topBlocking->tempOwner == LOCPLINT->playerID;
  1356. canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner);
  1357. if(selection->ID != Obj::HERO) //hero is not selected (presumably town)
  1358. {
  1359. assert(!terrain.currentPath); //path can be active only when hero is selected
  1360. if(selection == topBlocking) //selected town clicked
  1361. LOCPLINT->openTownWindow(static_cast<const CGTownInstance *>(topBlocking));
  1362. else if(canSelect)
  1363. select(static_cast<const CArmedInstance *>(topBlocking), false);
  1364. return;
  1365. }
  1366. else if(const CGHeroInstance * currentHero = curHero()) //hero is selected
  1367. {
  1368. const CGPathNode * pn = LOCPLINT->cb->getPathsInfo(currentHero)->getPathInfo(mapPos);
  1369. if(currentHero == topBlocking) //clicked selected hero
  1370. {
  1371. LOCPLINT->openHeroWindow(currentHero);
  1372. return;
  1373. }
  1374. else if(canSelect && pn->turns == 255) //selectable object at inaccessible tile
  1375. {
  1376. select(static_cast<const CArmedInstance *>(topBlocking), false);
  1377. return;
  1378. }
  1379. else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
  1380. {
  1381. if(terrain.currentPath && terrain.currentPath->endPos() == mapPos) //we'll be moving
  1382. {
  1383. if(CGI->mh->canStartHeroMovement())
  1384. LOCPLINT->moveHero(currentHero, *terrain.currentPath);
  1385. return;
  1386. }
  1387. else //remove old path and find a new one if we clicked on accessible tile
  1388. {
  1389. CGPath & path = LOCPLINT->paths[currentHero];
  1390. CGPath newpath;
  1391. bool gotPath = LOCPLINT->cb->getPathsInfo(currentHero)->getPath(newpath, mapPos); //try getting path, erase if failed
  1392. if(gotPath && newpath.nodes.size())
  1393. path = newpath;
  1394. if(path.nodes.size())
  1395. terrain.currentPath = &path;
  1396. else
  1397. LOCPLINT->eraseCurrentPathOf(currentHero);
  1398. updateMoveHero(currentHero);
  1399. }
  1400. }
  1401. } //end of hero is selected "case"
  1402. else
  1403. {
  1404. throw std::runtime_error("Nothing is selected...");
  1405. }
  1406. if(const IShipyard * shipyard = ourInaccessibleShipyard(topBlocking))
  1407. {
  1408. LOCPLINT->showShipyardDialogOrProblemPopup(shipyard);
  1409. }
  1410. }
  1411. void CAdvMapInt::tileHovered(const int3 & mapPos)
  1412. {
  1413. if(mode != EAdvMapMode::NORMAL //disable in world view
  1414. || !selection) //may occur just at the start of game (fake move before full intiialization)
  1415. return;
  1416. if(!LOCPLINT->cb->isVisible(mapPos))
  1417. {
  1418. CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
  1419. statusbar.clear();
  1420. return;
  1421. }
  1422. auto objRelations = PlayerRelations::ALLIES;
  1423. const CGObjectInstance * objAtTile = getActiveObject(mapPos);
  1424. if(objAtTile)
  1425. {
  1426. objRelations = LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner);
  1427. std::string text = curHero() ? objAtTile->getHoverText(curHero()) : objAtTile->getHoverText(LOCPLINT->playerID);
  1428. boost::replace_all(text, "\n", " ");
  1429. statusbar.setText(text);
  1430. }
  1431. else
  1432. {
  1433. std::string hlp;
  1434. CGI->mh->getTerrainDescr(mapPos, hlp, false);
  1435. statusbar.setText(hlp);
  1436. }
  1437. if(spellBeingCasted)
  1438. {
  1439. switch(spellBeingCasted->id)
  1440. {
  1441. case SpellID::SCUTTLE_BOAT:
  1442. if(objAtTile && objAtTile->ID == Obj::BOAT)
  1443. CCS->curh->changeGraphic(ECursor::ADVENTURE, 42);
  1444. else
  1445. CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
  1446. return;
  1447. case SpellID::DIMENSION_DOOR:
  1448. {
  1449. const TerrainTile * t = LOCPLINT->cb->getTile(mapPos, false);
  1450. int3 hpos = selection->getSightCenter();
  1451. if((!t || t->isClear(LOCPLINT->cb->getTile(hpos))) && isInScreenRange(hpos, mapPos))
  1452. CCS->curh->changeGraphic(ECursor::ADVENTURE, 41);
  1453. else
  1454. CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
  1455. return;
  1456. }
  1457. }
  1458. }
  1459. if(selection->ID == Obj::TOWN)
  1460. {
  1461. if(objAtTile)
  1462. {
  1463. if(objAtTile->ID == Obj::TOWN && objRelations != PlayerRelations::ENEMIES)
  1464. CCS->curh->changeGraphic(ECursor::ADVENTURE, 3);
  1465. else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
  1466. CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
  1467. else
  1468. CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
  1469. }
  1470. else
  1471. CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
  1472. }
  1473. else if(const CGHeroInstance * h = curHero())
  1474. {
  1475. int3 mapPosCopy = mapPos;
  1476. const CGPathNode * pnode = LOCPLINT->cb->getPathsInfo(h)->getPathInfo(mapPosCopy);
  1477. assert(pnode);
  1478. int turns = pnode->turns;
  1479. vstd::amin(turns, 3);
  1480. switch(pnode->action)
  1481. {
  1482. case CGPathNode::NORMAL:
  1483. case CGPathNode::TELEPORT_NORMAL:
  1484. if(pnode->layer == EPathfindingLayer::LAND)
  1485. CCS->curh->changeGraphic(ECursor::ADVENTURE, 4 + turns * 6);
  1486. else
  1487. CCS->curh->changeGraphic(ECursor::ADVENTURE, 28 + turns);
  1488. break;
  1489. case CGPathNode::VISIT:
  1490. case CGPathNode::BLOCKING_VISIT:
  1491. case CGPathNode::TELEPORT_BLOCKING_VISIT:
  1492. if(objAtTile && objAtTile->ID == Obj::HERO)
  1493. {
  1494. if(selection == objAtTile)
  1495. CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
  1496. else
  1497. CCS->curh->changeGraphic(ECursor::ADVENTURE, 8 + turns * 6);
  1498. }
  1499. else if(pnode->layer == EPathfindingLayer::LAND)
  1500. CCS->curh->changeGraphic(ECursor::ADVENTURE, 9 + turns * 6);
  1501. else
  1502. CCS->curh->changeGraphic(ECursor::ADVENTURE, 28 + turns);
  1503. break;
  1504. case CGPathNode::BATTLE:
  1505. case CGPathNode::TELEPORT_BATTLE:
  1506. CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns * 6);
  1507. break;
  1508. case CGPathNode::EMBARK:
  1509. CCS->curh->changeGraphic(ECursor::ADVENTURE, 6 + turns * 6);
  1510. break;
  1511. case CGPathNode::DISEMBARK:
  1512. CCS->curh->changeGraphic(ECursor::ADVENTURE, 7 + turns * 6);
  1513. break;
  1514. default:
  1515. if(objAtTile && objRelations != PlayerRelations::ENEMIES)
  1516. {
  1517. if(objAtTile->ID == Obj::TOWN)
  1518. CCS->curh->changeGraphic(ECursor::ADVENTURE, 3);
  1519. else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
  1520. CCS->curh->changeGraphic(ECursor::ADVENTURE, 2);
  1521. }
  1522. else
  1523. CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
  1524. break;
  1525. }
  1526. }
  1527. if(ourInaccessibleShipyard(objAtTile))
  1528. {
  1529. CCS->curh->changeGraphic(ECursor::ADVENTURE, 6);
  1530. }
  1531. }
  1532. void CAdvMapInt::tileRClicked(const int3 & mapPos)
  1533. {
  1534. if(mode != EAdvMapMode::NORMAL)
  1535. return;
  1536. if(spellBeingCasted)
  1537. {
  1538. leaveCastingMode();
  1539. return;
  1540. }
  1541. if(!LOCPLINT->cb->isVisible(mapPos))
  1542. {
  1543. CRClickPopup::createAndPush(VLC->generaltexth->allTexts[61]); //Uncharted Territory
  1544. return;
  1545. }
  1546. const CGObjectInstance * obj = getActiveObject(mapPos);
  1547. if(!obj)
  1548. {
  1549. // Bare or undiscovered terrain
  1550. const TerrainTile * tile = LOCPLINT->cb->getTile(mapPos);
  1551. if(tile)
  1552. {
  1553. std::string hlp;
  1554. CGI->mh->getTerrainDescr(mapPos, hlp, true);
  1555. CRClickPopup::createAndPush(hlp);
  1556. }
  1557. return;
  1558. }
  1559. CRClickPopup::createAndPush(obj, GH.current->motion, CENTER);
  1560. }
  1561. void CAdvMapInt::enterCastingMode(const CSpell * sp)
  1562. {
  1563. assert(sp->id == SpellID::SCUTTLE_BOAT || sp->id == SpellID::DIMENSION_DOOR);
  1564. spellBeingCasted = sp;
  1565. deactivate();
  1566. terrain.activate();
  1567. GH.fakeMouseMove();
  1568. }
  1569. void CAdvMapInt::leaveCastingMode(bool cast, int3 dest)
  1570. {
  1571. assert(spellBeingCasted);
  1572. SpellID id = spellBeingCasted->id;
  1573. spellBeingCasted = nullptr;
  1574. terrain.deactivate();
  1575. activate();
  1576. if(cast)
  1577. LOCPLINT->cb->castSpell(curHero(), id, dest);
  1578. else
  1579. LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[731]); //Spell cancelled
  1580. }
  1581. const CGHeroInstance * CAdvMapInt::curHero() const
  1582. {
  1583. if(selection && selection->ID == Obj::HERO)
  1584. return static_cast<const CGHeroInstance *>(selection);
  1585. else
  1586. return nullptr;
  1587. }
  1588. const CGTownInstance * CAdvMapInt::curTown() const
  1589. {
  1590. if(selection && selection->ID == Obj::TOWN)
  1591. return static_cast<const CGTownInstance *>(selection);
  1592. else
  1593. return nullptr;
  1594. }
  1595. const IShipyard * CAdvMapInt::ourInaccessibleShipyard(const CGObjectInstance * obj) const
  1596. {
  1597. const IShipyard * ret = IShipyard::castFrom(obj);
  1598. if(!ret || obj->tempOwner != player || CCS->curh->type || (CCS->curh->frame != 6 && CCS->curh->frame != 0))
  1599. return nullptr;
  1600. return ret;
  1601. }
  1602. void CAdvMapInt::aiTurnStarted()
  1603. {
  1604. if(settings["session"]["spectate"].Bool())
  1605. return;
  1606. adjustActiveness(true);
  1607. CCS->musich->playMusicFromSet("enemy-turn", true);
  1608. adventureInt->minimap.setAIRadar(true);
  1609. adventureInt->infoBar.startEnemyTurn(LOCPLINT->cb->getCurrentPlayer());
  1610. adventureInt->infoBar.showAll(screen); //force refresh on inactive object
  1611. }
  1612. void CAdvMapInt::adjustActiveness(bool aiTurnStart)
  1613. {
  1614. bool wasActive = isActive();
  1615. if(wasActive)
  1616. deactivate();
  1617. adventureInt->duringAITurn = aiTurnStart;
  1618. if(wasActive)
  1619. activate();
  1620. }
  1621. void CAdvMapInt::quickCombatLock()
  1622. {
  1623. if(!duringAITurn)
  1624. deactivate();
  1625. }
  1626. void CAdvMapInt::quickCombatUnlock()
  1627. {
  1628. if(!duringAITurn)
  1629. activate();
  1630. }
  1631. void CAdvMapInt::changeMode(EAdvMapMode newMode, float newScale)
  1632. {
  1633. if(mode != newMode)
  1634. {
  1635. mode = newMode;
  1636. switch(mode)
  1637. {
  1638. case EAdvMapMode::NORMAL:
  1639. panelMain->activate();
  1640. panelWorldView->deactivate();
  1641. activeMapPanel = panelMain;
  1642. townList.activate();
  1643. heroList.activate();
  1644. infoBar.activate();
  1645. worldViewOptions.clear();
  1646. break;
  1647. case EAdvMapMode::WORLD_VIEW:
  1648. panelMain->deactivate();
  1649. panelWorldView->activate();
  1650. activeMapPanel = panelWorldView;
  1651. townList.deactivate();
  1652. heroList.deactivate();
  1653. infoBar.showSelection(); // to prevent new day animation interfering world view mode
  1654. infoBar.deactivate();
  1655. break;
  1656. }
  1657. worldViewScale = newScale;
  1658. redraw();
  1659. }
  1660. else if(worldViewScale != newScale) // still in world view mode, but the scale changed
  1661. {
  1662. worldViewScale = newScale;
  1663. redraw();
  1664. }
  1665. }
  1666. CAdventureOptions::CAdventureOptions()
  1667. : CWindowObject(PLAYER_COLORED, "ADVOPTS")
  1668. {
  1669. OBJ_CONSTRUCTION_CAPTURING_ALL;
  1670. viewWorld = new CButton(Point(24, 23), "ADVVIEW.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_v);
  1671. viewWorld->addCallback(std::bind(&CPlayerInterface::viewWorldMap, LOCPLINT));
  1672. exit = new CButton(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), std::bind(&CAdventureOptions::close, this), SDLK_RETURN);
  1673. exit->assignedKeys.insert(SDLK_ESCAPE);
  1674. scenInfo = new CButton(Point(24, 198), "ADVINFO.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_i);
  1675. scenInfo->addCallback(CAdventureOptions::showScenarioInfo);
  1676. puzzle = new CButton(Point(24, 81), "ADVPUZ.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_p);
  1677. puzzle->addCallback(std::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT));
  1678. dig = new CButton(Point(24, 139), "ADVDIG.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_d);
  1679. if(const CGHeroInstance * h = adventureInt->curHero())
  1680. dig->addCallback(std::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h));
  1681. else
  1682. dig->block(true);
  1683. }
  1684. void CAdventureOptions::showScenarioInfo()
  1685. {
  1686. auto campState = LOCPLINT->cb->getStartInfo()->campState;
  1687. if(campState)
  1688. {
  1689. GH.pushInt(new CBonusSelection(campState));
  1690. }
  1691. else
  1692. {
  1693. GH.pushInt(new CScenarioInfo(LOCPLINT->cb->getMapHeader(), LOCPLINT->cb->getStartInfo()));
  1694. }
  1695. }
  1696. CAdvMapInt::WorldViewOptions::WorldViewOptions()
  1697. {
  1698. clear();
  1699. }
  1700. void CAdvMapInt::WorldViewOptions::clear()
  1701. {
  1702. showAllTerrain = false;
  1703. iconPositions.clear();
  1704. }
  1705. void CAdvMapInt::WorldViewOptions::adjustDrawingInfo(MapDrawingInfo & info)
  1706. {
  1707. info.showAllTerrain = showAllTerrain;
  1708. info.additionalIcons = &iconPositions;
  1709. }