diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 7ee7707ff..e06d5bd10 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -784,6 +784,14 @@ StackQueue::StackQueue(bool Embedded, BattleInterface & owner) void StackQueue::show(SDL_Surface * to) { + auto unitIdsToHighlight = owner.stacksController->getHoveredStacksUnitIds(); + + for(auto & stackBox : stackBoxes) + { + bool isBoundUnitCurrentlyHovered = vstd::contains(unitIdsToHighlight, stackBox->getBoundUnitID()); + stackBox->toggleHighlight(isBoundUnitCurrentlyHovered); + } + if (embedded) showAll(to); CIntObject::show(to); @@ -812,8 +820,21 @@ int32_t StackQueue::getSiegeShooterIconID() return owner.siegeController->getSiegedTown()->town->faction->getIndex(); } +boost::optional StackQueue::getHoveredUnitIdIfAny() const +{ + for(const auto & stackBox : stackBoxes) + { + if(stackBox->hovered || stackBox->mouseState(EIntObjMouseBtnType::RIGHT)) + { + return stackBox->getBoundUnitID(); + } + } + + return boost::none; +} + StackQueue::StackBox::StackBox(StackQueue * owner): - owner(owner) + CIntObject(RCLICK | HOVER), owner(owner) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared(owner->embedded ? "StackQueueSmall" : "StackQueueLarge"); @@ -843,6 +864,7 @@ void StackQueue::StackBox::setUnit(const battle::Unit * unit, size_t turn) { if(unit) { + boundUnitID = unit->unitId(); background->colorize(unit->unitOwner()); icon->visible = true; @@ -877,6 +899,7 @@ void StackQueue::StackBox::setUnit(const battle::Unit * unit, size_t turn) } else { + boundUnitID = boost::none; background->colorize(PlayerColor::NEUTRAL); icon->visible = false; icon->setFrame(0); @@ -886,3 +909,21 @@ void StackQueue::StackBox::setUnit(const battle::Unit * unit, size_t turn) stateIcon->visible = false; } } + +boost::optional StackQueue::StackBox::getBoundUnitID() const +{ + return boundUnitID; +} + +void StackQueue::StackBox::toggleHighlight(bool value) +{ + highlighted = value; +} + +void StackQueue::StackBox::show(SDL_Surface *to) +{ + if(highlighted) + CSDL_Ext::drawBorder(to, background->pos.x, background->pos.y, background->pos.w, background->pos.h, { 0, 255, 255, 255 }, 2); + + CIntObject::show(to); +} diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index 5413a1399..b1fe272fa 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -196,14 +196,21 @@ class StackQueue : public CIntObject class StackBox : public CIntObject { StackQueue * owner; + boost::optional boundUnitID; + bool highlighted = false; + public: std::shared_ptr background; std::shared_ptr icon; std::shared_ptr amount; std::shared_ptr stateIcon; - void setUnit(const battle::Unit * unit, size_t turn = 0); StackBox(StackQueue * owner); + void setUnit(const battle::Unit * unit, size_t turn = 0); + void toggleHighlight(bool value); + boost::optional getBoundUnitID() const; + + void show(SDL_Surface * to) override; }; static const int QUEUE_SIZE = 10; @@ -220,6 +227,7 @@ public: StackQueue(bool Embedded, BattleInterface & owner); void update(); + boost::optional getHoveredUnitIdIfAny() const; void show(SDL_Surface * to) override; }; diff --git a/client/battle/BattleStacksController.cpp b/client/battle/BattleStacksController.cpp index 8e3add970..8f74a098a 100644 --- a/client/battle/BattleStacksController.cpp +++ b/client/battle/BattleStacksController.cpp @@ -896,6 +896,12 @@ std::vector BattleStacksController::selectHoveredStacks() if(owner.getAnimationCondition(EAnimationEvents::ACTION) == true) return {}; + auto hoveredQueueUnitId = owner.windowObject->getQueueHoveredUnitId(); + if(hoveredQueueUnitId.is_initialized()) + { + return { owner.curInt->cb->battleGetStackByID(hoveredQueueUnitId.value(), true) }; + } + auto hoveredHex = owner.fieldController->getHoveredHex(); if (!hoveredHex.isValid()) @@ -938,3 +944,14 @@ std::vector BattleStacksController::selectHoveredStacks() return {}; } + +const std::vector BattleStacksController::getHoveredStacksUnitIds() const +{ + auto result = std::vector(); + for (auto const * stack : mouseHoveredStacks) + { + result.push_back(stack->unitId()); + } + + return result; +} diff --git a/client/battle/BattleStacksController.h b/client/battle/BattleStacksController.h index 7963c8c23..772aea6b3 100644 --- a/client/battle/BattleStacksController.h +++ b/client/battle/BattleStacksController.h @@ -70,7 +70,7 @@ class BattleStacksController /// currently active stack; nullptr - no one const CStack *activeStack; - /// stacks below mouse pointer (multiple stacks possible while spellcasting), used for border animation + /// stacks or their battle queue images below mouse pointer (multiple stacks possible while spellcasting), used for border animation std::vector mouseHoveredStacks; ///when animation is playing, we should wait till the end to make the next stack active; nullptr of none @@ -144,6 +144,7 @@ public: const CStack* getActiveStack() const; const CStack* getSelectedStack() const; + const std::vector getHoveredStacksUnitIds() const; void update(); diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 4279352bc..7bf7424e7 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -553,6 +553,11 @@ void BattleWindow::blockUI(bool on) } } +boost::optional BattleWindow::getQueueHoveredUnitId() +{ + return queue->getHoveredUnitIdIfAny(); +} + void BattleWindow::showAll(SDL_Surface *to) { CIntObject::showAll(to); diff --git a/client/battle/BattleWindow.h b/client/battle/BattleWindow.h index 696f92a85..dd80585c4 100644 --- a/client/battle/BattleWindow.h +++ b/client/battle/BattleWindow.h @@ -74,6 +74,9 @@ public: /// Refresh queue after turn order changes void updateQueue(); + /// Get mouse-hovered battle queue unit ID if any found + boost::optional getQueueHoveredUnitId(); + void activate() override; void deactivate() override; void keyPressed(const SDL_KeyboardEvent & key) override; diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index 0e0753bee..17b117a1a 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -479,23 +479,27 @@ void CSDL_Ext::drawLine(SDL_Surface * sur, int x1, int y1, int x2, int y2, const } } -void CSDL_Ext::drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const SDL_Color &color) +void CSDL_Ext::drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const SDL_Color &color, int depth) { - for(int i = 0; i < w; i++) + depth = std::max(1, depth); + for(int depthIterator = 0; depthIterator < depth; depthIterator++) { - CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur,x+i,y,color.r,color.g,color.b); - CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur,x+i,y+h-1,color.r,color.g,color.b); - } - for(int i = 0; i < h; i++) - { - CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur,x,y+i,color.r,color.g,color.b); - CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur,x+w-1,y+i,color.r,color.g,color.b); + for(int i = 0; i < w; i++) + { + CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur,x+i,y+depthIterator,color.r,color.g,color.b); + CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur,x+i,y+h-1-depthIterator,color.r,color.g,color.b); + } + for(int i = 0; i < h; i++) + { + CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur,x+depthIterator,y+i,color.r,color.g,color.b); + CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur,x+w-1-depthIterator,y+i,color.r,color.g,color.b); + } } } -void CSDL_Ext::drawBorder( SDL_Surface * sur, const Rect &r, const SDL_Color &color ) +void CSDL_Ext::drawBorder( SDL_Surface * sur, const Rect &r, const SDL_Color &color, int depth) { - drawBorder(sur, r.x, r.y, r.w, r.h, color); + drawBorder(sur, r.x, r.y, r.w, r.h, color, depth); } void CSDL_Ext::drawDashedBorder(SDL_Surface * sur, const Rect &r, const SDL_Color &color) diff --git a/client/renderSDL/SDL_Extensions.h b/client/renderSDL/SDL_Extensions.h index 0be7d1d45..14928b394 100644 --- a/client/renderSDL/SDL_Extensions.h +++ b/client/renderSDL/SDL_Extensions.h @@ -152,8 +152,8 @@ typedef void (*TColorPutterAlpha)(uint8_t *&ptr, const uint8_t & R, const uint8_ void update(SDL_Surface * what = screen); //updates whole surface (default - main screen) void drawLine(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2); - void drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const SDL_Color &color); - void drawBorder(SDL_Surface * sur, const Rect &r, const SDL_Color &color); + void drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const SDL_Color &color, int depth = 1); + void drawBorder(SDL_Surface * sur, const Rect &r, const SDL_Color &color, int depth = 1); void drawDashedBorder(SDL_Surface * sur, const Rect &r, const SDL_Color &color); void setPlayerColor(SDL_Surface * sur, PlayerColor player); //sets correct color of flags; -1 for neutral std::string processStr(std::string str, std::vector & tor); //replaces %s in string