CAdvMapInt.cpp 42 KB

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