/* * AdventureMapClasses.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #include "StdInc.h" #include "AdventureMapClasses.h" #include #include "MiscWidgets.h" #include "CComponent.h" #include "../CGameInfo.h" #include "../CMusicHandler.h" #include "../CPlayerInterface.h" #include "../mainmenu/CMainMenu.h" #include "../Graphics.h" #include "../CMessage.h" #include "../gui/CGuiHandler.h" #include "../gui/SDL_Pixels.h" #include "../gui/SDL_Compat.h" #include "../widgets/Images.h" #include "../windows/InfoWindows.h" #include "../windows/CAdvmapInterface.h" #include "../windows/GUIClasses.h" #include "../battle/CBattleInterfaceClasses.h" #include "../battle/CBattleInterface.h" #include "../../CCallback.h" #include "../../lib/StartInfo.h" #include "../../lib/CGameState.h" #include "../../lib/CGeneralTextHandler.h" #include "../../lib/CHeroHandler.h" #include "../../lib/CModHandler.h" #include "../../lib/CTownHandler.h" #include "../../lib/Terrain.h" #include "../../lib/filesystem/Filesystem.h" #include "../../lib/JsonNode.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapping/CMap.h" #include "../../lib/NetPacksBase.h" #include "../../lib/StringConstants.h" CList::CListItem::CListItem(CList * Parent) : CIntObject(LCLICK | RCLICK | HOVER), parent(Parent), selection() { defActions = 255-DISPOSE; } CList::CListItem::~CListItem() { } void CList::CListItem::clickRight(tribool down, bool previousState) { if (down == true) showTooltip(); } void CList::CListItem::clickLeft(tribool down, bool previousState) { if(down == true) { //second click on already selected item if(parent->selected == this->shared_from_this()) { open(); } else { //first click - switch selection parent->select(this->shared_from_this()); } } } void CList::CListItem::hover(bool on) { if (on) GH.statusbar->setText(getHoverText()); else GH.statusbar->clear(); } void CList::CListItem::onSelect(bool on) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); selection.reset(); if(on) selection = genSelection(); select(on); GH.totalRedraw(); } CList::CList(int Size, Point position, std::string btnUp, std::string btnDown, size_t listAmount, int helpUp, int helpDown, CListBox::CreateFunc create) : CIntObject(0, position), size(Size), selected(nullptr) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); scrollUp = std::make_shared(Point(0, 0), btnUp, CGI->generaltexth->zelp[helpUp]); scrollDown = std::make_shared(Point(0, scrollUp->pos.h + 32*(int)size), btnDown, CGI->generaltexth->zelp[helpDown]); listBox = std::make_shared(create, Point(1,scrollUp->pos.h), Point(0, 32), size, listAmount); //assign callback only after list was created scrollUp->addCallback(std::bind(&CListBox::moveToPrev, listBox)); scrollDown->addCallback(std::bind(&CListBox::moveToNext, listBox)); scrollUp->addCallback(std::bind(&CList::update, this)); scrollDown->addCallback(std::bind(&CList::update, this)); update(); } void CList::update() { bool onTop = listBox->getPos() == 0; bool onBottom = listBox->getPos() + size >= listBox->size(); scrollUp->block(onTop); scrollDown->block(onBottom); } void CList::select(std::shared_ptr which) { if(selected == which) return; if(selected) selected->onSelect(false); selected = which; if(which) { which->onSelect(true); onSelect(); } } int CList::getSelectedIndex() { return static_cast(listBox->getIndexOf(selected)); } void CList::selectIndex(int which) { if(which < 0) { if(selected) select(nullptr); } else { listBox->scrollTo(which); update(); select(std::dynamic_pointer_cast(listBox->getItem(which))); } } void CList::selectNext() { int index = getSelectedIndex() + 1; if(index >= listBox->size()) index = 0; selectIndex(index); } void CList::selectPrev() { int index = getSelectedIndex(); if(index <= 0) selectIndex(0); else selectIndex(index-1); } CHeroList::CEmptyHeroItem::CEmptyHeroItem() { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); movement = std::make_shared("IMOBIL", 0, 0, 0, 1); portrait = std::make_shared("HPSXXX", movement->pos.w + 1); mana = std::make_shared("IMANA", 0, 0, movement->pos.w + portrait->pos.w + 2, 1 ); pos.w = mana->pos.w + mana->pos.x - pos.x; pos.h = std::max(std::max(movement->pos.h + 1, mana->pos.h + 1), portrait->pos.h); } CHeroList::CHeroItem::CHeroItem(CHeroList *parent, const CGHeroInstance * Hero) : CListItem(parent), hero(Hero) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); movement = std::make_shared("IMOBIL", 0, 0, 0, 1); portrait = std::make_shared("PortraitsSmall", hero->portrait, 0, movement->pos.w + 1); mana = std::make_shared("IMANA", 0, 0, movement->pos.w + portrait->pos.w + 2, 1); pos.w = mana->pos.w + mana->pos.x - pos.x; pos.h = std::max(std::max(movement->pos.h + 1, mana->pos.h + 1), portrait->pos.h); update(); } void CHeroList::CHeroItem::update() { movement->setFrame(std::min(movement->size()-1, hero->movement / 100)); mana->setFrame(std::min(mana->size()-1, hero->mana / 5)); redraw(); } std::shared_ptr CHeroList::CHeroItem::genSelection() { return std::make_shared("HPSYYY", movement->pos.w + 1); } void CHeroList::CHeroItem::select(bool on) { if(on && adventureInt->selection != hero) adventureInt->select(hero); } void CHeroList::CHeroItem::open() { LOCPLINT->openHeroWindow(hero); } void CHeroList::CHeroItem::showTooltip() { CRClickPopup::createAndPush(hero, GH.current->motion); } std::string CHeroList::CHeroItem::getHoverText() { return boost::str(boost::format(CGI->generaltexth->allTexts[15]) % hero->name % hero->type->heroClass->name); } std::shared_ptr CHeroList::createHeroItem(size_t index) { if (LOCPLINT->wanderingHeroes.size() > index) return std::make_shared(this, LOCPLINT->wanderingHeroes[index]); return std::make_shared(); } CHeroList::CHeroList(int size, Point position, std::string btnUp, std::string btnDown): CList(size, position, btnUp, btnDown, LOCPLINT->wanderingHeroes.size(), 303, 304, std::bind(&CHeroList::createHeroItem, this, _1)) { } void CHeroList::select(const CGHeroInstance * hero) { selectIndex(vstd::find_pos(LOCPLINT->wanderingHeroes, hero)); } void CHeroList::update(const CGHeroInstance * hero) { //this hero is already present, update its status for(auto & elem : listBox->getItems()) { auto item = std::dynamic_pointer_cast(elem); if(item && item->hero == hero && vstd::contains(LOCPLINT->wanderingHeroes, hero)) { item->update(); return; } } //simplest solution for now: reset list and restore selection listBox->resize(LOCPLINT->wanderingHeroes.size()); if (adventureInt->selection) { auto selectedHero = dynamic_cast(adventureInt->selection); if (selectedHero) select(selectedHero); } CList::update(); } std::shared_ptr CTownList::createTownItem(size_t index) { if (LOCPLINT->towns.size() > index) return std::make_shared(this, LOCPLINT->towns[index]); return std::make_shared("ITPA", 0); } CTownList::CTownItem::CTownItem(CTownList *parent, const CGTownInstance *Town): CListItem(parent), town(Town) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); picture = std::make_shared("ITPA", 0); pos = picture->pos; update(); } std::shared_ptr CTownList::CTownItem::genSelection() { return std::make_shared("ITPA", 1); } void CTownList::CTownItem::update() { size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->builded >= CGI->modh->settings.MAX_BUILDING_PER_TURN]; picture->setFrame(iconIndex + 2); redraw(); } void CTownList::CTownItem::select(bool on) { if (on && adventureInt->selection != town) adventureInt->select(town); } void CTownList::CTownItem::open() { LOCPLINT->openTownWindow(town); } void CTownList::CTownItem::showTooltip() { CRClickPopup::createAndPush(town, GH.current->motion); } std::string CTownList::CTownItem::getHoverText() { return town->getObjectName(); } CTownList::CTownList(int size, Point position, std::string btnUp, std::string btnDown): CList(size, position, btnUp, btnDown, LOCPLINT->towns.size(), 306, 307, std::bind(&CTownList::createTownItem, this, _1)) { } void CTownList::select(const CGTownInstance * town) { selectIndex(vstd::find_pos(LOCPLINT->towns, town)); } void CTownList::update(const CGTownInstance *) { //simplest solution for now: reset list and restore selection listBox->resize(LOCPLINT->towns.size()); if (adventureInt->selection) { auto town = dynamic_cast(adventureInt->selection); if (town) select(town); } CList::update(); } const SDL_Color & CMinimapInstance::getTileColor(const int3 & pos) { static const SDL_Color fogOfWar = {0, 0, 0, 255}; const TerrainTile * tile = LOCPLINT->cb->getTile(pos, false); // if tile is not visible it will be black on minimap if(!tile) return fogOfWar; // if object at tile is owned - it will be colored as its owner for (const CGObjectInstance *obj : tile->blockingObjects) { //heroes will be blitted later switch (obj->ID) { case Obj::HERO: case Obj::PRISON: continue; } PlayerColor player = obj->getOwner(); if(player == PlayerColor::NEUTRAL) return *graphics->neutralColor; else if (player < PlayerColor::PLAYER_LIMIT) return graphics->playerColors[player.getNum()]; } // else - use terrain color (blocked version or normal) const auto & colorPair = parent->colors.find(tile->terType->id)->second; if (tile->blocked && (!tile->visitable)) return colorPair.second; else return colorPair.first; } void CMinimapInstance::tileToPixels (const int3 &tile, int &x, int &y, int toX, int toY) { int3 mapSizes = LOCPLINT->cb->getMapSize(); double stepX = double(pos.w) / mapSizes.x; double stepY = double(pos.h) / mapSizes.y; x = static_cast(toX + stepX * tile.x); y = static_cast(toY + stepY * tile.y); } void CMinimapInstance::blitTileWithColor(const SDL_Color &color, const int3 &tile, SDL_Surface *to, int toX, int toY) { //coordinates of rectangle on minimap representing this tile // begin - first to blit, end - first NOT to blit int xBegin, yBegin, xEnd, yEnd; tileToPixels (tile, xBegin, yBegin, toX, toY); tileToPixels (int3 (tile.x + 1, tile.y + 1, tile.z), xEnd, yEnd, toX, toY); for (int y=yBegin; ypixels + y * to->pitch + xBegin * minimap->format->BytesPerPixel; for (int x=xBegin; x::PutColor(ptr, color); } } void CMinimapInstance::refreshTile(const int3 &tile) { blitTileWithColor(getTileColor(int3(tile.x, tile.y, level)), tile, minimap, 0, 0); } void CMinimapInstance::drawScaled(int level) { int3 mapSizes = LOCPLINT->cb->getMapSize(); //size of one map tile on our minimap double stepX = double(pos.w) / mapSizes.x; double stepY = double(pos.h) / mapSizes.y; double currY = 0; for (int y=0; y(currX); int yBegin = static_cast(currY); int xEnd = static_cast(currX + stepX); int yEnd = static_cast(currY + stepY); for (int y=yBegin; ypixels + y * minimap->pitch + xBegin * minimap->format->BytesPerPixel; for (int x=xBegin; x::PutColor(ptr, color); } } } } CMinimapInstance::CMinimapInstance(CMinimap *Parent, int Level): parent(Parent), minimap(CSDL_Ext::createSurfaceWithBpp<4>(parent->pos.w, parent->pos.h)), level(Level) { pos.w = parent->pos.w; pos.h = parent->pos.h; drawScaled(level); } CMinimapInstance::~CMinimapInstance() { SDL_FreeSurface(minimap); } void CMinimapInstance::showAll(SDL_Surface * to) { blitAtLoc(minimap, 0, 0, to); //draw heroes std::vector heroes = LOCPLINT->cb->getHeroesInfo(false); //TODO: do we really need separate function for drawing heroes? for(auto & hero : heroes) { int3 position = hero->getPosition(false); if(position.z == level) { const SDL_Color & color = graphics->playerColors[hero->getOwner().getNum()]; blitTileWithColor(color, position, to, pos.x, pos.y); } } } std::map > CMinimap::loadColors() { std::map > ret; for(const auto * terrain : CGI->terrainTypeHandler->terrains()) { SDL_Color normal = { ui8(terrain->minimapUnblocked[0]), ui8(terrain->minimapUnblocked[1]), ui8(terrain->minimapUnblocked[2]), ui8(255) }; SDL_Color blocked = { ui8(terrain->minimapBlocked[0]), ui8(terrain->minimapBlocked[1]), ui8(terrain->minimapBlocked[2]), ui8(255) }; ret[terrain->id] = std::make_pair(normal, blocked); } return ret; } CMinimap::CMinimap(const Rect & position) : CIntObject(LCLICK | RCLICK | HOVER | MOVE, position.topLeft()), level(0), colors(loadColors()) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); pos.w = position.w; pos.h = position.h; aiShield = std::make_shared("AIShield"); aiShield->disable(); } int3 CMinimap::translateMousePosition() { // 0 = top-left corner, 1 = bottom-right corner double dx = double(GH.current->motion.x - pos.x) / pos.w; double dy = double(GH.current->motion.y - pos.y) / pos.h; int3 mapSizes = LOCPLINT->cb->getMapSize(); int3 tile ((si32)(mapSizes.x * dx), (si32)(mapSizes.y * dy), level); return tile; } void CMinimap::moveAdvMapSelection() { int3 newLocation = translateMousePosition(); adventureInt->centerOn(newLocation); if (!(adventureInt->active & GENERAL)) GH.totalRedraw(); //redraw this as well as inactive adventure map else redraw();//redraw only this } void CMinimap::clickLeft(tribool down, bool previousState) { if(down) moveAdvMapSelection(); } void CMinimap::clickRight(tribool down, bool previousState) { adventureInt->handleRightClick(CGI->generaltexth->zelp[291].second, down); } void CMinimap::hover(bool on) { if(on) GH.statusbar->setText(CGI->generaltexth->zelp[291].first); else GH.statusbar->clear(); } void CMinimap::mouseMoved(const SDL_MouseMotionEvent & sEvent) { if(mouseState(EIntObjMouseBtnType::LEFT)) moveAdvMapSelection(); } void CMinimap::showAll(SDL_Surface * to) { CIntObject::showAll(to); if(minimap) { int3 mapSizes = LOCPLINT->cb->getMapSize(); int3 tileCountOnScreen = adventureInt->terrain.tileCountOnScreen(); //draw radar SDL_Rect oldClip; SDL_Rect radar = { si16(adventureInt->position.x * pos.w / mapSizes.x + pos.x), si16(adventureInt->position.y * pos.h / mapSizes.y + pos.y), ui16(tileCountOnScreen.x * pos.w / mapSizes.x), ui16(tileCountOnScreen.y * pos.h / mapSizes.y) }; if(adventureInt->mode == EAdvMapMode::WORLD_VIEW) { // adjusts radar so that it doesn't go out of map in world view mode (since there's no frame) radar.x = std::min(std::max(pos.x, radar.x), pos.x + pos.w - radar.w); radar.y = std::min(std::max(pos.y, radar.y), pos.y + pos.h - radar.h); if(radar.x < pos.x && radar.y < pos.y) return; // whole map is visible at once, no point in redrawing border } SDL_GetClipRect(to, &oldClip); SDL_SetClipRect(to, &pos); CSDL_Ext::drawDashedBorder(to, radar, int3(255,75,125)); SDL_SetClipRect(to, &oldClip); } } void CMinimap::update() { if(aiShield->recActions & UPDATE) //AI turn is going on. There is no need to update minimap return; OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); minimap = std::make_shared(this, level); redraw(); } void CMinimap::setLevel(int newLevel) { level = newLevel; update(); } void CMinimap::setAIRadar(bool on) { if(on) { aiShield->enable(); minimap.reset(); } else { aiShield->disable(); update(); } // this my happen during AI turn when this interface is inactive // force redraw in order to properly update interface GH.totalRedraw(); } void CMinimap::hideTile(const int3 &pos) { if(minimap) minimap->refreshTile(pos); } void CMinimap::showTile(const int3 &pos) { if(minimap) minimap->refreshTile(pos); } CInfoBar::CVisibleInfo::CVisibleInfo() : CIntObject(0, Point(8, 12)) { } void CInfoBar::CVisibleInfo::show(SDL_Surface * to) { CIntObject::show(to); for(auto object : forceRefresh) object->showAll(to); } CInfoBar::EmptyVisibleInfo::EmptyVisibleInfo() { } CInfoBar::VisibleHeroInfo::VisibleHeroInfo(const CGHeroInstance * hero) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared("ADSTATHR"); heroTooltip = std::make_shared(Point(0,0), hero); } CInfoBar::VisibleTownInfo::VisibleTownInfo(const CGTownInstance * town) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared("ADSTATCS"); townTooltip = std::make_shared(Point(0,0), town); } CInfoBar::VisibleDateInfo::VisibleDateInfo() { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); animation = std::make_shared(1, 0, getNewDayName(), CShowableAnim::PLAY_ONCE); std::string labelText; 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 labelText = CGI->generaltexth->allTexts[63] + " " + boost::lexical_cast(LOCPLINT->cb->getDate(Date::WEEK)); else labelText = CGI->generaltexth->allTexts[64] + " " + boost::lexical_cast(LOCPLINT->cb->getDate(Date::DAY_OF_WEEK)); label = std::make_shared(95, 31, FONT_MEDIUM, CENTER, Colors::WHITE, labelText); forceRefresh.push_back(label); } std::string CInfoBar::VisibleDateInfo::getNewDayName() { if(LOCPLINT->cb->getDate(Date::DAY) == 1) return "NEWDAY"; if(LOCPLINT->cb->getDate(Date::DAY) != 1) return "NEWDAY"; switch(LOCPLINT->cb->getDate(Date::WEEK)) { case 1: return "NEWWEEK1"; case 2: return "NEWWEEK2"; case 3: return "NEWWEEK3"; case 4: return "NEWWEEK4"; default: return ""; } } CInfoBar::VisibleEnemyTurnInfo::VisibleEnemyTurnInfo(PlayerColor player) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared("ADSTATNX"); banner = std::make_shared("CREST58", player.getNum(), 0, 20, 51); sand = std::make_shared(99, 51, "HOURSAND"); glass = std::make_shared(99, 51, "HOURGLAS", CShowableAnim::PLAY_ONCE, 40); } CInfoBar::VisibleGameStatusInfo::VisibleGameStatusInfo() { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); //get amount of halls of each level std::vector halls(4, 0); for(auto town : LOCPLINT->towns) { int hallLevel = town->hallLevel(); //negative value means no village hall, unlikely but possible if(hallLevel >= 0) halls.at(hallLevel)++; } std::vector allies, enemies; //generate list of allies and enemies for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++) { if(LOCPLINT->cb->getPlayerStatus(PlayerColor(i), false) == EPlayerStatus::INGAME) { if(LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, PlayerColor(i)) != PlayerRelations::ENEMIES) allies.push_back(PlayerColor(i)); else enemies.push_back(PlayerColor(i)); } } //generate widgets background = std::make_shared("ADSTATIN"); allyLabel = std::make_shared(10, 106, FONT_SMALL, TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[390] + ":"); enemyLabel = std::make_shared(10, 136, FONT_SMALL, TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[391] + ":"); int posx = allyLabel->pos.w + allyLabel->pos.x - pos.x + 4; for(PlayerColor & player : allies) { auto image = std::make_shared("ITGFLAGS", player.getNum(), 0, posx, 102); posx += image->pos.w; flags.push_back(image); } posx = enemyLabel->pos.w + enemyLabel->pos.x - pos.x + 4; for(PlayerColor & player : enemies) { auto image = std::make_shared("ITGFLAGS", player.getNum(), 0, posx, 132); posx += image->pos.w; flags.push_back(image); } for(size_t i=0; i("itmtl", i, 0, 6 + 42 * (int)i , 11)); if(halls[i]) hallLabels.push_back(std::make_shared( 26 + 42 * (int)i, 64, FONT_SMALL, CENTER, Colors::WHITE, boost::lexical_cast(halls[i]))); } } CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const Component & compToDisplay, std::string message) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared("ADSTATOT", 1); comp = std::make_shared(compToDisplay); comp->moveTo(Point(pos.x+47, pos.y+50)); text = std::make_shared(message, Rect(10, 4, 160, 50), 0, FONT_SMALL, CENTER, Colors::WHITE); } void CInfoBar::playNewDaySound() { if(LOCPLINT->cb->getDate(Date::DAY_OF_WEEK) != 1) // not first day of the week CCS->soundh->playSound(soundBase::newDay); else if(LOCPLINT->cb->getDate(Date::WEEK) != 1) // not first week in month CCS->soundh->playSound(soundBase::newWeek); else if(LOCPLINT->cb->getDate(Date::MONTH) != 1) // not first month CCS->soundh->playSound(soundBase::newMonth); else CCS->soundh->playSound(soundBase::newDay); } void CInfoBar::reset() { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); state = EMPTY; visibleInfo = std::make_shared(); } void CInfoBar::showSelection() { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); if(adventureInt->selection) { if(auto hero = dynamic_cast(adventureInt->selection)) { showHeroSelection(hero); return; } else if(auto town = dynamic_cast(adventureInt->selection)) { showTownSelection(town); return; } } showGameStatus();//FIXME: may be incorrect but shouldn't happen in general } void CInfoBar::tick() { removeUsedEvents(TIME); if(GH.topInt() == adventureInt) showSelection(); } void CInfoBar::clickLeft(tribool down, bool previousState) { if(down) { if(state == HERO || state == TOWN) showGameStatus(); else if(state == GAME) showDate(); else showSelection(); } } void CInfoBar::clickRight(tribool down, bool previousState) { adventureInt->handleRightClick(CGI->generaltexth->allTexts[109], down); } void CInfoBar::hover(bool on) { if(on) GH.statusbar->setText(CGI->generaltexth->zelp[292].first); else GH.statusbar->clear(); } CInfoBar::CInfoBar(const Rect & position) : CIntObject(LCLICK | RCLICK | HOVER, position.topLeft()), state(EMPTY) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); pos.w = position.w; pos.h = position.h; reset(); } void CInfoBar::showDate() { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); playNewDaySound(); state = DATE; visibleInfo = std::make_shared(); setTimer(3000); redraw(); } void CInfoBar::showComponent(const Component & comp, std::string message) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); state = COMPONENT; visibleInfo = std::make_shared(comp, message); setTimer(3000); redraw(); } void CInfoBar::startEnemyTurn(PlayerColor color) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); state = AITURN; visibleInfo = std::make_shared(color); redraw(); } void CInfoBar::showHeroSelection(const CGHeroInstance * hero) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); if(!hero) { reset(); } else { state = HERO; visibleInfo = std::make_shared(hero); } redraw(); } void CInfoBar::showTownSelection(const CGTownInstance * town) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); if(!town) { reset(); } else { state = TOWN; visibleInfo = std::make_shared(town); } redraw(); } void CInfoBar::showGameStatus() { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); state = GAME; visibleInfo = std::make_shared(); setTimer(3000); redraw(); } CInGameConsole::CInGameConsole() : CIntObject(KEYBOARD | TEXTINPUT), prevEntDisp(-1), defaultTimeout(10000), maxDisplayedTexts(10) { } void CInGameConsole::show(SDL_Surface * to) { int number = 0; std::vector >::iterator> toDel; boost::unique_lock lock(texts_mx); for(auto it = texts.begin(); it != texts.end(); ++it, ++number) { Point leftBottomCorner(0, screen->h); if(LOCPLINT->battleInt) { leftBottomCorner = LOCPLINT->battleInt->pos.bottomLeft(); } graphics->fonts[FONT_MEDIUM]->renderTextLeft(to, it->first, Colors::GREEN, Point(leftBottomCorner.x + 50, leftBottomCorner.y - (int)texts.size() * 20 - 80 + number*20)); if((int)(SDL_GetTicks() - it->second) > defaultTimeout) { toDel.push_back(it); } } for(auto & elem : toDel) { texts.erase(elem); } } void CInGameConsole::print(const std::string &txt) { boost::unique_lock lock(texts_mx); int lineLen = conf.go()->ac.outputLineLength; if(txt.size() < lineLen) { texts.push_back(std::make_pair(txt, SDL_GetTicks())); if(texts.size() > maxDisplayedTexts) { texts.pop_front(); } } else { assert(lineLen); for(int g=0; g maxDisplayedTexts) { texts.pop_front(); } } } } void CInGameConsole::keyPressed (const SDL_KeyboardEvent & key) { if(key.type != SDL_KEYDOWN) return; if(!captureAllKeys && key.keysym.sym != SDLK_TAB) return; //because user is not entering any text switch(key.keysym.sym) { case SDLK_TAB: case SDLK_ESCAPE: { if(captureAllKeys) { captureAllKeys = false; endEnteringText(false); } else if(SDLK_TAB == key.keysym.sym) { captureAllKeys = true; startEnteringText(); } break; } case SDLK_RETURN: //enter key { if(enteredText.size() > 0 && captureAllKeys) { captureAllKeys = false; endEnteringText(true); CCS->soundh->playSound("CHAT"); } break; } case SDLK_BACKSPACE: { if(enteredText.size() > 1) { Unicode::trimRight(enteredText,2); enteredText += '_'; refreshEnteredText(); } break; } case SDLK_UP: //up arrow { if(previouslyEntered.size() == 0) break; if(prevEntDisp == -1) { prevEntDisp = static_cast(previouslyEntered.size() - 1); enteredText = previouslyEntered[prevEntDisp] + "_"; refreshEnteredText(); } else if( prevEntDisp > 0) { --prevEntDisp; enteredText = previouslyEntered[prevEntDisp] + "_"; refreshEnteredText(); } break; } case SDLK_DOWN: //down arrow { if(prevEntDisp != -1 && prevEntDisp+1 < previouslyEntered.size()) { ++prevEntDisp; enteredText = previouslyEntered[prevEntDisp] + "_"; refreshEnteredText(); } else if(prevEntDisp+1 == previouslyEntered.size()) //useful feature { prevEntDisp = -1; enteredText = "_"; refreshEnteredText(); } break; } default: { break; } } } void CInGameConsole::textInputed(const SDL_TextInputEvent & event) { if(!captureAllKeys || enteredText.size() == 0) return; enteredText.resize(enteredText.size()-1); enteredText += event.text; enteredText += "_"; refreshEnteredText(); } void CInGameConsole::textEdited(const SDL_TextEditingEvent & event) { //do nothing here } void CInGameConsole::startEnteringText() { CSDL_Ext::startTextInput(&pos); enteredText = "_"; if(GH.topInt() == adventureInt) { GH.statusbar->alignment = TOPLEFT; GH.statusbar->setText(enteredText); //Prevent changes to the text from mouse interaction with the adventure map GH.statusbar->lock(true); } else if(LOCPLINT->battleInt) { LOCPLINT->battleInt->console->ingcAlter = enteredText; } } void CInGameConsole::endEnteringText(bool printEnteredText) { CSDL_Ext::stopTextInput(); prevEntDisp = -1; if(printEnteredText) { std::string txt = enteredText.substr(0, enteredText.size()-1); LOCPLINT->cb->sendMessage(txt, LOCPLINT->getSelection()); previouslyEntered.push_back(txt); //print(txt); } enteredText = ""; if(GH.topInt() == adventureInt) { GH.statusbar->alignment = CENTER; GH.statusbar->lock(false); GH.statusbar->clear(); } else if(LOCPLINT->battleInt) { LOCPLINT->battleInt->console->ingcAlter = ""; } } void CInGameConsole::refreshEnteredText() { if(GH.topInt() == adventureInt) { GH.statusbar->lock(false); GH.statusbar->clear(); GH.statusbar->setText(enteredText); GH.statusbar->lock(true); } else if(LOCPLINT->battleInt) { LOCPLINT->battleInt->console->ingcAlter = enteredText; } } CAdvMapPanel::CAdvMapPanel(SDL_Surface * bg, Point position) : CIntObject(), background(bg) { defActions = 255; recActions = 255; pos.x += position.x; pos.y += position.y; if (bg) { pos.w = bg->w; pos.h = bg->h; } } CAdvMapPanel::~CAdvMapPanel() { if (background) SDL_FreeSurface(background); } void CAdvMapPanel::addChildColorableButton(std::shared_ptr button) { colorableButtons.push_back(button); addChildToPanel(button, ACTIVATE | DEACTIVATE); } void CAdvMapPanel::setPlayerColor(const PlayerColor & clr) { for(auto & button : colorableButtons) { button->setPlayerColor(clr); } } void CAdvMapPanel::showAll(SDL_Surface * to) { if(background) blitAt(background, pos.x, pos.y, to); CIntObject::showAll(to); } void CAdvMapPanel::addChildToPanel(std::shared_ptr obj, ui8 actions) { otherObjects.push_back(obj); obj->recActions |= actions | SHOWALL; obj->recActions &= ~DISPOSE; addChild(obj.get(), false); } CAdvMapWorldViewPanel::CAdvMapWorldViewPanel(std::shared_ptr _icons, SDL_Surface * bg, Point position, int spaceBottom, const PlayerColor &color) : CAdvMapPanel(bg, position), icons(_icons) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); int fillerHeight = bg ? spaceBottom - pos.y - pos.h : 0; if(fillerHeight > 0) { backgroundFiller = std::make_shared("DIBOXBCK", Rect(0, pos.h, pos.w, fillerHeight)); } } CAdvMapWorldViewPanel::~CAdvMapWorldViewPanel() = default; void CAdvMapWorldViewPanel::recolorIcons(const PlayerColor & color, int indexOffset) { assert(iconsData.size() == currentIcons.size()); for(size_t idx = 0; idx < iconsData.size(); idx++) { const auto & data = iconsData.at(idx); currentIcons[idx]->setFrame(data.first + indexOffset); } } void CAdvMapWorldViewPanel::addChildIcon(std::pair data, int indexOffset) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); iconsData.push_back(data); currentIcons.push_back(std::make_shared(icons, data.first + indexOffset, 0, data.second.x, data.second.y)); }