AdventureMapClasses.cpp 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214
  1. #include "StdInc.h"
  2. #include "AdventureMapClasses.h"
  3. #include <SDL.h>
  4. #include "MiscWidgets.h"
  5. #include "CComponent.h"
  6. #include "../CGameInfo.h"
  7. #include "../CMusicHandler.h"
  8. #include "../CPlayerInterface.h"
  9. #include "../CPreGame.h"
  10. #include "../Graphics.h"
  11. #include "../gui/CGuiHandler.h"
  12. #include "../gui/SDL_Pixels.h"
  13. #include "../windows/InfoWindows.h"
  14. #include "../windows/CAdvmapInterface.h"
  15. #include "../windows/GUIClasses.h"
  16. #include "../battle/CBattleInterfaceClasses.h"
  17. #include "../battle/CBattleInterface.h"
  18. #include "../../CCallback.h"
  19. #include "../../lib/StartInfo.h"
  20. #include "../../lib/CGameState.h"
  21. #include "../../lib/CGeneralTextHandler.h"
  22. #include "../../lib/CHeroHandler.h"
  23. #include "../../lib/CModHandler.h"
  24. #include "../../lib/CTownHandler.h"
  25. #include "../../lib/filesystem/Filesystem.h"
  26. #include "../../lib/JsonNode.h"
  27. #include "../../lib/mapObjects/CGHeroInstance.h"
  28. #include "../../lib/mapping/CMap.h"
  29. #include "../../lib/NetPacksBase.h"
  30. #include "../../lib/StringConstants.h"
  31. /*
  32. * CAdventureMapClasses.cpp, part of VCMI engine
  33. *
  34. * Authors: listed in file AUTHORS in main folder
  35. *
  36. * License: GNU General Public License v2.0 or later
  37. * Full text of license available in license.txt file, in main folder
  38. *
  39. */
  40. CList::CListItem::CListItem(CList * Parent):
  41. CIntObject(LCLICK | RCLICK | HOVER),
  42. parent(Parent),
  43. selection(nullptr)
  44. {
  45. }
  46. CList::CListItem::~CListItem()
  47. {
  48. // select() method in this was already destroyed so we can't safely call method in parent
  49. if (parent->selected == this)
  50. parent->selected = nullptr;
  51. }
  52. void CList::CListItem::clickRight(tribool down, bool previousState)
  53. {
  54. if (down == true)
  55. showTooltip();
  56. }
  57. void CList::CListItem::clickLeft(tribool down, bool previousState)
  58. {
  59. if (down == true)
  60. {
  61. //second click on already selected item
  62. if (parent->selected == this)
  63. open();
  64. else
  65. {
  66. //first click - switch selection
  67. parent->select(this);
  68. }
  69. }
  70. }
  71. void CList::CListItem::hover(bool on)
  72. {
  73. if (on)
  74. GH.statusbar->setText(getHoverText());
  75. else
  76. GH.statusbar->clear();
  77. }
  78. void CList::CListItem::onSelect(bool on)
  79. {
  80. OBJ_CONSTRUCTION_CAPTURING_ALL;
  81. vstd::clear_pointer(selection);
  82. if (on)
  83. selection = genSelection();
  84. select(on);
  85. GH.totalRedraw();
  86. }
  87. CList::CList(int Size, Point position, std::string btnUp, std::string btnDown, size_t listAmount,
  88. int helpUp, int helpDown, CListBox::CreateFunc create, CListBox::DestroyFunc destroy):
  89. CIntObject(0, position),
  90. size(Size),
  91. selected(nullptr)
  92. {
  93. OBJ_CONSTRUCTION_CAPTURING_ALL;
  94. scrollUp = new CButton(Point(0, 0), btnUp, CGI->generaltexth->zelp[helpUp]);
  95. list = new CListBox(create, destroy, Point(1,scrollUp->pos.h), Point(0, 32), size, listAmount);
  96. //assign callback only after list was created
  97. scrollUp->addCallback(std::bind(&CListBox::moveToPrev, list));
  98. scrollDown = new CButton(Point(0, scrollUp->pos.h + 32*size), btnDown, CGI->generaltexth->zelp[helpDown], std::bind(&CListBox::moveToNext, list));
  99. scrollDown->addCallback(std::bind(&CList::update, this));
  100. scrollUp->addCallback(std::bind(&CList::update, this));
  101. update();
  102. }
  103. void CList::update()
  104. {
  105. bool onTop = list->getPos() == 0;
  106. bool onBottom = list->getPos() + size >= list->size();
  107. scrollUp->block(onTop);
  108. scrollDown->block(onBottom);
  109. }
  110. void CList::select(CListItem *which)
  111. {
  112. if (selected == which)
  113. return;
  114. if (selected)
  115. selected->onSelect(false);
  116. selected = which;
  117. if (which)
  118. {
  119. which->onSelect(true);
  120. onSelect();
  121. }
  122. }
  123. int CList::getSelectedIndex()
  124. {
  125. return list->getIndexOf(selected);
  126. }
  127. void CList::selectIndex(int which)
  128. {
  129. if (which < 0)
  130. {
  131. if (selected)
  132. select(nullptr);
  133. }
  134. else
  135. {
  136. list->scrollTo(which);
  137. update();
  138. select(dynamic_cast<CListItem*>(list->getItem(which)));
  139. }
  140. }
  141. void CList::selectNext()
  142. {
  143. int index = getSelectedIndex();
  144. if (index < 0)
  145. selectIndex(0);
  146. else if (index + 1 < list->size())
  147. selectIndex(index+1);
  148. }
  149. void CList::selectPrev()
  150. {
  151. int index = getSelectedIndex();
  152. if (index <= 0)
  153. selectIndex(0);
  154. else
  155. selectIndex(index-1);
  156. }
  157. CHeroList::CEmptyHeroItem::CEmptyHeroItem()
  158. {
  159. OBJ_CONSTRUCTION_CAPTURING_ALL;
  160. auto move = new CAnimImage("IMOBIL", 0, 0, 0, 1);
  161. auto img = new CPicture("HPSXXX", move->pos.w + 1);
  162. auto mana = new CAnimImage("IMANA", 0, 0, move->pos.w + img->pos.w + 2, 1 );
  163. pos.w = mana->pos.w + mana->pos.x - pos.x;
  164. pos.h = std::max(std::max<SDLX_Size>(move->pos.h + 1, mana->pos.h + 1), img->pos.h);
  165. }
  166. CHeroList::CHeroItem::CHeroItem(CHeroList *parent, const CGHeroInstance * Hero):
  167. CListItem(parent),
  168. hero(Hero)
  169. {
  170. OBJ_CONSTRUCTION_CAPTURING_ALL;
  171. movement = new CAnimImage("IMOBIL", 0, 0, 0, 1);
  172. portrait = new CAnimImage("PortraitsSmall", hero->portrait, 0, movement->pos.w + 1);
  173. mana = new CAnimImage("IMANA", 0, 0, movement->pos.w + portrait->pos.w + 2, 1 );
  174. pos.w = mana->pos.w + mana->pos.x - pos.x;
  175. pos.h = std::max(std::max<SDLX_Size>(movement->pos.h + 1, mana->pos.h + 1), portrait->pos.h);
  176. update();
  177. }
  178. void CHeroList::CHeroItem::update()
  179. {
  180. movement->setFrame(std::min<size_t>(movement->size()-1, hero->movement / 100));
  181. mana->setFrame(std::min<size_t>(mana->size()-1, hero->mana / 5));
  182. redraw();
  183. }
  184. CIntObject * CHeroList::CHeroItem::genSelection()
  185. {
  186. return new CPicture("HPSYYY", movement->pos.w + 1);
  187. }
  188. void CHeroList::CHeroItem::select(bool on)
  189. {
  190. if (on && adventureInt->selection != hero)
  191. adventureInt->select(hero);
  192. }
  193. void CHeroList::CHeroItem::open()
  194. {
  195. LOCPLINT->openHeroWindow(hero);
  196. }
  197. void CHeroList::CHeroItem::showTooltip()
  198. {
  199. CRClickPopup::createAndPush(hero, GH.current->motion);
  200. }
  201. std::string CHeroList::CHeroItem::getHoverText()
  202. {
  203. return boost::str(boost::format(CGI->generaltexth->allTexts[15]) % hero->name % hero->type->heroClass->name);
  204. }
  205. CIntObject * CHeroList::createHeroItem(size_t index)
  206. {
  207. if (LOCPLINT->wanderingHeroes.size() > index)
  208. return new CHeroItem(this, LOCPLINT->wanderingHeroes[index]);
  209. return new CEmptyHeroItem();
  210. }
  211. CHeroList::CHeroList(int size, Point position, std::string btnUp, std::string btnDown):
  212. CList(size, position, btnUp, btnDown, LOCPLINT->wanderingHeroes.size(), 303, 304, std::bind(&CHeroList::createHeroItem, this, _1))
  213. {
  214. }
  215. void CHeroList::select(const CGHeroInstance * hero)
  216. {
  217. selectIndex(vstd::find_pos(LOCPLINT->wanderingHeroes, hero));
  218. }
  219. void CHeroList::update(const CGHeroInstance * hero)
  220. {
  221. //this hero is already present, update its status
  222. for (auto & elem : list->getItems())
  223. {
  224. auto item = dynamic_cast<CHeroItem*>(elem);
  225. if (item && item->hero == hero && vstd::contains(LOCPLINT->wanderingHeroes, hero))
  226. {
  227. item->update();
  228. return;
  229. }
  230. }
  231. //simplest solution for now: reset list and restore selection
  232. list->resize(LOCPLINT->wanderingHeroes.size());
  233. if (adventureInt->selection)
  234. {
  235. auto hero = dynamic_cast<const CGHeroInstance *>(adventureInt->selection);
  236. if (hero)
  237. select(hero);
  238. }
  239. CList::update();
  240. }
  241. CIntObject * CTownList::createTownItem(size_t index)
  242. {
  243. if (LOCPLINT->towns.size() > index)
  244. return new CTownItem(this, LOCPLINT->towns[index]);
  245. return new CAnimImage("ITPA", 0);
  246. }
  247. CTownList::CTownItem::CTownItem(CTownList *parent, const CGTownInstance *Town):
  248. CListItem(parent),
  249. town(Town)
  250. {
  251. OBJ_CONSTRUCTION_CAPTURING_ALL;
  252. picture = new CAnimImage("ITPA", 0);
  253. pos = picture->pos;
  254. update();
  255. }
  256. CIntObject * CTownList::CTownItem::genSelection()
  257. {
  258. return new CAnimImage("ITPA", 1);
  259. }
  260. void CTownList::CTownItem::update()
  261. {
  262. size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->builded >= CGI->modh->settings.MAX_BUILDING_PER_TURN];
  263. picture->setFrame(iconIndex + 2);
  264. redraw();
  265. }
  266. void CTownList::CTownItem::select(bool on)
  267. {
  268. if (on && adventureInt->selection != town)
  269. adventureInt->select(town);
  270. }
  271. void CTownList::CTownItem::open()
  272. {
  273. LOCPLINT->openTownWindow(town);
  274. }
  275. void CTownList::CTownItem::showTooltip()
  276. {
  277. CRClickPopup::createAndPush(town, GH.current->motion);
  278. }
  279. std::string CTownList::CTownItem::getHoverText()
  280. {
  281. return town->getObjectName();
  282. }
  283. CTownList::CTownList(int size, Point position, std::string btnUp, std::string btnDown):
  284. CList(size, position, btnUp, btnDown, LOCPLINT->towns.size(), 306, 307, std::bind(&CTownList::createTownItem, this, _1))
  285. {
  286. }
  287. void CTownList::select(const CGTownInstance * town)
  288. {
  289. selectIndex(vstd::find_pos(LOCPLINT->towns, town));
  290. }
  291. void CTownList::update(const CGTownInstance *)
  292. {
  293. //simplest solution for now: reset list and restore selection
  294. list->resize(LOCPLINT->towns.size());
  295. if (adventureInt->selection)
  296. {
  297. auto town = dynamic_cast<const CGTownInstance *>(adventureInt->selection);
  298. if (town)
  299. select(town);
  300. }
  301. CList::update();
  302. }
  303. const SDL_Color & CMinimapInstance::getTileColor(const int3 & pos)
  304. {
  305. static const SDL_Color fogOfWar = {0, 0, 0, 255};
  306. const TerrainTile * tile = LOCPLINT->cb->getTile(pos, false);
  307. // if tile is not visible it will be black on minimap
  308. if(!tile)
  309. return fogOfWar;
  310. // if object at tile is owned - it will be colored as its owner
  311. for (const CGObjectInstance *obj : tile->blockingObjects)
  312. {
  313. //heroes will be blitted later
  314. switch (obj->ID)
  315. {
  316. case Obj::HERO:
  317. case Obj::PRISON:
  318. continue;
  319. }
  320. PlayerColor player = obj->getOwner();
  321. if(player == PlayerColor::NEUTRAL)
  322. return *graphics->neutralColor;
  323. else
  324. if (player < PlayerColor::PLAYER_LIMIT)
  325. return graphics->playerColors[player.getNum()];
  326. }
  327. // else - use terrain color (blocked version or normal)
  328. if (tile->blocked && (!tile->visitable))
  329. return parent->colors.find(tile->terType)->second.second;
  330. else
  331. return parent->colors.find(tile->terType)->second.first;
  332. }
  333. void CMinimapInstance::tileToPixels (const int3 &tile, int &x, int &y, int toX, int toY)
  334. {
  335. int3 mapSizes = LOCPLINT->cb->getMapSize();
  336. double stepX = double(pos.w) / mapSizes.x;
  337. double stepY = double(pos.h) / mapSizes.y;
  338. x = toX + stepX * tile.x;
  339. y = toY + stepY * tile.y;
  340. }
  341. void CMinimapInstance::blitTileWithColor(const SDL_Color &color, const int3 &tile, SDL_Surface *to, int toX, int toY)
  342. {
  343. //coordinates of rectangle on minimap representing this tile
  344. // begin - first to blit, end - first NOT to blit
  345. int xBegin, yBegin, xEnd, yEnd;
  346. tileToPixels (tile, xBegin, yBegin, toX, toY);
  347. tileToPixels (int3 (tile.x + 1, tile.y + 1, tile.z), xEnd, yEnd, toX, toY);
  348. for (int y=yBegin; y<yEnd; y++)
  349. {
  350. Uint8 *ptr = (Uint8*)to->pixels + y * to->pitch + xBegin * minimap->format->BytesPerPixel;
  351. for (int x=xBegin; x<xEnd; x++)
  352. ColorPutter<4, 1>::PutColor(ptr, color);
  353. }
  354. }
  355. void CMinimapInstance::refreshTile(const int3 &tile)
  356. {
  357. blitTileWithColor(getTileColor(int3(tile.x, tile.y, level)), tile, minimap, 0, 0);
  358. }
  359. void CMinimapInstance::drawScaled(int level)
  360. {
  361. int3 mapSizes = LOCPLINT->cb->getMapSize();
  362. //size of one map tile on our minimap
  363. double stepX = double(pos.w) / mapSizes.x;
  364. double stepY = double(pos.h) / mapSizes.y;
  365. double currY = 0;
  366. for (int y=0; y<mapSizes.y; y++, currY += stepY)
  367. {
  368. double currX = 0;
  369. for (int x=0; x<mapSizes.x; x++, currX += stepX)
  370. {
  371. const SDL_Color &color = getTileColor(int3(x,y,level));
  372. //coordinates of rectangle on minimap representing this tile
  373. // begin - first to blit, end - first NOT to blit
  374. int xBegin = currX;
  375. int yBegin = currY;
  376. int xEnd = currX + stepX;
  377. int yEnd = currY + stepY;
  378. for (int y=yBegin; y<yEnd; y++)
  379. {
  380. Uint8 *ptr = (Uint8*)minimap->pixels + y * minimap->pitch + xBegin * minimap->format->BytesPerPixel;
  381. for (int x=xBegin; x<xEnd; x++)
  382. ColorPutter<4, 1>::PutColor(ptr, color);
  383. }
  384. }
  385. }
  386. }
  387. CMinimapInstance::CMinimapInstance(CMinimap *Parent, int Level):
  388. parent(Parent),
  389. minimap(CSDL_Ext::createSurfaceWithBpp<4>(parent->pos.w, parent->pos.h)),
  390. level(Level)
  391. {
  392. pos.w = parent->pos.w;
  393. pos.h = parent->pos.h;
  394. drawScaled(level);
  395. }
  396. CMinimapInstance::~CMinimapInstance()
  397. {
  398. SDL_FreeSurface(minimap);
  399. }
  400. void CMinimapInstance::showAll(SDL_Surface *to)
  401. {
  402. blitAtLoc(minimap, 0, 0, to);
  403. //draw heroes
  404. std::vector <const CGHeroInstance *> heroes = LOCPLINT->cb->getHeroesInfo(false); //TODO: do we really need separate function for drawing heroes?
  405. for(auto & hero : heroes)
  406. {
  407. int3 position = hero->getPosition(false);
  408. if (position.z == level)
  409. {
  410. const SDL_Color & color = graphics->playerColors[hero->getOwner().getNum()];
  411. blitTileWithColor(color, position, to, pos.x, pos.y);
  412. }
  413. }
  414. }
  415. std::map<int, std::pair<SDL_Color, SDL_Color> > CMinimap::loadColors(std::string from)
  416. {
  417. std::map<int, std::pair<SDL_Color, SDL_Color> > ret;
  418. const JsonNode config(ResourceID(from, EResType::TEXT));
  419. for(auto &m : config.Struct())
  420. {
  421. auto index = boost::find(GameConstants::TERRAIN_NAMES, m.first);
  422. if (index == std::end(GameConstants::TERRAIN_NAMES))
  423. {
  424. logGlobal->errorStream() << "Error: unknown terrain in terrains.json: " << m.first;
  425. continue;
  426. }
  427. int terrainID = index - std::begin(GameConstants::TERRAIN_NAMES);
  428. const JsonVector &unblockedVec = m.second["minimapUnblocked"].Vector();
  429. SDL_Color normal =
  430. {
  431. ui8(unblockedVec[0].Float()),
  432. ui8(unblockedVec[1].Float()),
  433. ui8(unblockedVec[2].Float()),
  434. ui8(255)
  435. };
  436. const JsonVector &blockedVec = m.second["minimapBlocked"].Vector();
  437. SDL_Color blocked =
  438. {
  439. ui8(blockedVec[0].Float()),
  440. ui8(blockedVec[1].Float()),
  441. ui8(blockedVec[2].Float()),
  442. ui8(255)
  443. };
  444. ret.insert(std::make_pair(terrainID, std::make_pair(normal, blocked)));
  445. }
  446. return ret;
  447. }
  448. CMinimap::CMinimap(const Rect &position):
  449. CIntObject(LCLICK | RCLICK | HOVER | MOVE, position.topLeft()),
  450. aiShield(nullptr),
  451. minimap(nullptr),
  452. level(0),
  453. colors(loadColors("config/terrains.json"))
  454. {
  455. pos.w = position.w;
  456. pos.h = position.h;
  457. }
  458. int3 CMinimap::translateMousePosition()
  459. {
  460. // 0 = top-left corner, 1 = bottom-right corner
  461. double dx = double(GH.current->motion.x - pos.x) / pos.w;
  462. double dy = double(GH.current->motion.y - pos.y) / pos.h;
  463. int3 mapSizes = LOCPLINT->cb->getMapSize();
  464. int3 tile (mapSizes.x * dx, mapSizes.y * dy, level);
  465. return tile;
  466. }
  467. void CMinimap::moveAdvMapSelection()
  468. {
  469. int3 newLocation = translateMousePosition();
  470. adventureInt->centerOn(newLocation);
  471. if (!(adventureInt->active & GENERAL))
  472. GH.totalRedraw(); //redraw this as well as inactive adventure map
  473. else
  474. redraw();//redraw only this
  475. }
  476. void CMinimap::clickLeft(tribool down, bool previousState)
  477. {
  478. if (down)
  479. moveAdvMapSelection();
  480. }
  481. void CMinimap::clickRight(tribool down, bool previousState)
  482. {
  483. adventureInt->handleRightClick(CGI->generaltexth->zelp[291].second, down);
  484. }
  485. void CMinimap::hover(bool on)
  486. {
  487. if (on)
  488. GH.statusbar->setText(CGI->generaltexth->zelp[291].first);
  489. else
  490. GH.statusbar->clear();
  491. }
  492. void CMinimap::mouseMoved(const SDL_MouseMotionEvent & sEvent)
  493. {
  494. if (pressedL)
  495. moveAdvMapSelection();
  496. }
  497. void CMinimap::showAll(SDL_Surface * to)
  498. {
  499. CIntObject::showAll(to);
  500. if (minimap)
  501. {
  502. int3 mapSizes = LOCPLINT->cb->getMapSize();
  503. //draw radar
  504. SDL_Rect oldClip;
  505. SDL_Rect radar =
  506. {
  507. si16(adventureInt->position.x * pos.w / mapSizes.x + pos.x),
  508. si16(adventureInt->position.y * pos.h / mapSizes.y + pos.y),
  509. ui16(adventureInt->terrain.tilesw * pos.w / mapSizes.x),
  510. ui16(adventureInt->terrain.tilesh * pos.h / mapSizes.y)
  511. };
  512. SDL_GetClipRect(to, &oldClip);
  513. SDL_SetClipRect(to, &pos);
  514. CSDL_Ext::drawDashedBorder(to, radar, int3(255,75,125));
  515. SDL_SetClipRect(to, &oldClip);
  516. }
  517. }
  518. void CMinimap::update()
  519. {
  520. if (aiShield) //AI turn is going on. There is no need to update minimap
  521. return;
  522. OBJ_CONSTRUCTION_CAPTURING_ALL;
  523. vstd::clear_pointer(minimap);
  524. minimap = new CMinimapInstance(this, level);
  525. redraw();
  526. }
  527. void CMinimap::setLevel(int newLevel)
  528. {
  529. level = newLevel;
  530. update();
  531. }
  532. void CMinimap::setAIRadar(bool on)
  533. {
  534. if (on)
  535. {
  536. OBJ_CONSTRUCTION_CAPTURING_ALL;
  537. vstd::clear_pointer(minimap);
  538. if (!aiShield)
  539. aiShield = new CPicture("AIShield");
  540. }
  541. else
  542. {
  543. vstd::clear_pointer(aiShield);
  544. update();
  545. }
  546. // this my happen during AI turn when this interface is inactive
  547. // force redraw in order to properly update interface
  548. GH.totalRedraw();
  549. }
  550. void CMinimap::hideTile(const int3 &pos)
  551. {
  552. if (minimap)
  553. minimap->refreshTile(pos);
  554. }
  555. void CMinimap::showTile(const int3 &pos)
  556. {
  557. if (minimap)
  558. minimap->refreshTile(pos);
  559. }
  560. CInfoBar::CVisibleInfo::CVisibleInfo(Point position):
  561. CIntObject(0, position),
  562. aiProgress(nullptr)
  563. {
  564. }
  565. void CInfoBar::CVisibleInfo::show(SDL_Surface *to)
  566. {
  567. CIntObject::show(to);
  568. for(auto object : forceRefresh)
  569. object->showAll(to);
  570. }
  571. void CInfoBar::CVisibleInfo::loadHero(const CGHeroInstance * hero)
  572. {
  573. assert(children.empty()); // visible info should be re-created to change type
  574. OBJ_CONSTRUCTION_CAPTURING_ALL;
  575. new CPicture("ADSTATHR");
  576. new CHeroTooltip(Point(0,0), hero);
  577. }
  578. void CInfoBar::CVisibleInfo::loadTown(const CGTownInstance *town)
  579. {
  580. assert(children.empty()); // visible info should be re-created to change type
  581. OBJ_CONSTRUCTION_CAPTURING_ALL;
  582. new CPicture("ADSTATCS");
  583. new CTownTooltip(Point(0,0), town);
  584. }
  585. void CInfoBar::CVisibleInfo::playNewDaySound()
  586. {
  587. if (LOCPLINT->cb->getDate(Date::DAY_OF_WEEK) != 1) // not first day of the week
  588. CCS->soundh->playSound(soundBase::newDay);
  589. else
  590. if (LOCPLINT->cb->getDate(Date::WEEK) != 1) // not first week in month
  591. CCS->soundh->playSound(soundBase::newWeek);
  592. else
  593. if (LOCPLINT->cb->getDate(Date::MONTH) != 1) // not first month
  594. CCS->soundh->playSound(soundBase::newMonth);
  595. else
  596. CCS->soundh->playSound(soundBase::newDay);
  597. }
  598. std::string CInfoBar::CVisibleInfo::getNewDayName()
  599. {
  600. if (LOCPLINT->cb->getDate(Date::DAY) == 1)
  601. return "NEWDAY";
  602. if (LOCPLINT->cb->getDate(Date::DAY) != 1)
  603. return "NEWDAY";
  604. switch(LOCPLINT->cb->getDate(Date::WEEK))
  605. {
  606. case 1: return "NEWWEEK1";
  607. case 2: return "NEWWEEK2";
  608. case 3: return "NEWWEEK3";
  609. case 4: return "NEWWEEK4";
  610. default: assert(0); return "";
  611. }
  612. }
  613. void CInfoBar::CVisibleInfo::loadDay()
  614. {
  615. assert(children.empty()); // visible info should be re-created first to change type
  616. playNewDaySound();
  617. OBJ_CONSTRUCTION_CAPTURING_ALL;
  618. new CShowableAnim(1, 0, getNewDayName(), CShowableAnim::PLAY_ONCE);
  619. std::string labelText;
  620. if (LOCPLINT->cb->getDate(Date::DAY_OF_WEEK) == 1 && LOCPLINT->cb->getDate(Date::DAY) != 1) // monday of any week but first - show new week info
  621. labelText = CGI->generaltexth->allTexts[63] + " " + boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::WEEK));
  622. else
  623. labelText = CGI->generaltexth->allTexts[64] + " " + boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::DAY_OF_WEEK));
  624. forceRefresh.push_back(new CLabel(95, 31, FONT_MEDIUM, CENTER, Colors::WHITE, labelText));
  625. }
  626. void CInfoBar::CVisibleInfo::loadEnemyTurn(PlayerColor player)
  627. {
  628. assert(children.empty()); // visible info should be re-created to change type
  629. OBJ_CONSTRUCTION_CAPTURING_ALL;
  630. new CPicture("ADSTATNX");
  631. new CAnimImage("CREST58", player.getNum(), 0, 20, 51);
  632. new CShowableAnim(99, 51, "HOURSAND");
  633. // FIXME: currently there is no way to get progress from VCAI
  634. // if this will change at some point switch this ifdef to enable correct code
  635. #if 0
  636. //prepare hourglass for updating AI turn
  637. aiProgress = new CAnimImage("HOURGLAS", 0, 0, 99, 51);
  638. forceRefresh.push_back(aiProgress);
  639. #else
  640. //create hourglass that will be always animated ignoring AI status
  641. new CShowableAnim(99, 51, "HOURGLAS", CShowableAnim::PLAY_ONCE, 40);
  642. #endif
  643. }
  644. void CInfoBar::CVisibleInfo::loadGameStatus()
  645. {
  646. assert(children.empty()); // visible info should be re-created to change type
  647. //get amount of halls of each level
  648. std::vector<int> halls(4, 0);
  649. for(auto town : LOCPLINT->towns)
  650. halls[town->hallLevel()]++;
  651. std::vector<PlayerColor> allies, enemies;
  652. //generate list of allies and enemies
  653. for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
  654. {
  655. if(LOCPLINT->cb->getPlayerStatus(PlayerColor(i), false) == EPlayerStatus::INGAME)
  656. {
  657. if (LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, PlayerColor(i)) != PlayerRelations::ENEMIES)
  658. allies.push_back(PlayerColor(i));
  659. else
  660. enemies.push_back(PlayerColor(i));
  661. }
  662. }
  663. //generate component
  664. OBJ_CONSTRUCTION_CAPTURING_ALL;
  665. new CPicture("ADSTATIN");
  666. auto allyLabel = new CLabel(10, 106, FONT_SMALL, TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[390] + ":");
  667. auto enemyLabel = new CLabel(10, 136, FONT_SMALL, TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[391] + ":");
  668. int posx = allyLabel->pos.w + allyLabel->pos.x - pos.x + 4;
  669. for(PlayerColor & player : allies)
  670. {
  671. auto image = new CAnimImage("ITGFLAGS", player.getNum(), 0, posx, 102);
  672. posx += image->pos.w;
  673. }
  674. posx = enemyLabel->pos.w + enemyLabel->pos.x - pos.x + 4;
  675. for(PlayerColor & player : enemies)
  676. {
  677. auto image = new CAnimImage("ITGFLAGS", player.getNum(), 0, posx, 132);
  678. posx += image->pos.w;
  679. }
  680. for (size_t i=0; i<halls.size(); i++)
  681. {
  682. new CAnimImage("itmtl", i, 0, 6 + 42 * i , 11);
  683. if (halls[i])
  684. new CLabel( 26 + 42 * i, 64, FONT_SMALL, CENTER, Colors::WHITE, boost::lexical_cast<std::string>(halls[i]));
  685. }
  686. }
  687. void CInfoBar::CVisibleInfo::loadComponent(const Component & compToDisplay, std::string message)
  688. {
  689. assert(children.empty()); // visible info should be re-created to change type
  690. OBJ_CONSTRUCTION_CAPTURING_ALL;
  691. new CPicture("ADSTATOT", 1);
  692. auto comp = new CComponent(compToDisplay);
  693. comp->moveTo(Point(pos.x+47, pos.y+50));
  694. new CTextBox(message, Rect(10, 4, 160, 50), 0, FONT_SMALL, CENTER, Colors::WHITE);
  695. }
  696. void CInfoBar::CVisibleInfo::updateEnemyTurn(double progress)
  697. {
  698. if (aiProgress)
  699. aiProgress->setFrame((aiProgress->size() - 1) * progress);
  700. }
  701. void CInfoBar::reset(EState newState = EMPTY)
  702. {
  703. OBJ_CONSTRUCTION_CAPTURING_ALL;
  704. vstd::clear_pointer(visibleInfo);
  705. currentObject = nullptr;
  706. state = newState;
  707. visibleInfo = new CVisibleInfo(Point(8, 12));
  708. }
  709. void CInfoBar::showSelection()
  710. {
  711. if (adventureInt->selection)
  712. {
  713. auto hero = dynamic_cast<const CGHeroInstance *>(adventureInt->selection);
  714. if (hero)
  715. {
  716. showHeroSelection(hero);
  717. return;
  718. }
  719. auto town = dynamic_cast<const CGTownInstance *>(adventureInt->selection);
  720. if (town)
  721. {
  722. showTownSelection(town);
  723. return;
  724. }
  725. }
  726. showGameStatus();//FIXME: may be incorrect but shouldn't happen in general
  727. }
  728. void CInfoBar::tick()
  729. {
  730. removeUsedEvents(TIME);
  731. showSelection();
  732. }
  733. void CInfoBar::clickLeft(tribool down, bool previousState)
  734. {
  735. if (down)
  736. {
  737. if (state == HERO || state == TOWN)
  738. showGameStatus();
  739. else if (state == GAME)
  740. showDate();
  741. else
  742. showSelection();
  743. }
  744. }
  745. void CInfoBar::clickRight(tribool down, bool previousState)
  746. {
  747. adventureInt->handleRightClick(CGI->generaltexth->allTexts[109], down);
  748. }
  749. void CInfoBar::hover(bool on)
  750. {
  751. if (on)
  752. GH.statusbar->setText(CGI->generaltexth->zelp[292].first);
  753. else
  754. GH.statusbar->clear();
  755. }
  756. CInfoBar::CInfoBar(const Rect &position):
  757. CIntObject(LCLICK | RCLICK | HOVER, position.topLeft()),
  758. visibleInfo(nullptr),
  759. state(EMPTY),
  760. currentObject(nullptr)
  761. {
  762. pos.w = position.w;
  763. pos.h = position.h;
  764. //FIXME: enable some mode? Should be done by advMap::select() when game starts but just in case?
  765. }
  766. void CInfoBar::showDate()
  767. {
  768. reset(DATE);
  769. visibleInfo->loadDay();
  770. setTimer(3000);
  771. redraw();
  772. }
  773. void CInfoBar::showComponent(const Component & comp, std::string message)
  774. {
  775. reset(COMPONENT);
  776. visibleInfo->loadComponent(comp, message);
  777. setTimer(3000);
  778. redraw();
  779. }
  780. void CInfoBar::startEnemyTurn(PlayerColor color)
  781. {
  782. reset(AITURN);
  783. visibleInfo->loadEnemyTurn(color);
  784. redraw();
  785. }
  786. void CInfoBar::updateEnemyTurn(double progress)
  787. {
  788. assert(state == AITURN);
  789. visibleInfo->updateEnemyTurn(progress);
  790. redraw();
  791. }
  792. void CInfoBar::showHeroSelection(const CGHeroInstance * hero)
  793. {
  794. if (!hero)
  795. return;
  796. reset(HERO);
  797. currentObject = hero;
  798. visibleInfo->loadHero(hero);
  799. redraw();
  800. }
  801. void CInfoBar::showTownSelection(const CGTownInstance * town)
  802. {
  803. if (!town)
  804. return;
  805. reset(TOWN);
  806. currentObject = town;
  807. visibleInfo->loadTown(town);
  808. redraw();
  809. }
  810. void CInfoBar::showGameStatus()
  811. {
  812. reset(GAME);
  813. visibleInfo->loadGameStatus();
  814. setTimer(3000);
  815. redraw();
  816. }
  817. void CInGameConsole::show(SDL_Surface * to)
  818. {
  819. int number = 0;
  820. std::vector<std::list< std::pair< std::string, int > >::iterator> toDel;
  821. boost::unique_lock<boost::mutex> lock(texts_mx);
  822. for(auto it = texts.begin(); it != texts.end(); ++it, ++number)
  823. {
  824. Point leftBottomCorner(0, screen->h);
  825. if(LOCPLINT->battleInt)
  826. {
  827. leftBottomCorner = LOCPLINT->battleInt->pos.bottomLeft();
  828. }
  829. graphics->fonts[FONT_MEDIUM]->renderTextLeft(to, it->first, Colors::GREEN,
  830. Point(leftBottomCorner.x + 50, leftBottomCorner.y - texts.size() * 20 - 80 + number*20));
  831. if(SDL_GetTicks() - it->second > defaultTimeout)
  832. {
  833. toDel.push_back(it);
  834. }
  835. }
  836. for(auto & elem : toDel)
  837. {
  838. texts.erase(elem);
  839. }
  840. }
  841. void CInGameConsole::print(const std::string &txt)
  842. {
  843. boost::unique_lock<boost::mutex> lock(texts_mx);
  844. int lineLen = conf.go()->ac.outputLineLength;
  845. if(txt.size() < lineLen)
  846. {
  847. texts.push_back(std::make_pair(txt, SDL_GetTicks()));
  848. if(texts.size() > maxDisplayedTexts)
  849. {
  850. texts.pop_front();
  851. }
  852. }
  853. else
  854. {
  855. assert(lineLen);
  856. for(int g=0; g<txt.size() / lineLen + 1; ++g)
  857. {
  858. std::string part = txt.substr(g * lineLen, lineLen);
  859. if(part.size() == 0)
  860. break;
  861. texts.push_back(std::make_pair(part, SDL_GetTicks()));
  862. if(texts.size() > maxDisplayedTexts)
  863. {
  864. texts.pop_front();
  865. }
  866. }
  867. }
  868. }
  869. void CInGameConsole::keyPressed (const SDL_KeyboardEvent & key)
  870. {
  871. if(key.type != SDL_KEYDOWN) return;
  872. if(!captureAllKeys && key.keysym.sym != SDLK_TAB) return; //because user is not entering any text
  873. switch(key.keysym.sym)
  874. {
  875. case SDLK_TAB:
  876. case SDLK_ESCAPE:
  877. {
  878. if(captureAllKeys)
  879. {
  880. captureAllKeys = false;
  881. endEnteringText(false);
  882. }
  883. else if(SDLK_TAB)
  884. {
  885. captureAllKeys = true;
  886. startEnteringText();
  887. }
  888. break;
  889. }
  890. case SDLK_RETURN: //enter key
  891. {
  892. if(enteredText.size() > 0 && captureAllKeys)
  893. {
  894. captureAllKeys = false;
  895. endEnteringText(true);
  896. CCS->soundh->playSound("CHAT");
  897. }
  898. break;
  899. }
  900. case SDLK_BACKSPACE:
  901. {
  902. if(enteredText.size() > 1)
  903. {
  904. Unicode::trimRight(enteredText,2);
  905. enteredText += '_';
  906. refreshEnteredText();
  907. }
  908. break;
  909. }
  910. case SDLK_UP: //up arrow
  911. {
  912. if(previouslyEntered.size() == 0)
  913. break;
  914. if(prevEntDisp == -1)
  915. {
  916. prevEntDisp = previouslyEntered.size() - 1;
  917. enteredText = previouslyEntered[prevEntDisp] + "_";
  918. refreshEnteredText();
  919. }
  920. else if( prevEntDisp > 0)
  921. {
  922. --prevEntDisp;
  923. enteredText = previouslyEntered[prevEntDisp] + "_";
  924. refreshEnteredText();
  925. }
  926. break;
  927. }
  928. case SDLK_DOWN: //down arrow
  929. {
  930. if(prevEntDisp != -1 && prevEntDisp+1 < previouslyEntered.size())
  931. {
  932. ++prevEntDisp;
  933. enteredText = previouslyEntered[prevEntDisp] + "_";
  934. refreshEnteredText();
  935. }
  936. else if(prevEntDisp+1 == previouslyEntered.size()) //useful feature
  937. {
  938. prevEntDisp = -1;
  939. enteredText = "_";
  940. refreshEnteredText();
  941. }
  942. break;
  943. }
  944. default:
  945. {
  946. #ifdef VCMI_SDL1
  947. if(enteredText.size() > 0 && enteredText.size() < conf.go()->ac.inputLineLength)
  948. {
  949. if( key.keysym.unicode < 0x80 && key.keysym.unicode > 0 )
  950. {
  951. enteredText[enteredText.size()-1] = (char)key.keysym.unicode;
  952. enteredText += "_";
  953. refreshEnteredText();
  954. }
  955. }
  956. #endif // VCMI_SDL1
  957. break;
  958. }
  959. }
  960. }
  961. #ifndef VCMI_SDL1
  962. void CInGameConsole::textInputed(const SDL_TextInputEvent & event)
  963. {
  964. if(!captureAllKeys || enteredText.size() == 0)
  965. return;
  966. enteredText.resize(enteredText.size()-1);
  967. enteredText += event.text;
  968. enteredText += "_";
  969. refreshEnteredText();
  970. }
  971. void CInGameConsole::textEdited(const SDL_TextEditingEvent & event)
  972. {
  973. //do nothing here
  974. }
  975. #endif // VCMI_SDL1
  976. void CInGameConsole::startEnteringText()
  977. {
  978. CSDL_Ext::startTextInput(&pos);
  979. enteredText = "_";
  980. if(GH.topInt() == adventureInt)
  981. {
  982. GH.statusbar->alignment = TOPLEFT;
  983. GH.statusbar->setText(enteredText);
  984. //Prevent changes to the text from mouse interaction with the adventure map
  985. GH.statusbar->lock(true);
  986. }
  987. else if(LOCPLINT->battleInt)
  988. {
  989. LOCPLINT->battleInt->console->ingcAlter = enteredText;
  990. }
  991. }
  992. void CInGameConsole::endEnteringText(bool printEnteredText)
  993. {
  994. CSDL_Ext::stopTextInput();
  995. prevEntDisp = -1;
  996. if(printEnteredText)
  997. {
  998. std::string txt = enteredText.substr(0, enteredText.size()-1);
  999. LOCPLINT->cb->sendMessage(txt, LOCPLINT->getSelection());
  1000. previouslyEntered.push_back(txt);
  1001. //print(txt);
  1002. }
  1003. enteredText = "";
  1004. if(GH.topInt() == adventureInt)
  1005. {
  1006. GH.statusbar->alignment = CENTER;
  1007. GH.statusbar->lock(false);
  1008. GH.statusbar->clear();
  1009. }
  1010. else if(LOCPLINT->battleInt)
  1011. {
  1012. LOCPLINT->battleInt->console->ingcAlter = "";
  1013. }
  1014. }
  1015. void CInGameConsole::refreshEnteredText()
  1016. {
  1017. if(GH.topInt() == adventureInt)
  1018. {
  1019. GH.statusbar->lock(false);
  1020. GH.statusbar->clear();
  1021. GH.statusbar->setText(enteredText);
  1022. GH.statusbar->lock(true);
  1023. }
  1024. else if(LOCPLINT->battleInt)
  1025. {
  1026. LOCPLINT->battleInt->console->ingcAlter = enteredText;
  1027. }
  1028. }
  1029. CInGameConsole::CInGameConsole() : prevEntDisp(-1), defaultTimeout(10000), maxDisplayedTexts(10)
  1030. {
  1031. #ifdef VCMI_SDL1
  1032. addUsedEvents(KEYBOARD);
  1033. #else
  1034. addUsedEvents(KEYBOARD | TEXTINPUT);
  1035. #endif
  1036. }