diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 8d7ea219a..6927611f0 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -583,6 +583,7 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town) castleInt->garr->setArmy(town->visitingHero, 1); castleInt->garr->recreateSlots(); castleInt->heroes->update(); + castleInt->redraw(); } for (auto isa : GH.listInt) { @@ -591,9 +592,9 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town) { ki->townChanged(town); ki->updateGarrisons(); + ki->redraw(); } } - GH.totalRedraw(); } void CPlayerInterface::heroVisitsTown(const CGHeroInstance* hero, const CGTownInstance * town) { @@ -1510,6 +1511,7 @@ void CPlayerInterface::objectRemoved(const CGObjectInstance * obj) const CGHeroInstance * h = static_cast(obj); heroKilled(h); } + GH.fakeMouseMove(); } void CPlayerInterface::objectRemovedAfter() @@ -1559,7 +1561,6 @@ void CPlayerInterface::update() } assert(adventureInt); - assert(adventureInt->selection); // Handles mouse and key input GH.updateTime(); @@ -2032,7 +2033,9 @@ bool CPlayerInterface::capturedAllEvents() return true; } - if (ignoreEvents) + bool needToLockAdventureMap = adventureInt->active && CGI->mh->hasOngoingAnimations(); + + if (ignoreEvents || needToLockAdventureMap) { boost::unique_lock un(eventsM); while(!SDLEventsQueue.empty()) diff --git a/client/ClientCommandManager.h b/client/ClientCommandManager.h index 61b2175fb..14ce180af 100644 --- a/client/ClientCommandManager.h +++ b/client/ClientCommandManager.h @@ -17,7 +17,7 @@ class CIntObject; class ClientCommandManager //take mantis #2292 issue about account if thinking about handling cheats from command-line { - bool currentCallFromIngameConsole; + bool currentCallFromIngameConsole = false; void giveTurn(const PlayerColor &color); void printInfoAboutInterfaceObject(const CIntObject *obj, int level); diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 9806932bb..9c8dfb8df 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -514,6 +514,11 @@ void ApplyClientNetPackVisitor::visitNewStructures(NewStructures & pack) { callInterfaceIfPresent(cl, town->tempOwner, &IGameEventsReceiver::buildChanged, town, id, 1); } + + // invalidate section of map view with our object and force an update + CGI->mh->onObjectInstantRemove(town); + CGI->mh->onObjectInstantAdd(town); + } void ApplyClientNetPackVisitor::visitRazeStructures(RazeStructures & pack) { @@ -522,6 +527,10 @@ void ApplyClientNetPackVisitor::visitRazeStructures(RazeStructures & pack) { callInterfaceIfPresent(cl, town->tempOwner, &IGameEventsReceiver::buildChanged, town, id, 2); } + + // invalidate section of map view with our object and force an update + CGI->mh->onObjectInstantRemove(town); + CGI->mh->onObjectInstantAdd(town); } void ApplyClientNetPackVisitor::visitSetAvailableCreatures(SetAvailableCreatures & pack) @@ -607,7 +616,7 @@ void ApplyClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty & pack) if (pack.what == ObjProperty::OWNER) { - // invalidate section of map view with our objec and force an update with new flag color + // invalidate section of map view with our object and force an update with new flag color CGI->mh->onObjectInstantRemove(gs.getObjInstance(pack.id)); CGI->mh->onObjectInstantAdd(gs.getObjInstance(pack.id)); } diff --git a/client/adventureMap/CAdvMapInt.cpp b/client/adventureMap/CAdvMapInt.cpp index b33697bd3..841d973d3 100644 --- a/client/adventureMap/CAdvMapInt.cpp +++ b/client/adventureMap/CAdvMapInt.cpp @@ -986,7 +986,6 @@ void CAdvMapInt::initializeNewTurn() { heroList->update(); townList->update(); - mapAudio->onPlayerTurnStarted(); const CGHeroInstance * heroToSelect = nullptr; @@ -1017,6 +1016,7 @@ void CAdvMapInt::initializeNewTurn() updateNextHero(nullptr); showAll(screen); + mapAudio->onPlayerTurnStarted(); if(settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown()) { @@ -1404,6 +1404,7 @@ void CAdvMapInt::aiTurnStarted() mapAudio->onEnemyTurnStarted(); adventureInt->minimap->setAIRadar(true); adventureInt->infoBar->startEnemyTurn(LOCPLINT->cb->getCurrentPlayer()); + adventureInt->minimap->showAll(screen);//force refresh on inactive object adventureInt->infoBar->showAll(screen);//force refresh on inactive object } diff --git a/client/adventureMap/CInGameConsole.cpp b/client/adventureMap/CInGameConsole.cpp index e62ec3e39..ed4588ea4 100644 --- a/client/adventureMap/CInGameConsole.cpp +++ b/client/adventureMap/CInGameConsole.cpp @@ -24,71 +24,85 @@ #include "../../lib/TextOperations.h" #include "../../lib/mapObjects/CArmedInstance.h" -#include - CInGameConsole::CInGameConsole() - : CIntObject(KEYBOARD | TEXTINPUT), - prevEntDisp(-1), - defaultTimeout(10000), - maxDisplayedTexts(10) + : CIntObject(KEYBOARD | TIME | TEXTINPUT) + , prevEntDisp(-1) { + type |= REDRAW_PARENT; +} + +void CInGameConsole::showAll(SDL_Surface * to) +{ + show(to); } 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) + for(auto & text : texts) { Point leftBottomCorner(0, pos.h); + Point textPosition(leftBottomCorner.x + 50, leftBottomCorner.y - texts.size() * 20 - 80 + number * 20); - graphics->fonts[FONT_MEDIUM]->renderTextLeft(to, it->first, Colors::GREEN, - Point(leftBottomCorner.x + 50, leftBottomCorner.y - (int)texts.size() * 20 - 80 + number*20)); + graphics->fonts[FONT_MEDIUM]->renderTextLeft(to, text.text, Colors::GREEN, textPosition ); - if((int)(SDL_GetTicks() - it->second) > defaultTimeout) - { - toDel.push_back(it); - } - } - - for(auto & elem : toDel) - { - texts.erase(elem); + number++; } } -void CInGameConsole::print(const std::string &txt) +void CInGameConsole::tick(uint32_t msPassed) { - boost::unique_lock lock(texts_mx); - int lineLen = conf.go()->ac.outputLineLength; - - if(txt.size() < lineLen) + size_t sizeBefore = texts.size(); { - texts.push_back(std::make_pair(txt, SDL_GetTicks())); - if(texts.size() > maxDisplayedTexts) - { - texts.pop_front(); - } - } - else - { - assert(lineLen); - for(int g=0; g lock(texts_mx); - texts.push_back(std::make_pair(part, SDL_GetTicks())); - if(texts.size() > maxDisplayedTexts) + for(auto & text : texts) + text.timeOnScreen += msPassed; + + vstd::erase_if( + texts, + [&](const auto & value) { - texts.pop_front(); + return value.timeOnScreen > defaultTimeout; + } + ); + } + + if(sizeBefore != texts.size()) + GH.totalRedraw(); // FIXME: ingame console has no parent widget set +} + +void CInGameConsole::print(const std::string & txt) +{ + // boost::unique_lock scope + { + boost::unique_lock lock(texts_mx); + int lineLen = conf.go()->ac.outputLineLength; + + if(txt.size() < lineLen) + { + texts.push_back({txt, 0}); + } + else + { + assert(lineLen); + for(int g = 0; g < txt.size() / lineLen + 1; ++g) + { + std::string part = txt.substr(g * lineLen, lineLen); + if(part.empty()) + break; + + texts.push_back({part, 0}); } } + + while(texts.size() > maxDisplayedTexts) + texts.erase(texts.begin()); } + + GH.totalRedraw(); // FIXME: ingame console has no parent widget set } void CInGameConsole::keyPressed (const SDL_Keycode & key) @@ -136,7 +150,7 @@ void CInGameConsole::keyPressed (const SDL_Keycode & key) } case SDLK_UP: //up arrow { - if(previouslyEntered.size() == 0) + if(previouslyEntered.empty()) break; if(prevEntDisp == -1) @@ -178,7 +192,7 @@ void CInGameConsole::keyPressed (const SDL_Keycode & key) void CInGameConsole::textInputed(const std::string & inputtedText) { - if(!captureAllKeys || enteredText.size() == 0) + if(!captureAllKeys || enteredText.empty()) return; enteredText.resize(enteredText.size()-1); diff --git a/client/adventureMap/CInGameConsole.h b/client/adventureMap/CInGameConsole.h index 93d5a204d..0d5266967 100644 --- a/client/adventureMap/CInGameConsole.h +++ b/client/adventureMap/CInGameConsole.h @@ -14,20 +14,39 @@ class CInGameConsole : public CIntObject { private: - std::list< std::pair< std::string, uint32_t > > texts; //list - boost::mutex texts_mx; // protects texts - std::vector< std::string > previouslyEntered; //previously entered texts, for up/down arrows to work - int prevEntDisp; //displayed entry from previouslyEntered - if none it's -1 - int defaultTimeout; //timeout for new texts (in ms) - int maxDisplayedTexts; //hiw many texts can be displayed simultaneously + struct TextState + { + std::string text; + uint32_t timeOnScreen; + }; + + /// Currently visible texts in the overlay + std::vector texts; + + /// protects texts + boost::mutex texts_mx; + + /// previously entered texts, for up/down arrows to work + std::vector previouslyEntered; + + /// displayed entry from previouslyEntered - if none it's -1 + int prevEntDisp; + + /// timeout for new texts (in ms) + static constexpr int defaultTimeout = 10000; + + /// how many texts can be displayed simultaneously + static constexpr int maxDisplayedTexts = 10; std::weak_ptr currentStatusBar; std::string enteredText; public: - void print(const std::string &txt); + void print(const std::string & txt); + void tick(uint32_t msPassed) override; void show(SDL_Surface * to) override; + void showAll(SDL_Surface * to) override; void keyPressed(const SDL_Keycode & key) override; void textInputed(const std::string & enteredText) override; void textEdited(const std::string & enteredText) override; diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index a3999c5e5..d1c1b1b6e 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -254,11 +254,21 @@ void CInfoBar::showSelection() showGameStatus();//FIXME: may be incorrect but shouldn't happen in general } -void CInfoBar::tick() +void CInfoBar::tick(uint32_t msPassed) { - removeUsedEvents(TIME); - if(GH.topInt() == adventureInt) - popComponents(true); + assert(timerCounter > 0); + + if (msPassed >= timerCounter) + { + timerCounter = 0; + removeUsedEvents(TIME); + if(GH.topInt() == adventureInt) + popComponents(true); + } + else + { + timerCounter -= msPassed; + } } void CInfoBar::clickLeft(tribool down, bool previousState) @@ -290,6 +300,7 @@ void CInfoBar::hover(bool on) CInfoBar::CInfoBar(const Rect & position) : CIntObject(LCLICK | RCLICK | HOVER, position.topLeft()), + timerCounter(0), state(EMPTY) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); @@ -302,6 +313,14 @@ CInfoBar::CInfoBar(const Point & position): CInfoBar(Rect(position.x, position.y { } + +void CInfoBar::setTimer(uint32_t msToTrigger) +{ + if (!(active & TIME)) + addUsedEvents(TIME); + timerCounter = msToTrigger; +} + void CInfoBar::showDate() { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); @@ -312,7 +331,6 @@ void CInfoBar::showDate() redraw(); } - void CInfoBar::pushComponents(const std::vector & components, std::string message, int timer) { auto actualPush = [&](const std::vector & components, std::string message, int timer, size_t max){ diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index df7d1b2ef..3a507e879 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -138,6 +138,7 @@ private: std::shared_ptr visibleInfo; EState state; + uint32_t timerCounter; bool shouldPopAll = false; std::queue> componentsQueue; @@ -151,13 +152,14 @@ private: //removes all information about current state, deactivates timer (if any) void reset(); - void tick() override; + void tick(uint32_t msPassed) override; void clickLeft(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override; void hover(bool on) override; void playNewDaySound(); + void setTimer(uint32_t msToTrigger); public: CInfoBar(const Rect & pos); CInfoBar(const Point & pos); diff --git a/client/adventureMap/CList.cpp b/client/adventureMap/CList.cpp index e60b08c71..d09f53dee 100644 --- a/client/adventureMap/CList.cpp +++ b/client/adventureMap/CList.cpp @@ -77,7 +77,7 @@ void CList::CListItem::onSelect(bool on) if(on) selection = genSelection(); select(on); - GH.totalRedraw(); + redraw(); } CList::CList(int Size, Point position, std::string btnUp, std::string btnDown, size_t listAmount, int helpUp, int helpDown, CListBox::CreateFunc create) diff --git a/client/adventureMap/CMinimap.cpp b/client/adventureMap/CMinimap.cpp index 22d7ac72c..b53e58f4c 100644 --- a/client/adventureMap/CMinimap.cpp +++ b/client/adventureMap/CMinimap.cpp @@ -222,10 +222,7 @@ void CMinimap::setAIRadar(bool on) aiShield->disable(); update(); } - - // this may happen during AI turn when this interface is inactive - // force redraw in order to properly update interface - GH.totalRedraw(); + redraw(); } void CMinimap::updateTile(const int3 &pos) diff --git a/client/gui/CGuiHandler.cpp b/client/gui/CGuiHandler.cpp index 1ebc32c11..3135104fe 100644 --- a/client/gui/CGuiHandler.cpp +++ b/client/gui/CGuiHandler.cpp @@ -192,7 +192,7 @@ void CGuiHandler::updateTime() for (auto & elem : hlp) { if(!vstd::contains(timeinterested,elem)) continue; - (elem)->onTimer(ms); + (elem)->tick(ms); } } diff --git a/client/gui/CIntObject.cpp b/client/gui/CIntObject.cpp index 8e5721e44..79ed430cd 100644 --- a/client/gui/CIntObject.cpp +++ b/client/gui/CIntObject.cpp @@ -29,7 +29,6 @@ CIntObject::CIntObject(int used_, Point pos_): active(active_m) { hovered = captureAllKeys = strongInterest = false; - toNextTick = timerDelay = 0; used = used_; recActions = defActions = GH.defActionsDef; @@ -60,24 +59,6 @@ CIntObject::~CIntObject() parent_m->removeChild(this); } -void CIntObject::setTimer(int msToTrigger) -{ - if (!(active & TIME)) - activate(TIME); - toNextTick = timerDelay = msToTrigger; - used |= TIME; -} - -void CIntObject::onTimer(int timePassed) -{ - toNextTick -= timePassed; - if (toNextTick < 0) - { - toNextTick += timerDelay; - tick(); - } -} - void CIntObject::show(SDL_Surface * to) { if(defActions & UPDATE) diff --git a/client/gui/CIntObject.h b/client/gui/CIntObject.h index 80017d32b..ff26c96d4 100644 --- a/client/gui/CIntObject.h +++ b/client/gui/CIntObject.h @@ -65,14 +65,8 @@ class CIntObject : public IShowActivatable //interface object { ui16 used;//change via addUsed() or delUsed - //time handling - int toNextTick; - int timerDelay; - std::map currentMouseState; - void onTimer(int timePassed); - //non-const versions of fields to allow changing them in CIntObject CIntObject *parent_m; //parent object ui16 active_m; @@ -129,8 +123,7 @@ public: virtual void mouseMoved (const Point & cursorPosition){} //time handling - void setTimer(int msToTrigger);//set timer delay and activate timer if needed. - virtual void tick(){} + virtual void tick(uint32_t msPassed){} //mouse wheel virtual void wheelScrolled(bool down, bool in){} diff --git a/client/mapView/MapView.cpp b/client/mapView/MapView.cpp index 79483b4df..730ec0b6b 100644 --- a/client/mapView/MapView.cpp +++ b/client/mapView/MapView.cpp @@ -60,14 +60,14 @@ BasicMapView::BasicMapView(const Point & offset, const Point & dimensions) void BasicMapView::render(Canvas & target, bool fullUpdate) { Canvas targetClipped(target, pos); - - controller->update(GH.mainFPSmng->getElapsedMilliseconds()); tilesCache->update(controller->getContext()); tilesCache->render(controller->getContext(), targetClipped, fullUpdate); } void BasicMapView::show(SDL_Surface * to) { + controller->update(GH.mainFPSmng->getElapsedMilliseconds()); + Canvas target(to); CSDL_Ext::CClipRectGuard guard(to, pos); render(target, false); @@ -75,6 +75,8 @@ void BasicMapView::show(SDL_Surface * to) void BasicMapView::showAll(SDL_Surface * to) { + controller->update(0); + Canvas target(to); CSDL_Ext::CClipRectGuard guard(to, pos); render(target, true); diff --git a/client/mapView/MapViewActions.cpp b/client/mapView/MapViewActions.cpp index 854edd036..40402cb8f 100644 --- a/client/mapView/MapViewActions.cpp +++ b/client/mapView/MapViewActions.cpp @@ -24,7 +24,6 @@ MapViewActions::MapViewActions(MapView & owner, const std::shared_ptr & model) : model(model) , owner(owner) - , curHoveredTile(-1, -1, -1) , isSwiping(false) { pos.w = model->getPixelsVisibleDimensions().x; @@ -47,17 +46,6 @@ void MapViewActions::setContext(const std::shared_ptr & con this->context = context; } -void MapViewActions::activate() -{ - CIntObject::activate(); -} - -void MapViewActions::deactivate() -{ - CIntObject::deactivate(); - curHoveredTile = int3(-1, -1, -1); //we lost info about hovered tile when disabling -} - void MapViewActions::clickLeft(tribool down, bool previousState) { if(indeterminate(down)) @@ -159,11 +147,7 @@ void MapViewActions::handleHover(const Point & cursorPosition) return; } - if(tile != curHoveredTile) - { - curHoveredTile = tile; - adventureInt->onTileHovered(tile); - } + adventureInt->onTileHovered(tile); } void MapViewActions::hover(bool on) diff --git a/client/mapView/MapViewActions.h b/client/mapView/MapViewActions.h index c2655f7b0..dc6d84e16 100644 --- a/client/mapView/MapViewActions.h +++ b/client/mapView/MapViewActions.h @@ -23,8 +23,6 @@ class MapViewActions : public CIntObject Point swipeInitialViewPos; Point swipeInitialRealPos; - int3 curHoveredTile; - MapView & owner; std::shared_ptr model; std::shared_ptr context; @@ -39,8 +37,6 @@ public: void setContext(const std::shared_ptr & context); - void activate() override; - void deactivate() override; void clickLeft(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override; void clickMiddle(tribool down, bool previousState) override; diff --git a/client/mapView/MapViewController.cpp b/client/mapView/MapViewController.cpp index 5a8276612..c8b6599c7 100644 --- a/client/mapView/MapViewController.cpp +++ b/client/mapView/MapViewController.cpp @@ -251,6 +251,26 @@ void MapViewController::fadeInObject(const CGObjectInstance * obj) void MapViewController::removeObject(const CGObjectInstance * obj) { + if (obj->ID == Obj::BOAT) + { + auto * boat = dynamic_cast(obj); + if (boat->hero) + { + view->invalidate(context, boat->hero->id); + state->removeObject(boat->hero); + } + } + + if (obj->ID == Obj::HERO) + { + auto * hero = dynamic_cast(obj); + if (hero->boat) + { + view->invalidate(context, hero->boat->id); + state->removeObject(hero->boat); + } + } + view->invalidate(context, obj->id); state->removeObject(obj); } @@ -265,7 +285,7 @@ void MapViewController::onBeforeHeroEmbark(const CGHeroInstance * obj, const int { if(isEventVisible(obj, from, dest)) { - onObjectFadeOut(obj); + fadeOutObject(obj); setViewCenter(obj->getSightCenter()); } else @@ -288,7 +308,7 @@ void MapViewController::onAfterHeroDisembark(const CGHeroInstance * obj, const i { if(isEventVisible(obj, from, dest)) { - onObjectFadeIn(obj); + fadeInObject(obj); setViewCenter(obj->getSightCenter()); } addObject(obj); diff --git a/client/mapView/mapHandler.cpp b/client/mapView/mapHandler.cpp index ac92ad0d6..8fbf66c67 100644 --- a/client/mapView/mapHandler.cpp +++ b/client/mapView/mapHandler.cpp @@ -229,5 +229,6 @@ IMapObjectObserver::IMapObjectObserver() IMapObjectObserver::~IMapObjectObserver() { - CGI->mh->removeMapObserver(this); + if (CGI && CGI->mh) + CGI->mh->removeMapObserver(this); }