CAdvMapInt.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512
  1. /*
  2. * CAdvMapInt.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 "CAdvMapInt.h"
  12. #include "CAdvMapPanel.h"
  13. #include "CAdventureOptions.h"
  14. #include "CInGameConsole.h"
  15. #include "mapHandler.h"
  16. #include "../windows/CKingdomInterface.h"
  17. #include "../windows/CSpellWindow.h"
  18. #include "../windows/CTradeWindow.h"
  19. #include "../windows/GUIClasses.h"
  20. #include "../windows/InfoWindows.h"
  21. #include "../CGameInfo.h"
  22. #include "../CMusicHandler.h"
  23. #include "../CPlayerInterface.h"
  24. #include "../lobby/CSavingScreen.h"
  25. #include "../render/CAnimation.h"
  26. #include "../gui/CursorHandler.h"
  27. #include "../render/IImage.h"
  28. #include "../gui/CGuiHandler.h"
  29. #include "../widgets/TextControls.h"
  30. #include "../widgets/Buttons.h"
  31. #include "../windows/SettingsMainContainer.h"
  32. #include "../../CCallback.h"
  33. #include "../../lib/CConfigHandler.h"
  34. #include "../../lib/CGeneralTextHandler.h"
  35. #include "../../lib/spells/CSpellHandler.h"
  36. #include "../../lib/mapObjects/CGHeroInstance.h"
  37. #include "../../lib/mapObjects/CGTownInstance.h"
  38. #include "../../lib/CPathfinder.h"
  39. #include "../../lib/mapping/CMap.h"
  40. #include "../../lib/UnlockGuard.h"
  41. #include "../../lib/TerrainHandler.h"
  42. #include <SDL_surface.h>
  43. #include <SDL_events.h>
  44. #define ADVOPT (conf.go()->ac)
  45. std::shared_ptr<CAdvMapInt> adventureInt;
  46. static void setScrollingCursor(ui8 direction)
  47. {
  48. if(direction & CAdvMapInt::RIGHT)
  49. {
  50. if(direction & CAdvMapInt::UP)
  51. CCS->curh->set(Cursor::Map::SCROLL_NORTHEAST);
  52. else if(direction & CAdvMapInt::DOWN)
  53. CCS->curh->set(Cursor::Map::SCROLL_SOUTHEAST);
  54. else
  55. CCS->curh->set(Cursor::Map::SCROLL_EAST);
  56. }
  57. else if(direction & CAdvMapInt::LEFT)
  58. {
  59. if(direction & CAdvMapInt::UP)
  60. CCS->curh->set(Cursor::Map::SCROLL_NORTHWEST);
  61. else if(direction & CAdvMapInt::DOWN)
  62. CCS->curh->set(Cursor::Map::SCROLL_SOUTHWEST);
  63. else
  64. CCS->curh->set(Cursor::Map::SCROLL_WEST);
  65. }
  66. else if(direction & CAdvMapInt::UP)
  67. CCS->curh->set(Cursor::Map::SCROLL_NORTH);
  68. else if(direction & CAdvMapInt::DOWN)
  69. CCS->curh->set(Cursor::Map::SCROLL_SOUTH);
  70. }
  71. CAdvMapInt::CAdvMapInt():
  72. mode(EAdvMapMode::NORMAL),
  73. worldViewScale(0.0f), //actual init later in changeMode
  74. minimap(Rect(ADVOPT.minimapX, ADVOPT.minimapY, ADVOPT.minimapW, ADVOPT.minimapH)),
  75. statusbar(CGStatusBar::create(ADVOPT.statusbarX,ADVOPT.statusbarY,ADVOPT.statusbarG)),
  76. heroList(ADVOPT.hlistSize, Point(ADVOPT.hlistX, ADVOPT.hlistY), ADVOPT.hlistAU, ADVOPT.hlistAD),
  77. townList(ADVOPT.tlistSize, Point(ADVOPT.tlistX, ADVOPT.tlistY), ADVOPT.tlistAU, ADVOPT.tlistAD),
  78. infoBar(Rect(ADVOPT.infoboxX, ADVOPT.infoboxY, 192, 192)), state(NA),
  79. spellBeingCasted(nullptr), position(int3(0, 0, 0)), selection(nullptr),
  80. updateScreen(false), anim(0), animValHitCount(0), heroAnim(0), heroAnimValHitCount(0),
  81. activeMapPanel(nullptr), duringAITurn(false), scrollingDir(0), scrollingState(false),
  82. swipeEnabled(settings["general"]["swipe"].Bool()), swipeMovementRequested(false),
  83. swipeTargetPosition(int3(-1, -1, -1))
  84. {
  85. pos.x = pos.y = 0;
  86. pos.w = screen->w;
  87. pos.h = screen->h;
  88. strongInterest = true; // handle all mouse move events to prevent dead mouse move space in fullscreen mode
  89. townList.onSelect = std::bind(&CAdvMapInt::selectionChanged,this);
  90. bg = IImage::createFromFile(ADVOPT.mainGraphic);
  91. if(!ADVOPT.worldViewGraphic.empty())
  92. {
  93. bgWorldView = IImage::createFromFile(ADVOPT.worldViewGraphic);
  94. }
  95. else
  96. {
  97. bgWorldView = nullptr;
  98. logGlobal->warn("ADVOPT.worldViewGraphic is empty => bitmap not loaded");
  99. }
  100. if (!bgWorldView)
  101. {
  102. logGlobal->warn("bgWorldView not defined in resolution config; fallback to VWorld.bmp");
  103. bgWorldView = IImage::createFromFile("VWorld.bmp");
  104. }
  105. worldViewIcons = std::make_shared<CAnimation>("VwSymbol");//todo: customize with ADVOPT
  106. worldViewIcons->preload();
  107. for(int g = 0; g < ADVOPT.gemG.size(); ++g)
  108. {
  109. gems.push_back(std::make_shared<CAnimImage>(ADVOPT.gemG[g], 0, 0, ADVOPT.gemX[g], ADVOPT.gemY[g]));
  110. }
  111. auto makeButton = [&](int textID, std::function<void()> callback, config::ButtonInfo info, int key) -> std::shared_ptr<CButton>
  112. {
  113. auto button = std::make_shared<CButton>(Point(info.x, info.y), info.defName, CGI->generaltexth->zelp[textID], callback, key, info.playerColoured);
  114. for(auto image : info.additionalDefs)
  115. button->addImage(image);
  116. return button;
  117. };
  118. kingOverview = makeButton(293, std::bind(&CAdvMapInt::fshowOverview,this), ADVOPT.kingOverview, SDLK_k);
  119. underground = makeButton(294, std::bind(&CAdvMapInt::fswitchLevel,this), ADVOPT.underground, SDLK_u);
  120. questlog = makeButton(295, std::bind(&CAdvMapInt::fshowQuestlog,this), ADVOPT.questlog, SDLK_q);
  121. sleepWake = makeButton(296, std::bind(&CAdvMapInt::fsleepWake,this), ADVOPT.sleepWake, SDLK_w);
  122. moveHero = makeButton(297, std::bind(&CAdvMapInt::fmoveHero,this), ADVOPT.moveHero, SDLK_m);
  123. spellbook = makeButton(298, std::bind(&CAdvMapInt::fshowSpellbok,this), ADVOPT.spellbook, SDLK_c);
  124. advOptions = makeButton(299, std::bind(&CAdvMapInt::fadventureOPtions,this), ADVOPT.advOptions, SDLK_a);
  125. sysOptions = makeButton(300, std::bind(&CAdvMapInt::fsystemOptions,this), ADVOPT.sysOptions, SDLK_o);
  126. nextHero = makeButton(301, std::bind(&CAdvMapInt::fnextHero,this), ADVOPT.nextHero, SDLK_h);
  127. endTurn = makeButton(302, std::bind(&CAdvMapInt::fendTurn,this), ADVOPT.endTurn, SDLK_e);
  128. int panelSpaceBottom = screen->h - resdatabar.pos.h - 4;
  129. panelMain = std::make_shared<CAdvMapPanel>(nullptr, Point(0, 0));
  130. // TODO correct drawing position
  131. panelWorldView = std::make_shared<CAdvMapWorldViewPanel>(worldViewIcons, bgWorldView, Point(heroList.pos.x - 2, 195), panelSpaceBottom, LOCPLINT->playerID);
  132. panelMain->addChildColorableButton(kingOverview);
  133. panelMain->addChildColorableButton(underground);
  134. panelMain->addChildColorableButton(questlog);
  135. panelMain->addChildColorableButton(sleepWake);
  136. panelMain->addChildColorableButton(moveHero);
  137. panelMain->addChildColorableButton(spellbook);
  138. panelMain->addChildColorableButton(advOptions);
  139. panelMain->addChildColorableButton(sysOptions);
  140. panelMain->addChildColorableButton(nextHero);
  141. panelMain->addChildColorableButton(endTurn);
  142. // TODO move configs to resolutions.json, similarly to previous buttons
  143. config::ButtonInfo worldViewBackConfig = config::ButtonInfo();
  144. worldViewBackConfig.defName = "IOK6432.DEF";
  145. worldViewBackConfig.x = screen->w - 73;
  146. worldViewBackConfig.y = 343 + 195;
  147. worldViewBackConfig.playerColoured = false;
  148. panelWorldView->addChildToPanel(
  149. makeButton(288, std::bind(&CAdvMapInt::fworldViewBack,this), worldViewBackConfig, SDLK_ESCAPE), ACTIVATE | DEACTIVATE);
  150. config::ButtonInfo worldViewPuzzleConfig = config::ButtonInfo();
  151. worldViewPuzzleConfig.defName = "VWPUZ.DEF";
  152. worldViewPuzzleConfig.x = screen->w - 188;
  153. worldViewPuzzleConfig.y = 343 + 195;
  154. worldViewPuzzleConfig.playerColoured = false;
  155. panelWorldView->addChildToPanel( // no help text for this one
  156. std::make_shared<CButton>(Point(worldViewPuzzleConfig.x, worldViewPuzzleConfig.y), worldViewPuzzleConfig.defName, std::pair<std::string, std::string>(),
  157. std::bind(&CPlayerInterface::showPuzzleMap,LOCPLINT), SDLK_p, worldViewPuzzleConfig.playerColoured), ACTIVATE | DEACTIVATE);
  158. config::ButtonInfo worldViewScale1xConfig = config::ButtonInfo();
  159. worldViewScale1xConfig.defName = "VWMAG1.DEF";
  160. worldViewScale1xConfig.x = screen->w - 191;
  161. worldViewScale1xConfig.y = 23 + 195;
  162. worldViewScale1xConfig.playerColoured = false;
  163. panelWorldView->addChildToPanel( // help text is wrong for this button
  164. makeButton(291, std::bind(&CAdvMapInt::fworldViewScale1x,this), worldViewScale1xConfig, SDLK_1), ACTIVATE | DEACTIVATE);
  165. config::ButtonInfo worldViewScale2xConfig = config::ButtonInfo();
  166. worldViewScale2xConfig.defName = "VWMAG2.DEF";
  167. worldViewScale2xConfig.x = screen->w - 191 + 63;
  168. worldViewScale2xConfig.y = 23 + 195;
  169. worldViewScale2xConfig.playerColoured = false;
  170. panelWorldView->addChildToPanel( // help text is wrong for this button
  171. makeButton(291, std::bind(&CAdvMapInt::fworldViewScale2x,this), worldViewScale2xConfig, SDLK_2), ACTIVATE | DEACTIVATE);
  172. config::ButtonInfo worldViewScale4xConfig = config::ButtonInfo();
  173. worldViewScale4xConfig.defName = "VWMAG4.DEF";
  174. worldViewScale4xConfig.x = screen->w - 191 + 126;
  175. worldViewScale4xConfig.y = 23 + 195;
  176. worldViewScale4xConfig.playerColoured = false;
  177. panelWorldView->addChildToPanel( // help text is wrong for this button
  178. makeButton(291, std::bind(&CAdvMapInt::fworldViewScale4x,this), worldViewScale4xConfig, SDLK_4), ACTIVATE | DEACTIVATE);
  179. config::ButtonInfo worldViewUndergroundConfig = config::ButtonInfo();
  180. worldViewUndergroundConfig.defName = "IAM010.DEF";
  181. worldViewUndergroundConfig.additionalDefs.push_back("IAM003.DEF");
  182. worldViewUndergroundConfig.x = screen->w - 115;
  183. worldViewUndergroundConfig.y = 343 + 195;
  184. worldViewUndergroundConfig.playerColoured = true;
  185. worldViewUnderground = makeButton(294, std::bind(&CAdvMapInt::fswitchLevel,this), worldViewUndergroundConfig, SDLK_u);
  186. panelWorldView->addChildColorableButton(worldViewUnderground);
  187. setPlayer(LOCPLINT->playerID);
  188. int iconColorMultiplier = player.getNum() * 19;
  189. int wvLeft = heroList.pos.x - 2; // TODO correct drawing position
  190. //int wvTop = 195;
  191. for (int i = 0; i < 5; ++i)
  192. {
  193. panelWorldView->addChildIcon(std::pair<int, Point>(i, Point(5, 58 + i * 20)), iconColorMultiplier);
  194. panelWorldView->addChildToPanel(std::make_shared<CLabel>(wvLeft + 45, 263 + i * 20, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT,
  195. Colors::WHITE, CGI->generaltexth->allTexts[612 + i]));
  196. }
  197. for (int i = 0; i < 7; ++i)
  198. {
  199. panelWorldView->addChildIcon(std::pair<int, Point>(i + 5, Point(5, 182 + i * 20)), iconColorMultiplier);
  200. panelWorldView->addChildIcon(std::pair<int, Point>(i + 12, Point(160, 182 + i * 20)), iconColorMultiplier);
  201. panelWorldView->addChildToPanel(std::make_shared<CLabel>(wvLeft + 45, 387 + i * 20, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT,
  202. Colors::WHITE, CGI->generaltexth->allTexts[619 + i]));
  203. }
  204. panelWorldView->addChildToPanel(std::make_shared<CLabel>(wvLeft + 5, 367, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT,
  205. Colors::WHITE, CGI->generaltexth->allTexts[617]));
  206. panelWorldView->addChildToPanel(std::make_shared<CLabel>(wvLeft + 45, 367, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT,
  207. Colors::WHITE, CGI->generaltexth->allTexts[618]));
  208. activeMapPanel = panelMain;
  209. changeMode(EAdvMapMode::NORMAL);
  210. underground->block(!CGI->mh->map->twoLevel);
  211. questlog->block(!CGI->mh->map->quests.size());
  212. worldViewUnderground->block(!CGI->mh->map->twoLevel);
  213. addUsedEvents(MOVE);
  214. }
  215. void CAdvMapInt::fshowOverview()
  216. {
  217. GH.pushIntT<CKingdomInterface>();
  218. }
  219. void CAdvMapInt::fworldViewBack()
  220. {
  221. changeMode(EAdvMapMode::NORMAL);
  222. CGI->mh->discardWorldViewCache();
  223. auto hero = curHero();
  224. if (hero)
  225. centerOn(hero);
  226. }
  227. void CAdvMapInt::fworldViewScale1x()
  228. {
  229. // TODO set corresponding scale button to "selected" mode
  230. changeMode(EAdvMapMode::WORLD_VIEW, 0.22f);
  231. }
  232. void CAdvMapInt::fworldViewScale2x()
  233. {
  234. changeMode(EAdvMapMode::WORLD_VIEW, 0.36f);
  235. }
  236. void CAdvMapInt::fworldViewScale4x()
  237. {
  238. changeMode(EAdvMapMode::WORLD_VIEW, 0.5f);
  239. }
  240. void CAdvMapInt::fswitchLevel()
  241. {
  242. // with support for future multi-level maps :)
  243. int maxLevels = CGI->mh->map->levels();
  244. if (maxLevels < 2)
  245. return;
  246. position.z = (position.z + 1) % maxLevels;
  247. underground->setIndex(position.z, true);
  248. underground->redraw();
  249. worldViewUnderground->setIndex(position.z, true);
  250. worldViewUnderground->redraw();
  251. updateScreen = true;
  252. minimap.setLevel(position.z);
  253. if (mode == EAdvMapMode::WORLD_VIEW)
  254. terrain.redraw();
  255. }
  256. void CAdvMapInt::fshowQuestlog()
  257. {
  258. LOCPLINT->showQuestLog();
  259. }
  260. void CAdvMapInt::fsleepWake()
  261. {
  262. const CGHeroInstance *h = curHero();
  263. if (!h)
  264. return;
  265. bool newSleep = !isHeroSleeping(h);
  266. setHeroSleeping(h, newSleep);
  267. updateSleepWake(h);
  268. if (newSleep)
  269. {
  270. fnextHero();
  271. //moveHero.block(true);
  272. //uncomment to enable original HoMM3 behaviour:
  273. //move button is disabled for hero going to sleep, even though it's enabled when you reselect him
  274. }
  275. }
  276. void CAdvMapInt::fmoveHero()
  277. {
  278. const CGHeroInstance *h = curHero();
  279. if (!h || !terrain.currentPath || !CGI->mh->canStartHeroMovement())
  280. return;
  281. LOCPLINT->moveHero(h, *terrain.currentPath);
  282. }
  283. void CAdvMapInt::fshowSpellbok()
  284. {
  285. if (!curHero()) //checking necessary values
  286. return;
  287. centerOn(selection);
  288. GH.pushIntT<CSpellWindow>(curHero(), LOCPLINT, false);
  289. }
  290. void CAdvMapInt::fadventureOPtions()
  291. {
  292. GH.pushIntT<CAdventureOptions>();
  293. }
  294. void CAdvMapInt::fsystemOptions()
  295. {
  296. GH.pushIntT<SettingsMainContainer>();
  297. }
  298. void CAdvMapInt::fnextHero()
  299. {
  300. auto hero = dynamic_cast<const CGHeroInstance*>(selection);
  301. int next = getNextHeroIndex(vstd::find_pos(LOCPLINT->wanderingHeroes, hero));
  302. if (next < 0)
  303. return;
  304. select(LOCPLINT->wanderingHeroes[next], true);
  305. }
  306. void CAdvMapInt::fendTurn()
  307. {
  308. if(!LOCPLINT->makingTurn)
  309. return;
  310. if(settings["adventure"]["heroReminder"].Bool())
  311. {
  312. for(auto hero : LOCPLINT->wanderingHeroes)
  313. {
  314. if(!isHeroSleeping(hero) && hero->movement > 0)
  315. {
  316. // Only show hero reminder if conditions met:
  317. // - There still movement points
  318. // - Hero don't have a path or there not points for first step on path
  319. auto path = LOCPLINT->getAndVerifyPath(hero);
  320. if(!path || path->nodes.size() < 2 || !path->nodes[path->nodes.size()-2].turns)
  321. {
  322. LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[55], std::bind(&CAdvMapInt::endingTurn, this), nullptr);
  323. return;
  324. }
  325. }
  326. }
  327. }
  328. endingTurn();
  329. }
  330. void CAdvMapInt::updateSleepWake(const CGHeroInstance *h)
  331. {
  332. sleepWake->block(!h);
  333. if (!h)
  334. return;
  335. bool state = isHeroSleeping(h);
  336. sleepWake->setIndex(state ? 1 : 0, true);
  337. sleepWake->assignedKeys.clear();
  338. sleepWake->assignedKeys.insert(state ? SDLK_w : SDLK_z);
  339. }
  340. void CAdvMapInt::updateMoveHero(const CGHeroInstance *h, tribool hasPath)
  341. {
  342. if(!h)
  343. {
  344. moveHero->block(true);
  345. return;
  346. }
  347. //default value is for everywhere but CPlayerInterface::moveHero, because paths are not updated from there immediately
  348. if(boost::logic::indeterminate(hasPath))
  349. hasPath = LOCPLINT->paths[h].nodes.size() ? true : false;
  350. moveHero->block(!(bool)hasPath || (h->movement == 0));
  351. }
  352. void CAdvMapInt::updateSpellbook(const CGHeroInstance *h)
  353. {
  354. spellbook->block(!h);
  355. }
  356. int CAdvMapInt::getNextHeroIndex(int startIndex)
  357. {
  358. if (LOCPLINT->wanderingHeroes.size() == 0)
  359. return -1;
  360. if (startIndex < 0)
  361. startIndex = 0;
  362. int i = startIndex;
  363. do
  364. {
  365. i++;
  366. if (i >= LOCPLINT->wanderingHeroes.size())
  367. i = 0;
  368. }
  369. while (((LOCPLINT->wanderingHeroes[i]->movement == 0) || isHeroSleeping(LOCPLINT->wanderingHeroes[i])) && (i != startIndex));
  370. if ((LOCPLINT->wanderingHeroes[i]->movement != 0) && !isHeroSleeping(LOCPLINT->wanderingHeroes[i]))
  371. return i;
  372. else
  373. return -1;
  374. }
  375. void CAdvMapInt::updateNextHero(const CGHeroInstance *h)
  376. {
  377. int start = vstd::find_pos(LOCPLINT->wanderingHeroes, h);
  378. int next = getNextHeroIndex(start);
  379. if (next < 0)
  380. {
  381. nextHero->block(true);
  382. return;
  383. }
  384. const CGHeroInstance *nextH = LOCPLINT->wanderingHeroes[next];
  385. bool noActiveHeroes = (next == start) && ((nextH->movement == 0) || isHeroSleeping(nextH));
  386. nextHero->block(noActiveHeroes);
  387. }
  388. void CAdvMapInt::activate()
  389. {
  390. CIntObject::activate();
  391. if (!(active & KEYBOARD))
  392. CIntObject::activate(KEYBOARD);
  393. screenBuf = screen;
  394. GH.statusbar = statusbar;
  395. if(LOCPLINT)
  396. {
  397. LOCPLINT->cingconsole->activate();
  398. LOCPLINT->cingconsole->pos = this->pos;
  399. }
  400. if(!duringAITurn)
  401. {
  402. activeMapPanel->activate();
  403. if (mode == EAdvMapMode::NORMAL)
  404. {
  405. heroList.activate();
  406. townList.activate();
  407. infoBar.activate();
  408. }
  409. minimap.activate();
  410. terrain.activate();
  411. statusbar->activate();
  412. GH.fakeMouseMove(); //to restore the cursor
  413. }
  414. }
  415. void CAdvMapInt::deactivate()
  416. {
  417. CIntObject::deactivate();
  418. if(!duringAITurn)
  419. {
  420. scrollingDir = 0;
  421. CCS->curh->set(Cursor::Map::POINTER);
  422. activeMapPanel->deactivate();
  423. if (mode == EAdvMapMode::NORMAL)
  424. {
  425. heroList.deactivate();
  426. townList.deactivate();
  427. infoBar.deactivate();
  428. }
  429. minimap.deactivate();
  430. terrain.deactivate();
  431. statusbar->deactivate();
  432. }
  433. }
  434. void CAdvMapInt::showAll(SDL_Surface * to)
  435. {
  436. bg->draw(to, 0, 0);
  437. if(state != INGAME)
  438. return;
  439. switch (mode)
  440. {
  441. case EAdvMapMode::NORMAL:
  442. heroList.showAll(to);
  443. townList.showAll(to);
  444. infoBar.showAll(to);
  445. break;
  446. case EAdvMapMode::WORLD_VIEW:
  447. terrain.showAll(to);
  448. break;
  449. }
  450. activeMapPanel->showAll(to);
  451. updateScreen = true;
  452. minimap.showAll(to);
  453. show(to);
  454. resdatabar.showAll(to);
  455. statusbar->show(to);
  456. LOCPLINT->cingconsole->show(to);
  457. }
  458. bool CAdvMapInt::isHeroSleeping(const CGHeroInstance *hero)
  459. {
  460. if (!hero)
  461. return false;
  462. return vstd::contains(LOCPLINT->sleepingHeroes, hero);
  463. }
  464. void CAdvMapInt::setHeroSleeping(const CGHeroInstance *hero, bool sleep)
  465. {
  466. if (sleep)
  467. LOCPLINT->sleepingHeroes.push_back(hero); //FIXME: should we check for existence?
  468. else
  469. LOCPLINT->sleepingHeroes -= hero;
  470. updateNextHero(nullptr);
  471. }
  472. void CAdvMapInt::show(SDL_Surface * to)
  473. {
  474. if(state != INGAME)
  475. return;
  476. ++animValHitCount; //for animations
  477. if(animValHitCount % 2 == 0)
  478. {
  479. ++heroAnim;
  480. }
  481. if(animValHitCount >= 8)
  482. {
  483. CGI->mh->updateWater();
  484. animValHitCount = 0;
  485. ++anim;
  486. updateScreen = true;
  487. }
  488. if(swipeEnabled)
  489. {
  490. handleSwipeUpdate();
  491. }
  492. #if defined(VCMI_ANDROID) || defined(VCMI_IOS) // on mobile, map-moving mode is exclusive (TODO technically it might work with both enabled; to be checked)
  493. else
  494. #endif
  495. {
  496. handleMapScrollingUpdate();
  497. }
  498. for(int i = 0; i < 4; i++)
  499. {
  500. if(settings["session"]["spectate"].Bool())
  501. gems[i]->setFrame(PlayerColor(1).getNum());
  502. else
  503. gems[i]->setFrame(LOCPLINT->playerID.getNum());
  504. }
  505. if(updateScreen)
  506. {
  507. int3 betterPos = LOCPLINT->repairScreenPos(position);
  508. if (betterPos != position)
  509. {
  510. logGlobal->warn("Incorrect position for adventure map!");
  511. position = betterPos;
  512. }
  513. terrain.show(to);
  514. for(int i = 0; i < 4; i++)
  515. gems[i]->showAll(to);
  516. updateScreen=false;
  517. LOCPLINT->cingconsole->show(to);
  518. }
  519. else if (terrain.needsAnimUpdate())
  520. {
  521. terrain.showAnim(to);
  522. for(int i = 0; i < 4; i++)
  523. gems[i]->showAll(to);
  524. }
  525. infoBar.show(to);
  526. statusbar->showAll(to);
  527. }
  528. void CAdvMapInt::handleMapScrollingUpdate()
  529. {
  530. int scrollSpeed = static_cast<int>(settings["adventure"]["scrollSpeed"].Float());
  531. //if advmap needs updating AND (no dialog is shown OR ctrl is pressed)
  532. if((animValHitCount % (4 / scrollSpeed)) == 0
  533. && ((GH.topInt().get() == this) || CSDL_Ext::isCtrlKeyDown()))
  534. {
  535. if((scrollingDir & LEFT) && (position.x > -CGI->mh->frameW))
  536. position.x--;
  537. if((scrollingDir & RIGHT) && (position.x < CGI->mh->map->width - CGI->mh->tilesW + CGI->mh->frameW))
  538. position.x++;
  539. if((scrollingDir & UP) && (position.y > -CGI->mh->frameH))
  540. position.y--;
  541. if((scrollingDir & DOWN) && (position.y < CGI->mh->map->height - CGI->mh->tilesH + CGI->mh->frameH))
  542. position.y++;
  543. if(scrollingDir)
  544. {
  545. setScrollingCursor(scrollingDir);
  546. scrollingState = true;
  547. updateScreen = true;
  548. minimap.redraw();
  549. if(mode == EAdvMapMode::WORLD_VIEW)
  550. terrain.redraw();
  551. }
  552. else if(scrollingState)
  553. {
  554. CCS->curh->set(Cursor::Map::POINTER);
  555. scrollingState = false;
  556. }
  557. }
  558. }
  559. void CAdvMapInt::handleSwipeUpdate()
  560. {
  561. if(swipeMovementRequested)
  562. {
  563. auto fixedPos = LOCPLINT->repairScreenPos(swipeTargetPosition);
  564. position.x = fixedPos.x;
  565. position.y = fixedPos.y;
  566. CCS->curh->set(Cursor::Map::POINTER);
  567. updateScreen = true;
  568. minimap.redraw();
  569. swipeMovementRequested = false;
  570. }
  571. }
  572. void CAdvMapInt::selectionChanged()
  573. {
  574. const CGTownInstance *to = LOCPLINT->towns[townList.getSelectedIndex()];
  575. if (selection != to)
  576. select(to);
  577. }
  578. void CAdvMapInt::centerOn(int3 on, bool fade)
  579. {
  580. bool switchedLevels = on.z != position.z;
  581. if (fade)
  582. {
  583. terrain.fadeFromCurrentView();
  584. }
  585. switch (mode)
  586. {
  587. default:
  588. case EAdvMapMode::NORMAL:
  589. on.x -= CGI->mh->frameW; // is this intentional? frame size doesn't really have to correspond to camera size...
  590. on.y -= CGI->mh->frameH;
  591. break;
  592. case EAdvMapMode::WORLD_VIEW:
  593. on.x -= static_cast<si32>(CGI->mh->tilesW / 2 / worldViewScale);
  594. on.y -= static_cast<si32>(CGI->mh->tilesH / 2 / worldViewScale);
  595. break;
  596. }
  597. on = LOCPLINT->repairScreenPos(on);
  598. position = on;
  599. updateScreen=true;
  600. underground->setIndex(on.z,true); //change underground switch button image
  601. underground->redraw();
  602. worldViewUnderground->setIndex(on.z, true);
  603. worldViewUnderground->redraw();
  604. if (switchedLevels)
  605. minimap.setLevel(position.z);
  606. minimap.redraw();
  607. if (mode == EAdvMapMode::WORLD_VIEW)
  608. terrain.redraw();
  609. }
  610. void CAdvMapInt::centerOn(const CGObjectInstance * obj, bool fade)
  611. {
  612. centerOn(obj->getSightCenter(), fade);
  613. }
  614. void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
  615. {
  616. if (mode == EAdvMapMode::WORLD_VIEW)
  617. return;
  618. ui8 Dir = 0;
  619. SDL_Keycode k = key.keysym.sym;
  620. const CGHeroInstance *h = curHero(); //selected hero
  621. const CGTownInstance *t = curTown(); //selected town
  622. switch(k)
  623. {
  624. case SDLK_g:
  625. if(key.state != SDL_PRESSED || GH.topInt()->type & BLOCK_ADV_HOTKEYS)
  626. return;
  627. {
  628. //find first town with tavern
  629. auto itr = range::find_if(LOCPLINT->towns, [](const CGTownInstance * town)
  630. {
  631. return town->hasBuilt(BuildingID::TAVERN);
  632. });
  633. if(itr != LOCPLINT->towns.end())
  634. LOCPLINT->showThievesGuildWindow(*itr);
  635. else
  636. LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithTavern"));
  637. }
  638. return;
  639. case SDLK_i:
  640. if(isActive())
  641. CAdventureOptions::showScenarioInfo();
  642. return;
  643. case SDLK_l:
  644. if(isActive())
  645. LOCPLINT->proposeLoadingGame();
  646. return;
  647. case SDLK_s:
  648. if(isActive() && key.type == SDL_KEYUP)
  649. GH.pushIntT<CSavingScreen>();
  650. return;
  651. case SDLK_d:
  652. {
  653. if(h && isActive() && LOCPLINT->makingTurn && key.state == SDL_PRESSED)
  654. LOCPLINT->tryDiggging(h);
  655. return;
  656. }
  657. case SDLK_p:
  658. if(isActive())
  659. LOCPLINT->showPuzzleMap();
  660. return;
  661. case SDLK_v:
  662. if(isActive())
  663. LOCPLINT->viewWorldMap();
  664. return;
  665. case SDLK_r:
  666. if(isActive() && LOCPLINT->ctrlPressed())
  667. {
  668. LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.adventureMap.confirmRestartGame"),
  669. [](){ LOCPLINT->sendCustomEvent(EUserEvent::RESTART_GAME); }, nullptr);
  670. }
  671. return;
  672. case SDLK_SPACE: //space - try to revisit current object with selected hero
  673. {
  674. if(!isActive())
  675. return;
  676. if(h && key.state == SDL_PRESSED)
  677. {
  678. auto unlockPim = vstd::makeUnlockGuard(*CPlayerInterface::pim);
  679. //TODO!!!!!!! possible freeze, when GS mutex is locked and network thread can't apply package
  680. //this thread leaves scope and tries to lock pim while holding gs,
  681. //network thread tries to lock gs (appluy cl) while holding pim
  682. //this thread should first lock pim, however gs locking/unlocking is done inside cb
  683. LOCPLINT->cb->moveHero(h,h->pos);
  684. }
  685. }
  686. return;
  687. case SDLK_RETURN:
  688. {
  689. if(!isActive() || !selection || key.state != SDL_PRESSED)
  690. return;
  691. if(h)
  692. LOCPLINT->openHeroWindow(h);
  693. else if(t)
  694. LOCPLINT->openTownWindow(t);
  695. return;
  696. }
  697. case SDLK_ESCAPE:
  698. {
  699. if(isActive() || GH.topInt().get() != this || !spellBeingCasted || key.state != SDL_PRESSED)
  700. return;
  701. leaveCastingMode();
  702. return;
  703. }
  704. case SDLK_t:
  705. {
  706. //act on key down if marketplace windows is not already opened
  707. if(key.state != SDL_PRESSED || GH.topInt()->type & BLOCK_ADV_HOTKEYS)
  708. return;
  709. if(LOCPLINT->ctrlPressed()) //CTRL + T => open marketplace
  710. {
  711. //check if we have any marketplace
  712. const CGTownInstance *townWithMarket = nullptr;
  713. for(const CGTownInstance *t : LOCPLINT->cb->getTownsInfo())
  714. {
  715. if(t->hasBuilt(BuildingID::MARKETPLACE))
  716. {
  717. townWithMarket = t;
  718. break;
  719. }
  720. }
  721. if(townWithMarket) //if any town has marketplace, open window
  722. GH.pushIntT<CMarketplaceWindow>(townWithMarket);
  723. else //if not - complain
  724. LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket"));
  725. }
  726. else if(isActive()) //no ctrl, advmapint is on the top => switch to town
  727. {
  728. townList.selectNext();
  729. }
  730. return;
  731. }
  732. default:
  733. {
  734. static const int3 directions[] = { int3(-1, +1, 0), int3(0, +1, 0), int3(+1, +1, 0),
  735. int3(-1, 0, 0), int3(0, 0, 0), int3(+1, 0, 0),
  736. int3(-1, -1, 0), int3(0, -1, 0), int3(+1, -1, 0) };
  737. //numpad arrow
  738. if(CGuiHandler::isArrowKey(k))
  739. k = CGuiHandler::arrowToNum(k);
  740. k -= SDLK_KP_1;
  741. if(k < 0 || k > 8)
  742. return;
  743. if (!CGI->mh->canStartHeroMovement())
  744. return;
  745. int3 dir = directions[k];
  746. if(!isActive() || LOCPLINT->ctrlPressed())//ctrl makes arrow move screen, not hero
  747. {
  748. Dir = (dir.x<0 ? LEFT : 0) |
  749. (dir.x>0 ? RIGHT : 0) |
  750. (dir.y<0 ? UP : 0) |
  751. (dir.y>0 ? DOWN : 0) ;
  752. break;
  753. }
  754. if(!h || key.state != SDL_PRESSED)
  755. break;
  756. if(k == 4)
  757. {
  758. centerOn(h);
  759. return;
  760. }
  761. CGPath &path = LOCPLINT->paths[h];
  762. terrain.currentPath = &path;
  763. int3 dst = h->visitablePos() + dir;
  764. if(dst != verifyPos(dst) || !LOCPLINT->cb->getPathsInfo(h)->getPath(path, dst))
  765. {
  766. terrain.currentPath = nullptr;
  767. return;
  768. }
  769. if (path.nodes.size() > 2)
  770. updateMoveHero(h);
  771. else
  772. if(!path.nodes[0].turns)
  773. LOCPLINT->moveHero(h, path);
  774. }
  775. return;
  776. }
  777. if(Dir && key.state == SDL_PRESSED //arrow is pressed
  778. && LOCPLINT->ctrlPressed()
  779. )
  780. scrollingDir |= Dir;
  781. else
  782. scrollingDir &= ~Dir;
  783. }
  784. void CAdvMapInt::handleRightClick(std::string text, tribool down)
  785. {
  786. if(down)
  787. {
  788. CRClickPopup::createAndPush(text);
  789. }
  790. }
  791. int3 CAdvMapInt::verifyPos(int3 ver)
  792. {
  793. if (ver.x<0)
  794. ver.x=0;
  795. if (ver.y<0)
  796. ver.y=0;
  797. if (ver.z<0)
  798. ver.z=0;
  799. if (ver.x>=CGI->mh->sizes.x)
  800. ver.x=CGI->mh->sizes.x-1;
  801. if (ver.y>=CGI->mh->sizes.y)
  802. ver.y=CGI->mh->sizes.y-1;
  803. if (ver.z>=CGI->mh->sizes.z)
  804. ver.z=CGI->mh->sizes.z-1;
  805. return ver;
  806. }
  807. void CAdvMapInt::select(const CArmedInstance *sel, bool centerView)
  808. {
  809. assert(sel);
  810. LOCPLINT->setSelection(sel);
  811. selection = sel;
  812. if (LOCPLINT->battleInt == nullptr && LOCPLINT->makingTurn)
  813. {
  814. auto pos = sel->visitablePos();
  815. auto tile = LOCPLINT->cb->getTile(pos);
  816. if(tile)
  817. CCS->musich->playMusicFromSet("terrain", tile->terType->getJsonKey(), true, false);
  818. }
  819. if(centerView)
  820. centerOn(sel);
  821. terrain.currentPath = nullptr;
  822. if(sel->ID==Obj::TOWN)
  823. {
  824. auto town = dynamic_cast<const CGTownInstance*>(sel);
  825. infoBar.showTownSelection(town);
  826. townList.select(town);
  827. heroList.select(nullptr);
  828. updateSleepWake(nullptr);
  829. updateMoveHero(nullptr);
  830. updateSpellbook(nullptr);
  831. }
  832. else //hero selected
  833. {
  834. auto hero = dynamic_cast<const CGHeroInstance*>(sel);
  835. infoBar.showHeroSelection(hero);
  836. heroList.select(hero);
  837. townList.select(nullptr);
  838. terrain.currentPath = LOCPLINT->getAndVerifyPath(hero);
  839. updateSleepWake(hero);
  840. updateMoveHero(hero);
  841. updateSpellbook(hero);
  842. }
  843. townList.redraw();
  844. heroList.redraw();
  845. }
  846. void CAdvMapInt::mouseMoved( const SDL_MouseMotionEvent & sEvent )
  847. {
  848. #if defined(VCMI_ANDROID) || defined(VCMI_IOS)
  849. if(swipeEnabled)
  850. return;
  851. #endif
  852. // adventure map scrolling with mouse
  853. // currently disabled in world view mode (as it is in OH3), but should work correctly if mode check is removed
  854. // don't scroll if there is no window in focus - these events don't seem to correspond to the actual mouse movement
  855. if(!CSDL_Ext::isCtrlKeyDown() && isActive() && sEvent.windowID != 0 && mode == EAdvMapMode::NORMAL)
  856. {
  857. if(sEvent.x<15)
  858. {
  859. scrollingDir |= LEFT;
  860. }
  861. else
  862. {
  863. scrollingDir &= ~LEFT;
  864. }
  865. if(sEvent.x>screen->w-15)
  866. {
  867. scrollingDir |= RIGHT;
  868. }
  869. else
  870. {
  871. scrollingDir &= ~RIGHT;
  872. }
  873. if(sEvent.y<15)
  874. {
  875. scrollingDir |= UP;
  876. }
  877. else
  878. {
  879. scrollingDir &= ~UP;
  880. }
  881. if(sEvent.y>screen->h-15)
  882. {
  883. scrollingDir |= DOWN;
  884. }
  885. else
  886. {
  887. scrollingDir &= ~DOWN;
  888. }
  889. }
  890. }
  891. bool CAdvMapInt::isActive()
  892. {
  893. return active & ~CIntObject::KEYBOARD;
  894. }
  895. void CAdvMapInt::startHotSeatWait(PlayerColor Player)
  896. {
  897. state = WAITING;
  898. }
  899. void CAdvMapInt::setPlayer(PlayerColor Player)
  900. {
  901. player = Player;
  902. bg->playerColored(player);
  903. panelMain->setPlayerColor(player);
  904. panelWorldView->setPlayerColor(player);
  905. panelWorldView->recolorIcons(player, player.getNum() * 19);
  906. resdatabar.background->colorize(player);
  907. }
  908. void CAdvMapInt::startTurn()
  909. {
  910. state = INGAME;
  911. if(LOCPLINT->cb->getCurrentPlayer() == LOCPLINT->playerID
  912. || settings["session"]["spectate"].Bool())
  913. {
  914. adjustActiveness(false);
  915. minimap.setAIRadar(false);
  916. }
  917. }
  918. void CAdvMapInt::endingTurn()
  919. {
  920. if(settings["session"]["spectate"].Bool())
  921. return;
  922. LOCPLINT->makingTurn = false;
  923. LOCPLINT->cb->endTurn();
  924. CCS->soundh->ambientStopAllChannels();
  925. }
  926. const CGObjectInstance* CAdvMapInt::getActiveObject(const int3 &mapPos)
  927. {
  928. std::vector < const CGObjectInstance * > bobjs = LOCPLINT->cb->getBlockingObjs(mapPos); //blocking objects at tile
  929. if (bobjs.empty())
  930. return nullptr;
  931. return *boost::range::max_element(bobjs, &CMapHandler::compareObjectBlitOrder);
  932. /*
  933. if (bobjs.back()->ID == Obj::HERO)
  934. return bobjs.back();
  935. else
  936. return bobjs.front();*/
  937. }
  938. void CAdvMapInt::tileLClicked(const int3 &mapPos)
  939. {
  940. if(mode != EAdvMapMode::NORMAL)
  941. return;
  942. if(!LOCPLINT->cb->isVisible(mapPos) || !LOCPLINT->makingTurn)
  943. return;
  944. const TerrainTile *tile = LOCPLINT->cb->getTile(mapPos);
  945. const CGObjectInstance *topBlocking = getActiveObject(mapPos);
  946. int3 selPos = selection->getSightCenter();
  947. if(spellBeingCasted && isInScreenRange(selPos, mapPos))
  948. {
  949. const TerrainTile *heroTile = LOCPLINT->cb->getTile(selPos);
  950. switch(spellBeingCasted->id)
  951. {
  952. case SpellID::SCUTTLE_BOAT: //Scuttle Boat
  953. if(topBlocking && topBlocking->ID == Obj::BOAT)
  954. leaveCastingMode(true, mapPos);
  955. break;
  956. case SpellID::DIMENSION_DOOR:
  957. if(!tile || tile->isClear(heroTile))
  958. leaveCastingMode(true, mapPos);
  959. break;
  960. }
  961. return;
  962. }
  963. //check if we can select this object
  964. bool canSelect = topBlocking && topBlocking->ID == Obj::HERO && topBlocking->tempOwner == LOCPLINT->playerID;
  965. canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner);
  966. bool isHero = false;
  967. if(selection->ID != Obj::HERO) //hero is not selected (presumably town)
  968. {
  969. assert(!terrain.currentPath); //path can be active only when hero is selected
  970. if(selection == topBlocking) //selected town clicked
  971. LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(topBlocking));
  972. else if(canSelect)
  973. select(static_cast<const CArmedInstance*>(topBlocking), false);
  974. }
  975. else if(const CGHeroInstance * currentHero = curHero()) //hero is selected
  976. {
  977. isHero = true;
  978. const CGPathNode *pn = LOCPLINT->cb->getPathsInfo(currentHero)->getPathInfo(mapPos);
  979. if(currentHero == topBlocking) //clicked selected hero
  980. {
  981. LOCPLINT->openHeroWindow(currentHero);
  982. return;
  983. }
  984. else if(canSelect && pn->turns == 255 ) //selectable object at inaccessible tile
  985. {
  986. select(static_cast<const CArmedInstance*>(topBlocking), false);
  987. return;
  988. }
  989. else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
  990. {
  991. if(terrain.currentPath && terrain.currentPath->endPos() == mapPos)//we'll be moving
  992. {
  993. if(CGI->mh->canStartHeroMovement())
  994. LOCPLINT->moveHero(currentHero, *terrain.currentPath);
  995. return;
  996. }
  997. else //remove old path and find a new one if we clicked on accessible tile
  998. {
  999. CGPath &path = LOCPLINT->paths[currentHero];
  1000. CGPath newpath;
  1001. bool gotPath = LOCPLINT->cb->getPathsInfo(currentHero)->getPath(newpath, mapPos); //try getting path, erase if failed
  1002. if(gotPath && newpath.nodes.size())
  1003. path = newpath;
  1004. if(path.nodes.size())
  1005. terrain.currentPath = &path;
  1006. else
  1007. LOCPLINT->eraseCurrentPathOf(currentHero);
  1008. updateMoveHero(currentHero);
  1009. }
  1010. }
  1011. } //end of hero is selected "case"
  1012. else
  1013. {
  1014. throw std::runtime_error("Nothing is selected...");
  1015. }
  1016. const auto shipyard = ourInaccessibleShipyard(topBlocking);
  1017. if(isHero && shipyard != nullptr)
  1018. {
  1019. LOCPLINT->showShipyardDialogOrProblemPopup(shipyard);
  1020. }
  1021. }
  1022. void CAdvMapInt::tileHovered(const int3 &mapPos)
  1023. {
  1024. if(mode != EAdvMapMode::NORMAL //disable in world view
  1025. || !selection) //may occur just at the start of game (fake move before full intiialization)
  1026. return;
  1027. if(!LOCPLINT->cb->isVisible(mapPos))
  1028. {
  1029. CCS->curh->set(Cursor::Map::POINTER);
  1030. statusbar->clear();
  1031. return;
  1032. }
  1033. auto objRelations = PlayerRelations::ALLIES;
  1034. const CGObjectInstance *objAtTile = getActiveObject(mapPos);
  1035. if(objAtTile)
  1036. {
  1037. objRelations = LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner);
  1038. std::string text = curHero() ? objAtTile->getHoverText(curHero()) : objAtTile->getHoverText(LOCPLINT->playerID);
  1039. boost::replace_all(text,"\n"," ");
  1040. statusbar->write(text);
  1041. }
  1042. else
  1043. {
  1044. std::string hlp;
  1045. CGI->mh->getTerrainDescr(mapPos, hlp, false);
  1046. statusbar->write(hlp);
  1047. }
  1048. if(spellBeingCasted)
  1049. {
  1050. switch(spellBeingCasted->id)
  1051. {
  1052. case SpellID::SCUTTLE_BOAT:
  1053. if(objAtTile && objAtTile->ID == Obj::BOAT)
  1054. CCS->curh->set(Cursor::Map::SCUTTLE_BOAT);
  1055. else
  1056. CCS->curh->set(Cursor::Map::POINTER);
  1057. return;
  1058. case SpellID::DIMENSION_DOOR:
  1059. {
  1060. const TerrainTile * t = LOCPLINT->cb->getTile(mapPos, false);
  1061. int3 hpos = selection->getSightCenter();
  1062. if((!t || t->isClear(LOCPLINT->cb->getTile(hpos))) && isInScreenRange(hpos, mapPos))
  1063. CCS->curh->set(Cursor::Map::TELEPORT);
  1064. else
  1065. CCS->curh->set(Cursor::Map::POINTER);
  1066. return;
  1067. }
  1068. }
  1069. }
  1070. if(selection->ID == Obj::TOWN)
  1071. {
  1072. if(objAtTile)
  1073. {
  1074. if(objAtTile->ID == Obj::TOWN && objRelations != PlayerRelations::ENEMIES)
  1075. CCS->curh->set(Cursor::Map::TOWN);
  1076. else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
  1077. CCS->curh->set(Cursor::Map::HERO);
  1078. else
  1079. CCS->curh->set(Cursor::Map::POINTER);
  1080. }
  1081. else
  1082. CCS->curh->set(Cursor::Map::POINTER);
  1083. }
  1084. else if(const CGHeroInstance * hero = curHero())
  1085. {
  1086. std::array<Cursor::Map, 4> cursorMove = { Cursor::Map::T1_MOVE, Cursor::Map::T2_MOVE, Cursor::Map::T3_MOVE, Cursor::Map::T4_MOVE, };
  1087. std::array<Cursor::Map, 4> cursorAttack = { Cursor::Map::T1_ATTACK, Cursor::Map::T2_ATTACK, Cursor::Map::T3_ATTACK, Cursor::Map::T4_ATTACK, };
  1088. std::array<Cursor::Map, 4> cursorSail = { Cursor::Map::T1_SAIL, Cursor::Map::T2_SAIL, Cursor::Map::T3_SAIL, Cursor::Map::T4_SAIL, };
  1089. std::array<Cursor::Map, 4> cursorDisembark = { Cursor::Map::T1_DISEMBARK, Cursor::Map::T2_DISEMBARK, Cursor::Map::T3_DISEMBARK, Cursor::Map::T4_DISEMBARK, };
  1090. std::array<Cursor::Map, 4> cursorExchange = { Cursor::Map::T1_EXCHANGE, Cursor::Map::T2_EXCHANGE, Cursor::Map::T3_EXCHANGE, Cursor::Map::T4_EXCHANGE, };
  1091. std::array<Cursor::Map, 4> cursorVisit = { Cursor::Map::T1_VISIT, Cursor::Map::T2_VISIT, Cursor::Map::T3_VISIT, Cursor::Map::T4_VISIT, };
  1092. std::array<Cursor::Map, 4> cursorSailVisit = { Cursor::Map::T1_SAIL_VISIT, Cursor::Map::T2_SAIL_VISIT, Cursor::Map::T3_SAIL_VISIT, Cursor::Map::T4_SAIL_VISIT, };
  1093. const CGPathNode * pathNode = LOCPLINT->cb->getPathsInfo(hero)->getPathInfo(mapPos);
  1094. assert(pathNode);
  1095. if(LOCPLINT->altPressed() && pathNode->reachable()) //overwrite status bar text with movement info
  1096. {
  1097. ShowMoveDetailsInStatusbar(*hero, *pathNode);
  1098. }
  1099. int turns = pathNode->turns;
  1100. vstd::amin(turns, 3);
  1101. switch(pathNode->action)
  1102. {
  1103. case CGPathNode::NORMAL:
  1104. case CGPathNode::TELEPORT_NORMAL:
  1105. if(pathNode->layer == EPathfindingLayer::LAND)
  1106. CCS->curh->set(cursorMove[turns]);
  1107. else
  1108. CCS->curh->set(cursorSailVisit[turns]);
  1109. break;
  1110. case CGPathNode::VISIT:
  1111. case CGPathNode::BLOCKING_VISIT:
  1112. case CGPathNode::TELEPORT_BLOCKING_VISIT:
  1113. if(objAtTile && objAtTile->ID == Obj::HERO)
  1114. {
  1115. if(selection == objAtTile)
  1116. CCS->curh->set(Cursor::Map::HERO);
  1117. else
  1118. CCS->curh->set(cursorExchange[turns]);
  1119. }
  1120. else if(pathNode->layer == EPathfindingLayer::LAND)
  1121. CCS->curh->set(cursorVisit[turns]);
  1122. else
  1123. CCS->curh->set(cursorSailVisit[turns]);
  1124. break;
  1125. case CGPathNode::BATTLE:
  1126. case CGPathNode::TELEPORT_BATTLE:
  1127. CCS->curh->set(cursorAttack[turns]);
  1128. break;
  1129. case CGPathNode::EMBARK:
  1130. CCS->curh->set(cursorSail[turns]);
  1131. break;
  1132. case CGPathNode::DISEMBARK:
  1133. CCS->curh->set(cursorDisembark[turns]);
  1134. break;
  1135. default:
  1136. if(objAtTile && objRelations != PlayerRelations::ENEMIES)
  1137. {
  1138. if(objAtTile->ID == Obj::TOWN)
  1139. CCS->curh->set(Cursor::Map::TOWN);
  1140. else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
  1141. CCS->curh->set(Cursor::Map::HERO);
  1142. else
  1143. CCS->curh->set(Cursor::Map::POINTER);
  1144. }
  1145. else
  1146. CCS->curh->set(Cursor::Map::POINTER);
  1147. break;
  1148. }
  1149. }
  1150. if(ourInaccessibleShipyard(objAtTile))
  1151. {
  1152. CCS->curh->set(Cursor::Map::T1_SAIL);
  1153. }
  1154. }
  1155. void CAdvMapInt::ShowMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode)
  1156. {
  1157. const int maxMovementPointsAtStartOfLastTurn = pathNode.turns > 0 ? hero.maxMovePoints(pathNode.layer == EPathfindingLayer::LAND) : hero.movement;
  1158. const int movementPointsLastTurnCost = maxMovementPointsAtStartOfLastTurn - pathNode.moveRemains;
  1159. const int remainingPointsAfterMove = pathNode.turns == 0 ? pathNode.moveRemains : 0;
  1160. std::string result = VLC->generaltexth->translate("vcmi.adventureMap", pathNode.turns > 0 ? "moveCostDetails" : "moveCostDetailsNoTurns");
  1161. boost::replace_first(result, "%TURNS", std::to_string(pathNode.turns));
  1162. boost::replace_first(result, "%POINTS", std::to_string(movementPointsLastTurnCost));
  1163. boost::replace_first(result, "%REMAINING", std::to_string(remainingPointsAfterMove));
  1164. statusbar->write(result);
  1165. }
  1166. void CAdvMapInt::tileRClicked(const int3 &mapPos)
  1167. {
  1168. if(mode != EAdvMapMode::NORMAL)
  1169. return;
  1170. if(spellBeingCasted)
  1171. {
  1172. leaveCastingMode();
  1173. return;
  1174. }
  1175. if(!LOCPLINT->cb->isVisible(mapPos))
  1176. {
  1177. CRClickPopup::createAndPush(VLC->generaltexth->allTexts[61]); //Uncharted Territory
  1178. return;
  1179. }
  1180. const CGObjectInstance * obj = getActiveObject(mapPos);
  1181. if(!obj)
  1182. {
  1183. // Bare or undiscovered terrain
  1184. const TerrainTile * tile = LOCPLINT->cb->getTile(mapPos);
  1185. if (tile)
  1186. {
  1187. std::string hlp;
  1188. CGI->mh->getTerrainDescr(mapPos, hlp, true);
  1189. CRClickPopup::createAndPush(hlp);
  1190. }
  1191. return;
  1192. }
  1193. CRClickPopup::createAndPush(obj, GH.getCursorPosition(), ETextAlignment::CENTER);
  1194. }
  1195. void CAdvMapInt::enterCastingMode(const CSpell * sp)
  1196. {
  1197. assert(sp->id == SpellID::SCUTTLE_BOAT || sp->id == SpellID::DIMENSION_DOOR);
  1198. spellBeingCasted = sp;
  1199. deactivate();
  1200. terrain.activate();
  1201. GH.fakeMouseMove();
  1202. }
  1203. void CAdvMapInt::leaveCastingMode(bool cast, int3 dest)
  1204. {
  1205. assert(spellBeingCasted);
  1206. SpellID id = spellBeingCasted->id;
  1207. spellBeingCasted = nullptr;
  1208. terrain.deactivate();
  1209. activate();
  1210. if(cast)
  1211. LOCPLINT->cb->castSpell(curHero(), id, dest);
  1212. else
  1213. LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[731]); //Spell cancelled
  1214. }
  1215. const CGHeroInstance * CAdvMapInt::curHero() const
  1216. {
  1217. if(selection && selection->ID == Obj::HERO)
  1218. return static_cast<const CGHeroInstance *>(selection);
  1219. else
  1220. return nullptr;
  1221. }
  1222. const CGTownInstance * CAdvMapInt::curTown() const
  1223. {
  1224. if(selection && selection->ID == Obj::TOWN)
  1225. return static_cast<const CGTownInstance *>(selection);
  1226. else
  1227. return nullptr;
  1228. }
  1229. const IShipyard * CAdvMapInt::ourInaccessibleShipyard(const CGObjectInstance *obj) const
  1230. {
  1231. const IShipyard *ret = IShipyard::castFrom(obj);
  1232. if(!ret ||
  1233. obj->tempOwner != player ||
  1234. (CCS->curh->get<Cursor::Map>() != Cursor::Map::T1_SAIL && CCS->curh->get<Cursor::Map>() != Cursor::Map::POINTER))
  1235. return nullptr;
  1236. return ret;
  1237. }
  1238. void CAdvMapInt::aiTurnStarted()
  1239. {
  1240. if(settings["session"]["spectate"].Bool())
  1241. return;
  1242. adjustActiveness(true);
  1243. CCS->musich->playMusicFromSet("enemy-turn", true, false);
  1244. adventureInt->minimap.setAIRadar(true);
  1245. adventureInt->infoBar.startEnemyTurn(LOCPLINT->cb->getCurrentPlayer());
  1246. adventureInt->infoBar.showAll(screen);//force refresh on inactive object
  1247. }
  1248. void CAdvMapInt::adjustActiveness(bool aiTurnStart)
  1249. {
  1250. bool wasActive = isActive();
  1251. if(wasActive)
  1252. deactivate();
  1253. adventureInt->duringAITurn = aiTurnStart;
  1254. if(wasActive)
  1255. activate();
  1256. }
  1257. void CAdvMapInt::quickCombatLock()
  1258. {
  1259. if(!duringAITurn)
  1260. deactivate();
  1261. }
  1262. void CAdvMapInt::quickCombatUnlock()
  1263. {
  1264. if(!duringAITurn)
  1265. activate();
  1266. }
  1267. void CAdvMapInt::changeMode(EAdvMapMode newMode, float newScale)
  1268. {
  1269. if (mode != newMode)
  1270. {
  1271. mode = newMode;
  1272. switch (mode)
  1273. {
  1274. case EAdvMapMode::NORMAL:
  1275. panelMain->activate();
  1276. panelWorldView->deactivate();
  1277. activeMapPanel = panelMain;
  1278. townList.activate();
  1279. heroList.activate();
  1280. infoBar.activate();
  1281. worldViewOptions.clear();
  1282. break;
  1283. case EAdvMapMode::WORLD_VIEW:
  1284. panelMain->deactivate();
  1285. panelWorldView->activate();
  1286. activeMapPanel = panelWorldView;
  1287. townList.deactivate();
  1288. heroList.deactivate();
  1289. infoBar.showSelection(); // to prevent new day animation interfering world view mode
  1290. infoBar.deactivate();
  1291. break;
  1292. }
  1293. worldViewScale = newScale;
  1294. redraw();
  1295. }
  1296. else if (worldViewScale != newScale) // still in world view mode, but the scale changed
  1297. {
  1298. worldViewScale = newScale;
  1299. redraw();
  1300. }
  1301. }
  1302. CAdvMapInt::WorldViewOptions::WorldViewOptions()
  1303. {
  1304. clear();
  1305. }
  1306. void CAdvMapInt::WorldViewOptions::clear()
  1307. {
  1308. showAllTerrain = false;
  1309. iconPositions.clear();
  1310. }
  1311. void CAdvMapInt::WorldViewOptions::adjustDrawingInfo(MapDrawingInfo& info)
  1312. {
  1313. info.showAllTerrain = showAllTerrain;
  1314. info.additionalIcons = &iconPositions;
  1315. }