From 92754e22f00cac59c4bf02de3d412df6a5d8f731 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Sun, 22 Jan 2023 20:03:11 +0100 Subject: [PATCH 1/5] Highlight stack on queue icon hovered --- client/battle/BattleInterfaceClasses.cpp | 22 +++++++++++++++++++++- client/battle/BattleInterfaceClasses.h | 3 +++ client/battle/BattleStacksController.cpp | 6 ++++++ client/battle/BattleStacksController.h | 2 +- client/battle/BattleWindow.cpp | 5 +++++ client/battle/BattleWindow.h | 3 +++ lib/battle/CBattleInfoEssentials.cpp | 15 +++++++++++++++ lib/battle/CBattleInfoEssentials.h | 1 + 8 files changed, 55 insertions(+), 2 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 599d3c5ca..5ecbb2e70 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -807,8 +807,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) + { + return stackBox->getBoundUnitID(); + } + } + + return boost::none; +} + StackQueue::StackBox::StackBox(StackQueue * owner): - owner(owner) + CIntObject(HOVER), owner(owner) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared(owner->embedded ? "StackQueueSmall" : "StackQueueLarge"); @@ -838,6 +851,7 @@ void StackQueue::StackBox::setUnit(const battle::Unit * unit, size_t turn) { if(unit) { + boundUnitID = unit->unitId(); background->colorize(unit->unitOwner()); icon->visible = true; @@ -872,6 +886,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); @@ -881,3 +896,8 @@ void StackQueue::StackBox::setUnit(const battle::Unit * unit, size_t turn) stateIcon->visible = false; } } + +boost::optional StackQueue::StackBox::getBoundUnitID() const +{ + return boundUnitID; +} diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index 5413a1399..ba6ebe4f8 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -196,6 +196,7 @@ class StackQueue : public CIntObject class StackBox : public CIntObject { StackQueue * owner; + boost::optional boundUnitID; public: std::shared_ptr background; std::shared_ptr icon; @@ -204,6 +205,7 @@ class StackQueue : public CIntObject void setUnit(const battle::Unit * unit, size_t turn = 0); StackBox(StackQueue * owner); + boost::optional getBoundUnitID() const; }; static const int QUEUE_SIZE = 10; @@ -220,6 +222,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 8ef6b8107..dabd2e6e3 100644 --- a/client/battle/BattleStacksController.cpp +++ b/client/battle/BattleStacksController.cpp @@ -897,6 +897,12 @@ std::vector BattleStacksController::selectHoveredStacks() if(owner.getAnimationCondition(EAnimationEvents::ACTION) == true) return {}; + auto hoveredQueueUnitId = owner.windowObject->getQueueHoveredUnitId(); + if(hoveredQueueUnitId.has_value()) + { + return { owner.curInt->cb->battleGetStackByUnitId(hoveredQueueUnitId.value(), true) }; + } + auto hoveredHex = owner.fieldController->getHoveredHex(); if (!hoveredHex.isValid()) diff --git a/client/battle/BattleStacksController.h b/client/battle/BattleStacksController.h index 92ffdaae0..1ccf7ceef 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 diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index f47a559ac..a780c1e49 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -550,6 +550,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/lib/battle/CBattleInfoEssentials.cpp b/lib/battle/CBattleInfoEssentials.cpp index e7e0062aa..e72ed608f 100644 --- a/lib/battle/CBattleInfoEssentials.cpp +++ b/lib/battle/CBattleInfoEssentials.cpp @@ -185,6 +185,21 @@ const CStack* CBattleInfoEssentials::battleGetStackByID(int ID, bool onlyAlive) return stacks[0]; } +const CStack* CBattleInfoEssentials::battleGetStackByUnitId(int unitId, bool onlyAlive) const +{ + RETURN_IF_NOT_BATTLE(nullptr); + + auto stacks = battleGetStacksIf([=](const CStack * s) + { + return s->unitId() == unitId && (!onlyAlive || s->alive()); + }); + + if(stacks.empty()) + return nullptr; + else + return stacks[0]; +} + bool CBattleInfoEssentials::battleDoWeKnowAbout(ui8 side) const { RETURN_IF_NOT_BATTLE(false); diff --git a/lib/battle/CBattleInfoEssentials.h b/lib/battle/CBattleInfoEssentials.h index 117e352b3..f2ee32893 100644 --- a/lib/battle/CBattleInfoEssentials.h +++ b/lib/battle/CBattleInfoEssentials.h @@ -102,6 +102,7 @@ public: TStacks battleGetAllStacks(bool includeTurrets = false) const; const CStack * battleGetStackByID(int ID, bool onlyAlive = true) const; //returns stack info by given ID + const CStack * battleGetStackByUnitId(int unitId, bool onlyAlive = true) const; //returns stack info by given ID bool battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattlePerspective::BattlePerspective side) const; ///returns player that controls given stack; mind control included From bd1bd5064aff5b5571d1e86202b413b32a880c11 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Sun, 22 Jan 2023 20:06:10 +0100 Subject: [PATCH 2/5] Highlight logic for queue icon on stack hover, lacks visual representation --- client/battle/BattleInterfaceClasses.cpp | 27 ++++++++++++++++++++++++ client/battle/BattleInterfaceClasses.h | 7 +++++- client/battle/BattleStacksController.cpp | 11 ++++++++++ client/battle/BattleStacksController.h | 1 + 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 5ecbb2e70..b9a91b695 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -779,6 +779,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); @@ -901,3 +909,22 @@ 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) + { + //TODO: logic to perform on image that changes it visually when unit highlighted + } + else + { + //TODO: logic to perform on image that changes it visually when unit loses highlight + } + + CIntObject::show(to); +} diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index ba6ebe4f8..b1fe272fa 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -197,15 +197,20 @@ class StackQueue : 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; diff --git a/client/battle/BattleStacksController.cpp b/client/battle/BattleStacksController.cpp index dabd2e6e3..3196c28c6 100644 --- a/client/battle/BattleStacksController.cpp +++ b/client/battle/BattleStacksController.cpp @@ -945,3 +945,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 1ccf7ceef..82c95a7b6 100644 --- a/client/battle/BattleStacksController.h +++ b/client/battle/BattleStacksController.h @@ -144,6 +144,7 @@ public: const CStack* getActiveStack() const; const CStack* getSelectedStack() const; + const std::vector getHoveredStacksUnitIds() const; void update(); From aa18118df069c64a09a3fb5fc8f63737b36f07bb Mon Sep 17 00:00:00 2001 From: Dydzio Date: Mon, 23 Jan 2023 11:34:21 +0100 Subject: [PATCH 3/5] Add simple cyan color frame on highlight + mouse right-click for mobile --- client/battle/BattleInterfaceClasses.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index b9a91b695..633a93bb6 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -819,7 +819,7 @@ boost::optional StackQueue::getHoveredUnitIdIfAny() const { for(const auto & stackBox : stackBoxes) { - if(stackBox->hovered) + if(stackBox->hovered || stackBox->mouseState(EIntObjMouseBtnType::RIGHT)) { return stackBox->getBoundUnitID(); } @@ -829,7 +829,7 @@ boost::optional StackQueue::getHoveredUnitIdIfAny() const } StackQueue::StackBox::StackBox(StackQueue * owner): - CIntObject(HOVER), owner(owner) + CIntObject(RCLICK | HOVER), owner(owner) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared(owner->embedded ? "StackQueueSmall" : "StackQueueLarge"); @@ -918,13 +918,7 @@ void StackQueue::StackBox::toggleHighlight(bool value) void StackQueue::StackBox::show(SDL_Surface *to) { if(highlighted) - { - //TODO: logic to perform on image that changes it visually when unit highlighted - } - else - { - //TODO: logic to perform on image that changes it visually when unit loses highlight - } + CSDL_Ext::drawBorder(to, background->pos.x, background->pos.y, background->pos.w, background->pos.h, { 0, 255, 255, 255 }); CIntObject::show(to); } From 4a23f93754ef3f4101a73c11c91e3b3c257498e5 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Mon, 23 Jan 2023 12:03:07 +0100 Subject: [PATCH 4/5] Give thicker frame to highlighted queue boxes --- client/battle/BattleInterfaceClasses.cpp | 2 +- client/battle/BattleStacksController.cpp | 2 +- client/gui/SDL_Extensions.cpp | 26 ++++++++++++++---------- client/gui/SDL_Extensions.h | 4 ++-- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 633a93bb6..7cca8a82b 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -918,7 +918,7 @@ void StackQueue::StackBox::toggleHighlight(bool 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 }); + 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/BattleStacksController.cpp b/client/battle/BattleStacksController.cpp index 3196c28c6..39d0a1af1 100644 --- a/client/battle/BattleStacksController.cpp +++ b/client/battle/BattleStacksController.cpp @@ -898,7 +898,7 @@ std::vector BattleStacksController::selectHoveredStacks() return {}; auto hoveredQueueUnitId = owner.windowObject->getQueueHoveredUnitId(); - if(hoveredQueueUnitId.has_value()) + if(hoveredQueueUnitId.is_initialized()) { return { owner.curInt->cb->battleGetStackByUnitId(hoveredQueueUnitId.value(), true) }; } diff --git a/client/gui/SDL_Extensions.cpp b/client/gui/SDL_Extensions.cpp index 3f326146f..5b6afe16f 100644 --- a/client/gui/SDL_Extensions.cpp +++ b/client/gui/SDL_Extensions.cpp @@ -472,23 +472,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/gui/SDL_Extensions.h b/client/gui/SDL_Extensions.h index 125ac8f8d..9af00cd13 100644 --- a/client/gui/SDL_Extensions.h +++ b/client/gui/SDL_Extensions.h @@ -150,8 +150,8 @@ typedef void (*TColorPutterAlpha)(Uint8 *&ptr, const Uint8 & R, const Uint8 & G, 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 From 68be7643f10a60e86ac4bb214f24b0d159367f7e Mon Sep 17 00:00:00 2001 From: Dydzio Date: Wed, 25 Jan 2023 13:06:13 +0100 Subject: [PATCH 5/5] Remove redundant method - code review suggestion --- client/battle/BattleStacksController.cpp | 2 +- lib/battle/CBattleInfoEssentials.cpp | 15 --------------- lib/battle/CBattleInfoEssentials.h | 1 - 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/client/battle/BattleStacksController.cpp b/client/battle/BattleStacksController.cpp index 39d0a1af1..684e07e57 100644 --- a/client/battle/BattleStacksController.cpp +++ b/client/battle/BattleStacksController.cpp @@ -900,7 +900,7 @@ std::vector BattleStacksController::selectHoveredStacks() auto hoveredQueueUnitId = owner.windowObject->getQueueHoveredUnitId(); if(hoveredQueueUnitId.is_initialized()) { - return { owner.curInt->cb->battleGetStackByUnitId(hoveredQueueUnitId.value(), true) }; + return { owner.curInt->cb->battleGetStackByID(hoveredQueueUnitId.value(), true) }; } auto hoveredHex = owner.fieldController->getHoveredHex(); diff --git a/lib/battle/CBattleInfoEssentials.cpp b/lib/battle/CBattleInfoEssentials.cpp index e72ed608f..e7e0062aa 100644 --- a/lib/battle/CBattleInfoEssentials.cpp +++ b/lib/battle/CBattleInfoEssentials.cpp @@ -185,21 +185,6 @@ const CStack* CBattleInfoEssentials::battleGetStackByID(int ID, bool onlyAlive) return stacks[0]; } -const CStack* CBattleInfoEssentials::battleGetStackByUnitId(int unitId, bool onlyAlive) const -{ - RETURN_IF_NOT_BATTLE(nullptr); - - auto stacks = battleGetStacksIf([=](const CStack * s) - { - return s->unitId() == unitId && (!onlyAlive || s->alive()); - }); - - if(stacks.empty()) - return nullptr; - else - return stacks[0]; -} - bool CBattleInfoEssentials::battleDoWeKnowAbout(ui8 side) const { RETURN_IF_NOT_BATTLE(false); diff --git a/lib/battle/CBattleInfoEssentials.h b/lib/battle/CBattleInfoEssentials.h index f2ee32893..117e352b3 100644 --- a/lib/battle/CBattleInfoEssentials.h +++ b/lib/battle/CBattleInfoEssentials.h @@ -102,7 +102,6 @@ public: TStacks battleGetAllStacks(bool includeTurrets = false) const; const CStack * battleGetStackByID(int ID, bool onlyAlive = true) const; //returns stack info by given ID - const CStack * battleGetStackByUnitId(int unitId, bool onlyAlive = true) const; //returns stack info by given ID bool battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattlePerspective::BattlePerspective side) const; ///returns player that controls given stack; mind control included