CAdventureMapInterface.cpp 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346
  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 "CAdventureMapInterface.h"
  12. #include "CAdvMapPanel.h"
  13. #include "CAdventureOptions.h"
  14. #include "CInGameConsole.h"
  15. #include "CMinimap.h"
  16. #include "CResDataBar.h"
  17. #include "CList.h"
  18. #include "CInfoBar.h"
  19. #include "MapAudioPlayer.h"
  20. #include "../mapView/mapHandler.h"
  21. #include "../mapView/MapView.h"
  22. #include "../windows/CKingdomInterface.h"
  23. #include "../windows/CSpellWindow.h"
  24. #include "../windows/CTradeWindow.h"
  25. #include "../windows/GUIClasses.h"
  26. #include "../windows/InfoWindows.h"
  27. #include "../CGameInfo.h"
  28. #include "../CPlayerInterface.h"
  29. #include "../lobby/CSavingScreen.h"
  30. #include "../render/CAnimation.h"
  31. #include "../gui/CursorHandler.h"
  32. #include "../render/IImage.h"
  33. #include "../gui/CGuiHandler.h"
  34. #include "../gui/Shortcut.h"
  35. #include "../widgets/TextControls.h"
  36. #include "../widgets/Buttons.h"
  37. #include "../windows/settings/SettingsMainWindow.h"
  38. #include "../CMT.h"
  39. #include "../PlayerLocalState.h"
  40. #include "../../CCallback.h"
  41. #include "../../lib/CConfigHandler.h"
  42. #include "../../lib/CGeneralTextHandler.h"
  43. #include "../../lib/spells/CSpellHandler.h"
  44. #include "../../lib/mapObjects/CGHeroInstance.h"
  45. #include "../../lib/mapObjects/CGTownInstance.h"
  46. #include "../../lib/CPathfinder.h"
  47. #include "../../lib/mapping/CMap.h"
  48. #define ADVOPT (conf.go()->ac)
  49. std::shared_ptr<CAdventureMapInterface> adventureInt;
  50. CAdventureMapInterface::CAdventureMapInterface():
  51. minimap(new CMinimap(Rect(ADVOPT.minimapX, ADVOPT.minimapY, ADVOPT.minimapW, ADVOPT.minimapH))),
  52. statusbar(CGStatusBar::create(ADVOPT.statusbarX,ADVOPT.statusbarY,ADVOPT.statusbarG)),
  53. heroList(new CHeroList(ADVOPT.hlistSize, Point(ADVOPT.hlistX, ADVOPT.hlistY), ADVOPT.hlistAU, ADVOPT.hlistAD)),
  54. townList(new CTownList(ADVOPT.tlistSize, Point(ADVOPT.tlistX, ADVOPT.tlistY), ADVOPT.tlistAU, ADVOPT.tlistAD)),
  55. infoBar(new CInfoBar(Point(ADVOPT.infoboxX, ADVOPT.infoboxY))),
  56. resdatabar(new CResDataBar),
  57. mapAudio(new MapAudioPlayer()),
  58. terrain(new MapView(Point(ADVOPT.advmapX, ADVOPT.advmapY), Point(ADVOPT.advmapW, ADVOPT.advmapH))),
  59. state(EGameState::NOT_INITIALIZED),
  60. spellBeingCasted(nullptr),
  61. activeMapPanel(nullptr),
  62. scrollingCursorSet(false)
  63. {
  64. pos.x = pos.y = 0;
  65. pos.w = GH.screenDimensions().x;
  66. pos.h = GH.screenDimensions().y;
  67. strongInterest = true; // handle all mouse move events to prevent dead mouse move space in fullscreen mode
  68. bg = IImage::createFromFile(ADVOPT.mainGraphic);
  69. if(!ADVOPT.worldViewGraphic.empty())
  70. {
  71. bgWorldView = IImage::createFromFile(ADVOPT.worldViewGraphic);
  72. }
  73. else
  74. {
  75. bgWorldView = nullptr;
  76. logGlobal->warn("ADVOPT.worldViewGraphic is empty => bitmap not loaded");
  77. }
  78. if (!bgWorldView)
  79. {
  80. logGlobal->warn("bgWorldView not defined in resolution config; fallback to VWorld.bmp");
  81. bgWorldView = IImage::createFromFile("VWorld.bmp");
  82. }
  83. worldViewIcons = std::make_shared<CAnimation>("VwSymbol");//todo: customize with ADVOPT
  84. worldViewIcons->preload();
  85. for(int g = 0; g < ADVOPT.gemG.size(); ++g)
  86. {
  87. gems.push_back(std::make_shared<CAnimImage>(ADVOPT.gemG[g], 0, 0, ADVOPT.gemX[g], ADVOPT.gemY[g]));
  88. }
  89. auto makeButton = [&](int textID, std::function<void()> callback, config::ButtonInfo info, EShortcut key) -> std::shared_ptr<CButton>
  90. {
  91. auto button = std::make_shared<CButton>(Point(info.x, info.y), info.defName, CGI->generaltexth->zelp[textID], callback, key, info.playerColoured);
  92. for(auto image : info.additionalDefs)
  93. button->addImage(image);
  94. return button;
  95. };
  96. kingOverview = makeButton(293, std::bind(&CAdventureMapInterface::fshowOverview,this), ADVOPT.kingOverview, EShortcut::ADVENTURE_KINGDOM_OVERVIEW);
  97. underground = makeButton(294, std::bind(&CAdventureMapInterface::fswitchLevel,this), ADVOPT.underground, EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL);
  98. questlog = makeButton(295, std::bind(&CAdventureMapInterface::fshowQuestlog,this), ADVOPT.questlog, EShortcut::ADVENTURE_QUEST_LOG);
  99. sleepWake = makeButton(296, std::bind(&CAdventureMapInterface::fsleepWake,this), ADVOPT.sleepWake, EShortcut::ADVENTURE_TOGGLE_SLEEP);
  100. moveHero = makeButton(297, std::bind(&CAdventureMapInterface::fmoveHero,this), ADVOPT.moveHero, EShortcut::ADVENTURE_MOVE_HERO);
  101. spellbook = makeButton(298, std::bind(&CAdventureMapInterface::fshowSpellbok,this), ADVOPT.spellbook, EShortcut::ADVENTURE_CAST_SPELL);
  102. advOptions = makeButton(299, std::bind(&CAdventureMapInterface::fadventureOPtions,this), ADVOPT.advOptions, EShortcut::ADVENTURE_GAME_OPTIONS);
  103. sysOptions = makeButton(300, std::bind(&CAdventureMapInterface::fsystemOptions,this), ADVOPT.sysOptions, EShortcut::GLOBAL_OPTIONS);
  104. nextHero = makeButton(301, std::bind(&CAdventureMapInterface::fnextHero,this), ADVOPT.nextHero, EShortcut::ADVENTURE_NEXT_HERO);
  105. endTurn = makeButton(302, std::bind(&CAdventureMapInterface::fendTurn,this), ADVOPT.endTurn, EShortcut::ADVENTURE_END_TURN);
  106. int panelSpaceBottom = GH.screenDimensions().y - resdatabar->pos.h - 4;
  107. panelMain = std::make_shared<CAdvMapPanel>(nullptr, Point(0, 0));
  108. // TODO correct drawing position
  109. panelWorldView = std::make_shared<CAdvMapWorldViewPanel>(worldViewIcons, bgWorldView, Point(heroList->pos.x - 2, 195), panelSpaceBottom, LOCPLINT->playerID);
  110. panelMain->addChildColorableButton(kingOverview);
  111. panelMain->addChildColorableButton(underground);
  112. panelMain->addChildColorableButton(questlog);
  113. panelMain->addChildColorableButton(sleepWake);
  114. panelMain->addChildColorableButton(moveHero);
  115. panelMain->addChildColorableButton(spellbook);
  116. panelMain->addChildColorableButton(advOptions);
  117. panelMain->addChildColorableButton(sysOptions);
  118. panelMain->addChildColorableButton(nextHero);
  119. panelMain->addChildColorableButton(endTurn);
  120. // TODO move configs to resolutions.json, similarly to previous buttons
  121. config::ButtonInfo worldViewBackConfig = config::ButtonInfo();
  122. worldViewBackConfig.defName = "IOK6432.DEF";
  123. worldViewBackConfig.x = GH.screenDimensions().x - 73;
  124. worldViewBackConfig.y = 343 + 195;
  125. worldViewBackConfig.playerColoured = false;
  126. panelWorldView->addChildToPanel(
  127. makeButton(288, std::bind(&CAdventureMapInterface::fworldViewBack,this), worldViewBackConfig, EShortcut::GLOBAL_CANCEL), ACTIVATE | DEACTIVATE);
  128. config::ButtonInfo worldViewPuzzleConfig = config::ButtonInfo();
  129. worldViewPuzzleConfig.defName = "VWPUZ.DEF";
  130. worldViewPuzzleConfig.x = GH.screenDimensions().x - 188;
  131. worldViewPuzzleConfig.y = 343 + 195;
  132. worldViewPuzzleConfig.playerColoured = false;
  133. panelWorldView->addChildToPanel( // no help text for this one
  134. std::make_shared<CButton>(Point(worldViewPuzzleConfig.x, worldViewPuzzleConfig.y), worldViewPuzzleConfig.defName, std::pair<std::string, std::string>(),
  135. std::bind(&CPlayerInterface::showPuzzleMap,LOCPLINT), EShortcut::ADVENTURE_VIEW_PUZZLE, worldViewPuzzleConfig.playerColoured), ACTIVATE | DEACTIVATE);
  136. config::ButtonInfo worldViewScale1xConfig = config::ButtonInfo();
  137. worldViewScale1xConfig.defName = "VWMAG1.DEF";
  138. worldViewScale1xConfig.x = GH.screenDimensions().x - 191;
  139. worldViewScale1xConfig.y = 23 + 195;
  140. worldViewScale1xConfig.playerColoured = false;
  141. panelWorldView->addChildToPanel( // help text is wrong for this button
  142. makeButton(291, std::bind(&CAdventureMapInterface::fworldViewScale1x,this), worldViewScale1xConfig, EShortcut::SELECT_INDEX_1), ACTIVATE | DEACTIVATE);
  143. config::ButtonInfo worldViewScale2xConfig = config::ButtonInfo();
  144. worldViewScale2xConfig.defName = "VWMAG2.DEF";
  145. worldViewScale2xConfig.x = GH.screenDimensions().x- 191 + 63;
  146. worldViewScale2xConfig.y = 23 + 195;
  147. worldViewScale2xConfig.playerColoured = false;
  148. panelWorldView->addChildToPanel( // help text is wrong for this button
  149. makeButton(291, std::bind(&CAdventureMapInterface::fworldViewScale2x,this), worldViewScale2xConfig, EShortcut::SELECT_INDEX_2), ACTIVATE | DEACTIVATE);
  150. config::ButtonInfo worldViewScale4xConfig = config::ButtonInfo();
  151. worldViewScale4xConfig.defName = "VWMAG4.DEF";
  152. worldViewScale4xConfig.x = GH.screenDimensions().x- 191 + 126;
  153. worldViewScale4xConfig.y = 23 + 195;
  154. worldViewScale4xConfig.playerColoured = false;
  155. panelWorldView->addChildToPanel( // help text is wrong for this button
  156. makeButton(291, std::bind(&CAdventureMapInterface::fworldViewScale4x,this), worldViewScale4xConfig, EShortcut::SELECT_INDEX_4), ACTIVATE | DEACTIVATE);
  157. config::ButtonInfo worldViewUndergroundConfig = config::ButtonInfo();
  158. worldViewUndergroundConfig.defName = "IAM010.DEF";
  159. worldViewUndergroundConfig.additionalDefs.push_back("IAM003.DEF");
  160. worldViewUndergroundConfig.x = GH.screenDimensions().x - 115;
  161. worldViewUndergroundConfig.y = 343 + 195;
  162. worldViewUndergroundConfig.playerColoured = true;
  163. worldViewUnderground = makeButton(294, std::bind(&CAdventureMapInterface::fswitchLevel,this), worldViewUndergroundConfig, EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL);
  164. panelWorldView->addChildColorableButton(worldViewUnderground);
  165. onCurrentPlayerChanged(LOCPLINT->playerID);
  166. int iconColorMultiplier = currentPlayerID.getNum() * 19;
  167. int wvLeft = heroList->pos.x - 2; // TODO correct drawing position
  168. //int wvTop = 195;
  169. for (int i = 0; i < 5; ++i)
  170. {
  171. panelWorldView->addChildIcon(std::pair<int, Point>(i, Point(5, 58 + i * 20)), iconColorMultiplier);
  172. panelWorldView->addChildToPanel(std::make_shared<CLabel>(wvLeft + 45, 263 + i * 20, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT,
  173. Colors::WHITE, CGI->generaltexth->allTexts[612 + i]));
  174. }
  175. for (int i = 0; i < 7; ++i)
  176. {
  177. panelWorldView->addChildIcon(std::pair<int, Point>(i + 5, Point(5, 182 + i * 20)), iconColorMultiplier);
  178. panelWorldView->addChildIcon(std::pair<int, Point>(i + 12, Point(160, 182 + i * 20)), iconColorMultiplier);
  179. panelWorldView->addChildToPanel(std::make_shared<CLabel>(wvLeft + 45, 387 + i * 20, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT,
  180. Colors::WHITE, CGI->generaltexth->allTexts[619 + i]));
  181. }
  182. panelWorldView->addChildToPanel(std::make_shared<CLabel>(wvLeft + 5, 367, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT,
  183. Colors::WHITE, CGI->generaltexth->allTexts[617]));
  184. panelWorldView->addChildToPanel(std::make_shared<CLabel>(wvLeft + 45, 367, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT,
  185. Colors::WHITE, CGI->generaltexth->allTexts[618]));
  186. activeMapPanel = panelMain;
  187. exitWorldView();
  188. underground->block(!CGI->mh->getMap()->twoLevel);
  189. questlog->block(!CGI->mh->getMap()->quests.size());
  190. worldViewUnderground->block(!CGI->mh->getMap()->twoLevel);
  191. }
  192. void CAdventureMapInterface::fshowOverview()
  193. {
  194. GH.pushIntT<CKingdomInterface>();
  195. }
  196. void CAdventureMapInterface::fworldViewBack()
  197. {
  198. exitWorldView();
  199. auto hero = LOCPLINT->localState->getCurrentHero();
  200. if (hero)
  201. centerOnObject(hero);
  202. }
  203. void CAdventureMapInterface::fworldViewScale1x()
  204. {
  205. // TODO set corresponding scale button to "selected" mode
  206. openWorldView(7);
  207. }
  208. void CAdventureMapInterface::fworldViewScale2x()
  209. {
  210. openWorldView(11);
  211. }
  212. void CAdventureMapInterface::fworldViewScale4x()
  213. {
  214. openWorldView(16);
  215. }
  216. void CAdventureMapInterface::fswitchLevel()
  217. {
  218. // with support for future multi-level maps :)
  219. int maxLevels = CGI->mh->getMap()->levels();
  220. if (maxLevels < 2)
  221. return;
  222. terrain->onMapLevelSwitched();
  223. }
  224. void CAdventureMapInterface::onMapViewMoved(const Rect & visibleArea, int mapLevel)
  225. {
  226. underground->setIndex(mapLevel, true);
  227. underground->redraw();
  228. worldViewUnderground->setIndex(mapLevel, true);
  229. worldViewUnderground->redraw();
  230. minimap->onMapViewMoved(visibleArea, mapLevel);
  231. }
  232. void CAdventureMapInterface::onAudioResumed()
  233. {
  234. mapAudio->onAudioResumed();
  235. }
  236. void CAdventureMapInterface::onAudioPaused()
  237. {
  238. mapAudio->onAudioPaused();
  239. }
  240. void CAdventureMapInterface::fshowQuestlog()
  241. {
  242. LOCPLINT->showQuestLog();
  243. }
  244. void CAdventureMapInterface::fsleepWake()
  245. {
  246. const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
  247. if (!h)
  248. return;
  249. bool newSleep = !LOCPLINT->localState->isHeroSleeping(h);
  250. if (newSleep)
  251. LOCPLINT->localState->setHeroAsleep(h);
  252. else
  253. LOCPLINT->localState->setHeroAwaken(h);
  254. onHeroChanged(h);
  255. if (newSleep)
  256. fnextHero();
  257. // redraw to update the image of sleep/wake button
  258. panelMain->redraw();
  259. }
  260. void CAdventureMapInterface::fmoveHero()
  261. {
  262. const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
  263. if (!h || !LOCPLINT->localState->hasPath(h) || CGI->mh->hasOngoingAnimations())
  264. return;
  265. LOCPLINT->moveHero(h, LOCPLINT->localState->getPath(h));
  266. }
  267. void CAdventureMapInterface::fshowSpellbok()
  268. {
  269. if (!LOCPLINT->localState->getCurrentHero()) //checking necessary values
  270. return;
  271. centerOnObject(LOCPLINT->localState->getCurrentHero());
  272. GH.pushIntT<CSpellWindow>(LOCPLINT->localState->getCurrentHero(), LOCPLINT, false);
  273. }
  274. void CAdventureMapInterface::fadventureOPtions()
  275. {
  276. GH.pushIntT<CAdventureOptions>();
  277. }
  278. void CAdventureMapInterface::fsystemOptions()
  279. {
  280. GH.pushIntT<SettingsMainWindow>();
  281. }
  282. void CAdventureMapInterface::fnextHero()
  283. {
  284. const auto * currHero = LOCPLINT->localState->getCurrentHero();
  285. const auto * nextHero = LOCPLINT->localState->getNextWanderingHero(currHero);
  286. if (nextHero)
  287. {
  288. LOCPLINT->localState->setSelection(nextHero);
  289. centerOnObject(nextHero);
  290. }
  291. }
  292. void CAdventureMapInterface::fendTurn()
  293. {
  294. if(!LOCPLINT->makingTurn)
  295. return;
  296. if(settings["adventure"]["heroReminder"].Bool())
  297. {
  298. for(auto hero : LOCPLINT->localState->getWanderingHeroes())
  299. {
  300. if(!LOCPLINT->localState->isHeroSleeping(hero) && hero->movement > 0)
  301. {
  302. // Only show hero reminder if conditions met:
  303. // - There still movement points
  304. // - Hero don't have a path or there not points for first step on path
  305. LOCPLINT->localState->verifyPath(hero);
  306. if(!LOCPLINT->localState->hasPath(hero))
  307. {
  308. LOCPLINT->showYesNoDialog( CGI->generaltexth->allTexts[55], std::bind(&CAdventureMapInterface::endingTurn, this), nullptr );
  309. return;
  310. }
  311. auto path = LOCPLINT->localState->getPath(hero);
  312. if (path.nodes.size() < 2 || path.nodes[path.nodes.size() - 2].turns)
  313. {
  314. LOCPLINT->showYesNoDialog( CGI->generaltexth->allTexts[55], std::bind(&CAdventureMapInterface::endingTurn, this), nullptr );
  315. return;
  316. }
  317. }
  318. }
  319. }
  320. endingTurn();
  321. }
  322. void CAdventureMapInterface::updateButtons()
  323. {
  324. const auto * hero = LOCPLINT->localState->getCurrentHero();
  325. sleepWake->block(!hero);
  326. spellbook->block(!hero);
  327. moveHero->block(!hero || !LOCPLINT->localState->hasPath(hero) || hero->movement == 0);
  328. const auto * nextSuitableHero = LOCPLINT->localState->getNextWanderingHero(hero);
  329. nextHero->block(nextSuitableHero == nullptr);
  330. if(hero)
  331. {
  332. bool state = LOCPLINT->localState->isHeroSleeping(hero);
  333. sleepWake->setIndex(state ? 1 : 0, true);
  334. sleepWake->redraw();
  335. }
  336. }
  337. void CAdventureMapInterface::onHeroMovementStarted(const CGHeroInstance * hero)
  338. {
  339. infoBar->popAll();
  340. infoBar->showSelection();
  341. }
  342. void CAdventureMapInterface::onHeroChanged(const CGHeroInstance *h)
  343. {
  344. heroList->update(h);
  345. if (h && h == LOCPLINT->localState->getCurrentHero() && !infoBar->showingComponents())
  346. infoBar->showSelection();
  347. updateButtons();
  348. }
  349. void CAdventureMapInterface::onTownChanged(const CGTownInstance * town)
  350. {
  351. townList->update(town);
  352. if (town && town == LOCPLINT->localState->getCurrentTown() && !infoBar->showingComponents())
  353. infoBar->showSelection();
  354. }
  355. void CAdventureMapInterface::showInfoBoxMessage(const std::vector<Component> & components, std::string message, int timer)
  356. {
  357. infoBar->pushComponents(components, message, timer);
  358. }
  359. void CAdventureMapInterface::activate()
  360. {
  361. CIntObject::activate();
  362. if (!(active & KEYBOARD))
  363. CIntObject::activate(KEYBOARD);
  364. screenBuf = screen;
  365. GH.statusbar = statusbar;
  366. if(LOCPLINT)
  367. {
  368. LOCPLINT->cingconsole->activate();
  369. LOCPLINT->cingconsole->pos = this->pos;
  370. }
  371. if(state != EGameState::ENEMY_TURN && state != EGameState::HOTSEAT_WAIT)
  372. {
  373. assert(state == EGameState::MAKING_TURN);
  374. activeMapPanel->activate();
  375. if (state == EGameState::MAKING_TURN)
  376. {
  377. heroList->activate();
  378. townList->activate();
  379. infoBar->activate();
  380. }
  381. minimap->activate();
  382. terrain->activate();
  383. statusbar->activate();
  384. GH.fakeMouseMove(); //to restore the cursor
  385. }
  386. }
  387. void CAdventureMapInterface::deactivate()
  388. {
  389. CIntObject::deactivate();
  390. if(state != EGameState::ENEMY_TURN && state != EGameState::HOTSEAT_WAIT)
  391. {
  392. assert(state == EGameState::MAKING_TURN);
  393. CCS->curh->set(Cursor::Map::POINTER);
  394. activeMapPanel->deactivate();
  395. if (state == EGameState::MAKING_TURN)
  396. {
  397. heroList->deactivate();
  398. townList->deactivate();
  399. infoBar->deactivate();
  400. }
  401. minimap->deactivate();
  402. terrain->deactivate();
  403. statusbar->deactivate();
  404. }
  405. }
  406. void CAdventureMapInterface::showAll(SDL_Surface * to)
  407. {
  408. bg->draw(to, 0, 0);
  409. // if(state != EGameState::MAKING_TURN)
  410. // return;
  411. heroList->showAll(to);
  412. townList->showAll(to);
  413. infoBar->showAll(to);
  414. activeMapPanel->showAll(to);
  415. minimap->showAll(to);
  416. terrain->showAll(to);
  417. show(to);
  418. resdatabar->showAll(to);
  419. statusbar->show(to);
  420. LOCPLINT->cingconsole->show(to);
  421. }
  422. void CAdventureMapInterface::show(SDL_Surface * to)
  423. {
  424. // if(state != EGameState::MAKING_TURN)
  425. // return;
  426. handleMapScrollingUpdate();
  427. for(int i = 0; i < 4; i++)
  428. {
  429. if(settings["session"]["spectate"].Bool())
  430. gems[i]->setFrame(PlayerColor(1).getNum());
  431. else
  432. gems[i]->setFrame(LOCPLINT->playerID.getNum());
  433. }
  434. minimap->show(to);
  435. terrain->show(to);
  436. for(int i = 0; i < 4; i++)
  437. gems[i]->showAll(to);
  438. LOCPLINT->cingconsole->show(to);
  439. infoBar->show(to);
  440. statusbar->showAll(to);
  441. }
  442. void CAdventureMapInterface::handleMapScrollingUpdate()
  443. {
  444. /// Width of window border, in pixels, that triggers map scrolling
  445. static constexpr uint32_t borderScrollWidth = 15;
  446. uint32_t timePassed = GH.mainFPSmng->getElapsedMilliseconds();
  447. uint32_t scrollSpeedPixels = settings["adventure"]["scrollSpeedPixels"].Float();
  448. uint32_t scrollDistance = scrollSpeedPixels * timePassed / 1000;
  449. bool scrollingActive = !GH.isKeyboardCtrlDown() && isActive() && state == EGameState::MAKING_TURN;
  450. Point cursorPosition = GH.getCursorPosition();
  451. Point scrollDirection;
  452. if (cursorPosition.x < borderScrollWidth)
  453. scrollDirection.x = -1;
  454. if (cursorPosition.x > GH.screenDimensions().x - borderScrollWidth)
  455. scrollDirection.x = +1;
  456. if (cursorPosition.y < borderScrollWidth)
  457. scrollDirection.y = -1;
  458. if (cursorPosition.y > GH.screenDimensions().y - borderScrollWidth)
  459. scrollDirection.y = +1;
  460. Point scrollDelta = scrollDirection * scrollDistance;
  461. if (scrollingActive && scrollDelta != Point(0,0))
  462. terrain->onMapScrolled(scrollDelta);
  463. if (scrollDelta == Point(0,0) && !scrollingCursorSet)
  464. return;
  465. if(scrollDelta.x > 0)
  466. {
  467. if(scrollDelta.y < 0)
  468. CCS->curh->set(Cursor::Map::SCROLL_NORTHEAST);
  469. if(scrollDelta.y > 0)
  470. CCS->curh->set(Cursor::Map::SCROLL_SOUTHEAST);
  471. if(scrollDelta.y == 0)
  472. CCS->curh->set(Cursor::Map::SCROLL_EAST);
  473. }
  474. if(scrollDelta.x < 0)
  475. {
  476. if(scrollDelta.y < 0)
  477. CCS->curh->set(Cursor::Map::SCROLL_NORTHWEST);
  478. if(scrollDelta.y > 0)
  479. CCS->curh->set(Cursor::Map::SCROLL_SOUTHWEST);
  480. if(scrollDelta.y == 0)
  481. CCS->curh->set(Cursor::Map::SCROLL_WEST);
  482. }
  483. if (scrollDelta.x == 0)
  484. {
  485. if(scrollDelta.y < 0)
  486. CCS->curh->set(Cursor::Map::SCROLL_NORTH);
  487. if(scrollDelta.y > 0)
  488. CCS->curh->set(Cursor::Map::SCROLL_SOUTH);
  489. if(scrollDelta.y == 0)
  490. CCS->curh->set(Cursor::Map::POINTER);
  491. }
  492. scrollingCursorSet = scrollDelta != Point(0,0);
  493. }
  494. void CAdventureMapInterface::centerOnTile(int3 on)
  495. {
  496. terrain->onCenteredTile(on);
  497. }
  498. void CAdventureMapInterface::centerOnObject(const CGObjectInstance * obj)
  499. {
  500. terrain->onCenteredObject(obj);
  501. }
  502. void CAdventureMapInterface::keyPressed(EShortcut key)
  503. {
  504. if (state != EGameState::MAKING_TURN)
  505. return;
  506. //fake mouse use to trigger onTileHovered()
  507. GH.fakeMouseMove();
  508. const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); //selected hero
  509. const CGTownInstance *t = LOCPLINT->localState->getCurrentTown(); //selected town
  510. switch(key)
  511. {
  512. case EShortcut::ADVENTURE_THIEVES_GUILD:
  513. if(GH.topInt()->type & BLOCK_ADV_HOTKEYS)
  514. return;
  515. {
  516. //find first town with tavern
  517. auto itr = range::find_if(LOCPLINT->localState->getOwnedTowns(), [](const CGTownInstance * town)
  518. {
  519. return town->hasBuilt(BuildingID::TAVERN);
  520. });
  521. if(itr != LOCPLINT->localState->getOwnedTowns().end())
  522. LOCPLINT->showThievesGuildWindow(*itr);
  523. else
  524. LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithTavern"));
  525. }
  526. return;
  527. case EShortcut::ADVENTURE_VIEW_SCENARIO:
  528. if(isActive())
  529. CAdventureOptions::showScenarioInfo();
  530. return;
  531. case EShortcut::GAME_SAVE_GAME:
  532. if(isActive())
  533. GH.pushIntT<CSavingScreen>();
  534. return;
  535. case EShortcut::GAME_LOAD_GAME:
  536. if(isActive())
  537. LOCPLINT->proposeLoadingGame();
  538. return;
  539. case EShortcut::ADVENTURE_DIG_GRAIL:
  540. {
  541. if(h && isActive() && LOCPLINT->makingTurn)
  542. LOCPLINT->tryDiggging(h);
  543. return;
  544. }
  545. case EShortcut::ADVENTURE_VIEW_PUZZLE:
  546. if(isActive())
  547. LOCPLINT->showPuzzleMap();
  548. return;
  549. case EShortcut::ADVENTURE_VIEW_WORLD:
  550. if(isActive())
  551. LOCPLINT->viewWorldMap();
  552. return;
  553. case EShortcut::GAME_RESTART_GAME:
  554. if(isActive() && GH.isKeyboardCtrlDown())
  555. {
  556. LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.adventureMap.confirmRestartGame"),
  557. [](){ GH.pushUserEvent(EUserEvent::RESTART_GAME); }, nullptr);
  558. }
  559. return;
  560. case EShortcut::ADVENTURE_VISIT_OBJECT: //space - try to revisit current object with selected hero
  561. {
  562. if(!isActive())
  563. return;
  564. if(h)
  565. {
  566. LOCPLINT->cb->moveHero(h,h->pos);
  567. }
  568. }
  569. return;
  570. case EShortcut::ADVENTURE_VIEW_SELECTED:
  571. {
  572. if(!isActive() || !LOCPLINT->localState->getCurrentArmy())
  573. return;
  574. if(h)
  575. LOCPLINT->openHeroWindow(h);
  576. else if(t)
  577. LOCPLINT->openTownWindow(t);
  578. return;
  579. }
  580. case EShortcut::GLOBAL_CANCEL:
  581. {
  582. //FIXME: this case is never executed since AdvMapInt is disabled while in spellcasting mode
  583. if(!isActive() || GH.topInt().get() != this || !spellBeingCasted)
  584. return;
  585. abortCastingMode();
  586. return;
  587. }
  588. case EShortcut::GAME_OPEN_MARKETPLACE:
  589. {
  590. //act on key down if marketplace windows is not already opened
  591. if(GH.topInt()->type & BLOCK_ADV_HOTKEYS)
  592. return;
  593. if(GH.isKeyboardCtrlDown()) //CTRL + T => open marketplace
  594. {
  595. //check if we have any marketplace
  596. const CGTownInstance *townWithMarket = nullptr;
  597. for(const CGTownInstance *t : LOCPLINT->cb->getTownsInfo())
  598. {
  599. if(t->hasBuilt(BuildingID::MARKETPLACE))
  600. {
  601. townWithMarket = t;
  602. break;
  603. }
  604. }
  605. if(townWithMarket) //if any town has marketplace, open window
  606. GH.pushIntT<CMarketplaceWindow>(townWithMarket);
  607. else //if not - complain
  608. LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket"));
  609. }
  610. case EShortcut::ADVENTURE_NEXT_TOWN:
  611. if(isActive() && !GH.isKeyboardCtrlDown()) //no ctrl, advmapint is on the top => switch to town
  612. {
  613. townList->selectNext();
  614. }
  615. return;
  616. }
  617. case EShortcut::ADVENTURE_MOVE_HERO_SW: return hotkeyMoveHeroDirectional({-1, +1});
  618. case EShortcut::ADVENTURE_MOVE_HERO_SS: return hotkeyMoveHeroDirectional({ 0, +1});
  619. case EShortcut::ADVENTURE_MOVE_HERO_SE: return hotkeyMoveHeroDirectional({+1, +1});
  620. case EShortcut::ADVENTURE_MOVE_HERO_WW: return hotkeyMoveHeroDirectional({-1, 0});
  621. case EShortcut::ADVENTURE_MOVE_HERO_EE: return hotkeyMoveHeroDirectional({+1, 0});
  622. case EShortcut::ADVENTURE_MOVE_HERO_NW: return hotkeyMoveHeroDirectional({-1, -1});
  623. case EShortcut::ADVENTURE_MOVE_HERO_NN: return hotkeyMoveHeroDirectional({ 0, -1});
  624. case EShortcut::ADVENTURE_MOVE_HERO_NE: return hotkeyMoveHeroDirectional({+1, -1});
  625. }
  626. }
  627. void CAdventureMapInterface::hotkeyMoveHeroDirectional(Point direction)
  628. {
  629. const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); //selected hero
  630. if(!h || !isActive())
  631. return;
  632. if (CGI->mh->hasOngoingAnimations())
  633. return;
  634. if(direction == Point(0,0))
  635. {
  636. centerOnObject(h);
  637. return;
  638. }
  639. int3 dst = h->visitablePos() + int3(direction.x, direction.y, 0);
  640. if (!CGI->mh->isInMap((dst)))
  641. return;
  642. if ( !LOCPLINT->localState->setPath(h, dst))
  643. return;
  644. const CGPath & path = LOCPLINT->localState->getPath(h);
  645. if (path.nodes.size() > 2)
  646. onHeroChanged(h);
  647. else
  648. if(!path.nodes[0].turns)
  649. LOCPLINT->moveHero(h, path);
  650. }
  651. void CAdventureMapInterface::onSelectionChanged(const CArmedInstance *sel)
  652. {
  653. assert(sel);
  654. infoBar->popAll();
  655. mapAudio->onSelectionChanged(sel);
  656. bool centerView = !settings["session"]["autoSkip"].Bool();
  657. if (centerView)
  658. centerOnObject(sel);
  659. if(sel->ID==Obj::TOWN)
  660. {
  661. auto town = dynamic_cast<const CGTownInstance*>(sel);
  662. infoBar->showTownSelection(town);
  663. townList->select(town);
  664. heroList->select(nullptr);
  665. onHeroChanged(nullptr);
  666. }
  667. else //hero selected
  668. {
  669. auto hero = dynamic_cast<const CGHeroInstance*>(sel);
  670. infoBar->showHeroSelection(hero);
  671. heroList->select(hero);
  672. townList->select(nullptr);
  673. LOCPLINT->localState->verifyPath(hero);
  674. onHeroChanged(hero);
  675. }
  676. updateButtons();
  677. townList->redraw();
  678. heroList->redraw();
  679. }
  680. bool CAdventureMapInterface::isActive()
  681. {
  682. return active & ~CIntObject::KEYBOARD;
  683. }
  684. void CAdventureMapInterface::onMapTilesChanged(boost::optional<std::unordered_set<int3>> positions)
  685. {
  686. if (positions)
  687. minimap->updateTiles(*positions);
  688. else
  689. minimap->update();
  690. }
  691. void CAdventureMapInterface::onHotseatWaitStarted(PlayerColor playerID)
  692. {
  693. onCurrentPlayerChanged(playerID);
  694. state = EGameState::HOTSEAT_WAIT;
  695. }
  696. void CAdventureMapInterface::onEnemyTurnStarted(PlayerColor playerID)
  697. {
  698. if(settings["session"]["spectate"].Bool())
  699. return;
  700. adjustActiveness(true);
  701. mapAudio->onEnemyTurnStarted();
  702. minimap->setAIRadar(true);
  703. infoBar->startEnemyTurn(LOCPLINT->cb->getCurrentPlayer());
  704. minimap->showAll(screen);//force refresh on inactive object
  705. infoBar->showAll(screen);//force refresh on inactive object
  706. }
  707. void CAdventureMapInterface::adjustActiveness(bool aiTurnStart)
  708. {
  709. bool wasActive = isActive();
  710. if(wasActive)
  711. deactivate();
  712. if (aiTurnStart)
  713. state = EGameState::ENEMY_TURN;
  714. else
  715. state = EGameState::MAKING_TURN;
  716. if(wasActive)
  717. activate();
  718. }
  719. void CAdventureMapInterface::onCurrentPlayerChanged(PlayerColor playerID)
  720. {
  721. LOCPLINT->localState->setSelection(nullptr);
  722. if (playerID == currentPlayerID)
  723. return;
  724. currentPlayerID = playerID;
  725. bg->playerColored(currentPlayerID);
  726. panelMain->setPlayerColor(currentPlayerID);
  727. panelWorldView->setPlayerColor(currentPlayerID);
  728. panelWorldView->recolorIcons(currentPlayerID, currentPlayerID.getNum() * 19);
  729. resdatabar->colorize(currentPlayerID);
  730. }
  731. void CAdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID)
  732. {
  733. onCurrentPlayerChanged(playerID);
  734. state = EGameState::MAKING_TURN;
  735. if(LOCPLINT->cb->getCurrentPlayer() == LOCPLINT->playerID
  736. || settings["session"]["spectate"].Bool())
  737. {
  738. adjustActiveness(false);
  739. minimap->setAIRadar(false);
  740. infoBar->showSelection();
  741. }
  742. heroList->update();
  743. townList->update();
  744. const CGHeroInstance * heroToSelect = nullptr;
  745. // find first non-sleeping hero
  746. for (auto hero : LOCPLINT->localState->getWanderingHeroes())
  747. {
  748. if (!LOCPLINT->localState->isHeroSleeping(hero))
  749. {
  750. heroToSelect = hero;
  751. break;
  752. }
  753. }
  754. //select first hero if available.
  755. if (heroToSelect != nullptr)
  756. {
  757. LOCPLINT->localState->setSelection(heroToSelect);
  758. }
  759. else if (LOCPLINT->localState->getOwnedTowns().size())
  760. {
  761. LOCPLINT->localState->setSelection(LOCPLINT->localState->getOwnedTown(0));
  762. }
  763. else
  764. {
  765. LOCPLINT->localState->setSelection(LOCPLINT->localState->getWanderingHero(0));
  766. }
  767. //show new day animation and sound on infobar
  768. infoBar->showDate();
  769. onHeroChanged(nullptr);
  770. showAll(screen);
  771. mapAudio->onPlayerTurnStarted();
  772. if(settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown())
  773. {
  774. if(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt().get()))
  775. iw->close();
  776. endingTurn();
  777. }
  778. }
  779. void CAdventureMapInterface::endingTurn()
  780. {
  781. if(settings["session"]["spectate"].Bool())
  782. return;
  783. LOCPLINT->makingTurn = false;
  784. LOCPLINT->cb->endTurn();
  785. mapAudio->onPlayerTurnEnded();
  786. }
  787. const CGObjectInstance* CAdventureMapInterface::getActiveObject(const int3 &mapPos)
  788. {
  789. std::vector < const CGObjectInstance * > bobjs = LOCPLINT->cb->getBlockingObjs(mapPos); //blocking objects at tile
  790. if (bobjs.empty())
  791. return nullptr;
  792. return *boost::range::max_element(bobjs, &CMapHandler::compareObjectBlitOrder);
  793. /*
  794. if (bobjs.back()->ID == Obj::HERO)
  795. return bobjs.back();
  796. else
  797. return bobjs.front();*/
  798. }
  799. void CAdventureMapInterface::onTileLeftClicked(const int3 &mapPos)
  800. {
  801. if(state != EGameState::MAKING_TURN)
  802. return;
  803. //FIXME: this line breaks H3 behavior for Dimension Door
  804. if(!LOCPLINT->cb->isVisible(mapPos))
  805. return;
  806. if(!LOCPLINT->makingTurn)
  807. return;
  808. const TerrainTile *tile = LOCPLINT->cb->getTile(mapPos);
  809. const CGObjectInstance *topBlocking = getActiveObject(mapPos);
  810. int3 selPos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
  811. if(spellBeingCasted)
  812. {
  813. if (!isInScreenRange(selPos, mapPos))
  814. return;
  815. const TerrainTile *heroTile = LOCPLINT->cb->getTile(selPos);
  816. switch(spellBeingCasted->id)
  817. {
  818. case SpellID::SCUTTLE_BOAT: //Scuttle Boat
  819. if(topBlocking && topBlocking->ID == Obj::BOAT)
  820. leaveCastingMode(mapPos);
  821. break;
  822. case SpellID::DIMENSION_DOOR:
  823. if(!tile || tile->isClear(heroTile))
  824. leaveCastingMode(mapPos);
  825. break;
  826. }
  827. return;
  828. }
  829. //check if we can select this object
  830. bool canSelect = topBlocking && topBlocking->ID == Obj::HERO && topBlocking->tempOwner == LOCPLINT->playerID;
  831. canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner);
  832. bool isHero = false;
  833. if(LOCPLINT->localState->getCurrentArmy()->ID != Obj::HERO) //hero is not selected (presumably town)
  834. {
  835. if(LOCPLINT->localState->getCurrentArmy() == topBlocking) //selected town clicked
  836. LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(topBlocking));
  837. else if(canSelect)
  838. LOCPLINT->localState->setSelection(static_cast<const CArmedInstance*>(topBlocking));
  839. }
  840. else if(const CGHeroInstance * currentHero = LOCPLINT->localState->getCurrentHero()) //hero is selected
  841. {
  842. isHero = true;
  843. const CGPathNode *pn = LOCPLINT->cb->getPathsInfo(currentHero)->getPathInfo(mapPos);
  844. if(currentHero == topBlocking) //clicked selected hero
  845. {
  846. LOCPLINT->openHeroWindow(currentHero);
  847. return;
  848. }
  849. else if(canSelect && pn->turns == 255 ) //selectable object at inaccessible tile
  850. {
  851. LOCPLINT->localState->setSelection(static_cast<const CArmedInstance*>(topBlocking));
  852. return;
  853. }
  854. else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
  855. {
  856. if(LOCPLINT->localState->hasPath(currentHero) &&
  857. LOCPLINT->localState->getPath(currentHero).endPos() == mapPos)//we'll be moving
  858. {
  859. if(!CGI->mh->hasOngoingAnimations())
  860. LOCPLINT->moveHero(currentHero, LOCPLINT->localState->getPath(currentHero));
  861. return;
  862. }
  863. else //remove old path and find a new one if we clicked on accessible tile
  864. {
  865. LOCPLINT->localState->setPath(currentHero, mapPos);
  866. onHeroChanged(currentHero);
  867. }
  868. }
  869. } //end of hero is selected "case"
  870. else
  871. {
  872. throw std::runtime_error("Nothing is selected...");
  873. }
  874. const auto shipyard = ourInaccessibleShipyard(topBlocking);
  875. if(isHero && shipyard != nullptr)
  876. {
  877. LOCPLINT->showShipyardDialogOrProblemPopup(shipyard);
  878. }
  879. }
  880. void CAdventureMapInterface::onTileHovered(const int3 &mapPos)
  881. {
  882. if(state != EGameState::MAKING_TURN)
  883. return;
  884. //may occur just at the start of game (fake move before full intiialization)
  885. if(!LOCPLINT->localState->getCurrentArmy())
  886. return;
  887. if(!LOCPLINT->cb->isVisible(mapPos))
  888. {
  889. CCS->curh->set(Cursor::Map::POINTER);
  890. statusbar->clear();
  891. return;
  892. }
  893. auto objRelations = PlayerRelations::ALLIES;
  894. const CGObjectInstance *objAtTile = getActiveObject(mapPos);
  895. if(objAtTile)
  896. {
  897. objRelations = LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner);
  898. std::string text = LOCPLINT->localState->getCurrentHero() ? objAtTile->getHoverText(LOCPLINT->localState->getCurrentHero()) : objAtTile->getHoverText(LOCPLINT->playerID);
  899. boost::replace_all(text,"\n"," ");
  900. statusbar->write(text);
  901. }
  902. else
  903. {
  904. std::string hlp = CGI->mh->getTerrainDescr(mapPos, false);
  905. statusbar->write(hlp);
  906. }
  907. if(spellBeingCasted)
  908. {
  909. switch(spellBeingCasted->id)
  910. {
  911. case SpellID::SCUTTLE_BOAT:
  912. {
  913. int3 hpos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
  914. if(objAtTile && objAtTile->ID == Obj::BOAT && isInScreenRange(hpos, mapPos))
  915. CCS->curh->set(Cursor::Map::SCUTTLE_BOAT);
  916. else
  917. CCS->curh->set(Cursor::Map::POINTER);
  918. return;
  919. }
  920. case SpellID::DIMENSION_DOOR:
  921. {
  922. const TerrainTile * t = LOCPLINT->cb->getTile(mapPos, false);
  923. int3 hpos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
  924. if((!t || t->isClear(LOCPLINT->cb->getTile(hpos))) && isInScreenRange(hpos, mapPos))
  925. CCS->curh->set(Cursor::Map::TELEPORT);
  926. else
  927. CCS->curh->set(Cursor::Map::POINTER);
  928. return;
  929. }
  930. }
  931. }
  932. if(LOCPLINT->localState->getCurrentArmy()->ID == Obj::TOWN)
  933. {
  934. if(objAtTile)
  935. {
  936. if(objAtTile->ID == Obj::TOWN && objRelations != PlayerRelations::ENEMIES)
  937. CCS->curh->set(Cursor::Map::TOWN);
  938. else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
  939. CCS->curh->set(Cursor::Map::HERO);
  940. else
  941. CCS->curh->set(Cursor::Map::POINTER);
  942. }
  943. else
  944. CCS->curh->set(Cursor::Map::POINTER);
  945. }
  946. else if(const CGHeroInstance * hero = LOCPLINT->localState->getCurrentHero())
  947. {
  948. std::array<Cursor::Map, 4> cursorMove = { Cursor::Map::T1_MOVE, Cursor::Map::T2_MOVE, Cursor::Map::T3_MOVE, Cursor::Map::T4_MOVE, };
  949. std::array<Cursor::Map, 4> cursorAttack = { Cursor::Map::T1_ATTACK, Cursor::Map::T2_ATTACK, Cursor::Map::T3_ATTACK, Cursor::Map::T4_ATTACK, };
  950. std::array<Cursor::Map, 4> cursorSail = { Cursor::Map::T1_SAIL, Cursor::Map::T2_SAIL, Cursor::Map::T3_SAIL, Cursor::Map::T4_SAIL, };
  951. std::array<Cursor::Map, 4> cursorDisembark = { Cursor::Map::T1_DISEMBARK, Cursor::Map::T2_DISEMBARK, Cursor::Map::T3_DISEMBARK, Cursor::Map::T4_DISEMBARK, };
  952. std::array<Cursor::Map, 4> cursorExchange = { Cursor::Map::T1_EXCHANGE, Cursor::Map::T2_EXCHANGE, Cursor::Map::T3_EXCHANGE, Cursor::Map::T4_EXCHANGE, };
  953. std::array<Cursor::Map, 4> cursorVisit = { Cursor::Map::T1_VISIT, Cursor::Map::T2_VISIT, Cursor::Map::T3_VISIT, Cursor::Map::T4_VISIT, };
  954. 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, };
  955. const CGPathNode * pathNode = LOCPLINT->cb->getPathsInfo(hero)->getPathInfo(mapPos);
  956. assert(pathNode);
  957. if((GH.isKeyboardAltDown() || settings["gameTweaks"]["forceMovementInfo"].Bool()) && pathNode->reachable()) //overwrite status bar text with movement info
  958. {
  959. showMoveDetailsInStatusbar(*hero, *pathNode);
  960. }
  961. int turns = pathNode->turns;
  962. vstd::amin(turns, 3);
  963. switch(pathNode->action)
  964. {
  965. case CGPathNode::NORMAL:
  966. case CGPathNode::TELEPORT_NORMAL:
  967. if(pathNode->layer == EPathfindingLayer::LAND)
  968. CCS->curh->set(cursorMove[turns]);
  969. else
  970. CCS->curh->set(cursorSailVisit[turns]);
  971. break;
  972. case CGPathNode::VISIT:
  973. case CGPathNode::BLOCKING_VISIT:
  974. case CGPathNode::TELEPORT_BLOCKING_VISIT:
  975. if(objAtTile && objAtTile->ID == Obj::HERO)
  976. {
  977. if(LOCPLINT->localState->getCurrentArmy() == objAtTile)
  978. CCS->curh->set(Cursor::Map::HERO);
  979. else
  980. CCS->curh->set(cursorExchange[turns]);
  981. }
  982. else if(pathNode->layer == EPathfindingLayer::LAND)
  983. CCS->curh->set(cursorVisit[turns]);
  984. else
  985. CCS->curh->set(cursorSailVisit[turns]);
  986. break;
  987. case CGPathNode::BATTLE:
  988. case CGPathNode::TELEPORT_BATTLE:
  989. CCS->curh->set(cursorAttack[turns]);
  990. break;
  991. case CGPathNode::EMBARK:
  992. CCS->curh->set(cursorSail[turns]);
  993. break;
  994. case CGPathNode::DISEMBARK:
  995. CCS->curh->set(cursorDisembark[turns]);
  996. break;
  997. default:
  998. if(objAtTile && objRelations != PlayerRelations::ENEMIES)
  999. {
  1000. if(objAtTile->ID == Obj::TOWN)
  1001. CCS->curh->set(Cursor::Map::TOWN);
  1002. else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
  1003. CCS->curh->set(Cursor::Map::HERO);
  1004. else
  1005. CCS->curh->set(Cursor::Map::POINTER);
  1006. }
  1007. else
  1008. CCS->curh->set(Cursor::Map::POINTER);
  1009. break;
  1010. }
  1011. }
  1012. if(ourInaccessibleShipyard(objAtTile))
  1013. {
  1014. CCS->curh->set(Cursor::Map::T1_SAIL);
  1015. }
  1016. }
  1017. void CAdventureMapInterface::showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode)
  1018. {
  1019. const int maxMovementPointsAtStartOfLastTurn = pathNode.turns > 0 ? hero.maxMovePoints(pathNode.layer == EPathfindingLayer::LAND) : hero.movement;
  1020. const int movementPointsLastTurnCost = maxMovementPointsAtStartOfLastTurn - pathNode.moveRemains;
  1021. const int remainingPointsAfterMove = pathNode.turns == 0 ? pathNode.moveRemains : 0;
  1022. std::string result = VLC->generaltexth->translate("vcmi.adventureMap", pathNode.turns > 0 ? "moveCostDetails" : "moveCostDetailsNoTurns");
  1023. boost::replace_first(result, "%TURNS", std::to_string(pathNode.turns));
  1024. boost::replace_first(result, "%POINTS", std::to_string(movementPointsLastTurnCost));
  1025. boost::replace_first(result, "%REMAINING", std::to_string(remainingPointsAfterMove));
  1026. statusbar->write(result);
  1027. }
  1028. void CAdventureMapInterface::onTileRightClicked(const int3 &mapPos)
  1029. {
  1030. if(state != EGameState::MAKING_TURN)
  1031. return;
  1032. if(spellBeingCasted)
  1033. {
  1034. abortCastingMode();
  1035. return;
  1036. }
  1037. if(!LOCPLINT->cb->isVisible(mapPos))
  1038. {
  1039. CRClickPopup::createAndPush(VLC->generaltexth->allTexts[61]); //Uncharted Territory
  1040. return;
  1041. }
  1042. const CGObjectInstance * obj = getActiveObject(mapPos);
  1043. if(!obj)
  1044. {
  1045. // Bare or undiscovered terrain
  1046. const TerrainTile * tile = LOCPLINT->cb->getTile(mapPos);
  1047. if(tile)
  1048. {
  1049. std::string hlp = CGI->mh->getTerrainDescr(mapPos, true);
  1050. CRClickPopup::createAndPush(hlp);
  1051. }
  1052. return;
  1053. }
  1054. CRClickPopup::createAndPush(obj, GH.getCursorPosition(), ETextAlignment::CENTER);
  1055. }
  1056. void CAdventureMapInterface::enterCastingMode(const CSpell * sp)
  1057. {
  1058. assert(sp->id == SpellID::SCUTTLE_BOAT || sp->id == SpellID::DIMENSION_DOOR);
  1059. spellBeingCasted = sp;
  1060. Settings config = settings.write["session"]["showSpellRange"];
  1061. config->Bool() = true;
  1062. deactivate();
  1063. terrain->activate();
  1064. GH.fakeMouseMove();
  1065. }
  1066. void CAdventureMapInterface::exitCastingMode()
  1067. {
  1068. assert(spellBeingCasted);
  1069. spellBeingCasted = nullptr;
  1070. terrain->deactivate();
  1071. activate();
  1072. Settings config = settings.write["session"]["showSpellRange"];
  1073. config->Bool() = false;
  1074. }
  1075. void CAdventureMapInterface::abortCastingMode()
  1076. {
  1077. exitCastingMode();
  1078. LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[731]); //Spell cancelled
  1079. }
  1080. void CAdventureMapInterface::leaveCastingMode(const int3 & dest)
  1081. {
  1082. SpellID id = spellBeingCasted->id;
  1083. exitCastingMode();
  1084. LOCPLINT->cb->castSpell(LOCPLINT->localState->getCurrentHero(), id, dest);
  1085. }
  1086. Rect CAdventureMapInterface::terrainAreaPixels() const
  1087. {
  1088. return terrain->pos;
  1089. }
  1090. const IShipyard * CAdventureMapInterface::ourInaccessibleShipyard(const CGObjectInstance *obj) const
  1091. {
  1092. const IShipyard *ret = IShipyard::castFrom(obj);
  1093. if(!ret ||
  1094. obj->tempOwner != currentPlayerID ||
  1095. (CCS->curh->get<Cursor::Map>() != Cursor::Map::T1_SAIL && CCS->curh->get<Cursor::Map>() != Cursor::Map::POINTER))
  1096. return nullptr;
  1097. return ret;
  1098. }
  1099. void CAdventureMapInterface::exitWorldView()
  1100. {
  1101. state = EGameState::MAKING_TURN;
  1102. panelMain->activate();
  1103. panelWorldView->deactivate();
  1104. activeMapPanel = panelMain;
  1105. townList->activate();
  1106. heroList->activate();
  1107. infoBar->activate();
  1108. redraw();
  1109. terrain->onViewMapActivated();
  1110. }
  1111. void CAdventureMapInterface::openWorldView(int tileSize)
  1112. {
  1113. state = EGameState::WORLD_VIEW;
  1114. panelMain->deactivate();
  1115. panelWorldView->activate();
  1116. activeMapPanel = panelWorldView;
  1117. townList->deactivate();
  1118. heroList->deactivate();
  1119. infoBar->showSelection(); // to prevent new day animation interfering world view mode
  1120. infoBar->deactivate();
  1121. redraw();
  1122. terrain->onViewWorldActivated(tileSize);
  1123. }
  1124. void CAdventureMapInterface::openWorldView()
  1125. {
  1126. openWorldView(11);
  1127. }
  1128. void CAdventureMapInterface::openWorldView(const std::vector<ObjectPosInfo>& objectPositions, bool showTerrain)
  1129. {
  1130. openWorldView(11);
  1131. terrain->onViewSpellActivated(11, objectPositions, showTerrain);
  1132. }