From ec55ec76b65298dfd1d603385ededd1e44f2a282 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 1 Jun 2023 17:49:42 +0300 Subject: [PATCH] Show cursor (including attack direction) when swiping in battle --- client/battle/BattleActionsController.cpp | 113 ++++++++++++++-------- client/battle/BattleFieldController.cpp | 38 +++----- client/battle/BattleFieldController.h | 8 +- client/gui/CursorHandler.h | 7 +- client/gui/EventDispatcher.cpp | 8 +- 5 files changed, 102 insertions(+), 72 deletions(-) diff --git a/client/battle/BattleActionsController.cpp b/client/battle/BattleActionsController.cpp index 1aad40afa..8ffef5b1c 100644 --- a/client/battle/BattleActionsController.cpp +++ b/client/battle/BattleActionsController.cpp @@ -160,7 +160,7 @@ void BattleActionsController::enterCreatureCastingMode() if (!isActiveStackSpellcaster()) return; - for (auto const & action : possibleActions) + for(const auto & action : possibleActions) { if (action.get() != PossiblePlayerBattleAction::NO_LOCATION) continue; @@ -203,7 +203,7 @@ std::vector BattleActionsController::getPossibleActi { BattleClientInterfaceData data; //hard to get rid of these things so for now they're required data to pass - for (auto const & spell : creatureSpells) + for(const auto & spell : creatureSpells) data.creatureSpellsToCast.push_back(spell->id); data.tacticsMode = owner.tacticsMode; @@ -219,49 +219,62 @@ void BattleActionsController::reorderPossibleActionsPriority(const CStack * stac { if(owner.tacticsMode || possibleActions.empty()) return; //this function is not supposed to be called in tactics mode or before getPossibleActionsForStack - auto assignPriority = [&](PossiblePlayerBattleAction const & item) -> uint8_t //large lambda assigning priority which would have to be part of possibleActions without it + auto assignPriority = [&](const PossiblePlayerBattleAction & item + ) -> uint8_t //large lambda assigning priority which would have to be part of possibleActions without it { switch(item.get()) { - case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE: - case PossiblePlayerBattleAction::ANY_LOCATION: - case PossiblePlayerBattleAction::NO_LOCATION: - case PossiblePlayerBattleAction::FREE_LOCATION: - case PossiblePlayerBattleAction::OBSTACLE: - if(!stack->hasBonusOfType(BonusType::NO_SPELLCAST_BY_DEFAULT) && context == MouseHoveredHexContext::OCCUPIED_HEX) - return 1; - else - return 100;//bottom priority - break; - case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL: - return 2; break; - case PossiblePlayerBattleAction::SHOOT: - return 4; break; - case PossiblePlayerBattleAction::ATTACK_AND_RETURN: - return 5; break; - case PossiblePlayerBattleAction::ATTACK: - return 6; break; - case PossiblePlayerBattleAction::WALK_AND_ATTACK: - return 7; break; - case PossiblePlayerBattleAction::MOVE_STACK: - return 8; break; - case PossiblePlayerBattleAction::CATAPULT: - return 9; break; - case PossiblePlayerBattleAction::HEAL: - return 10; break; - case PossiblePlayerBattleAction::CREATURE_INFO: - return 11; break; - case PossiblePlayerBattleAction::HERO_INFO: - return 12; break; - case PossiblePlayerBattleAction::TELEPORT: - return 13; break; - default: - assert(0); - return 200; break; + case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE: + case PossiblePlayerBattleAction::ANY_LOCATION: + case PossiblePlayerBattleAction::NO_LOCATION: + case PossiblePlayerBattleAction::FREE_LOCATION: + case PossiblePlayerBattleAction::OBSTACLE: + if(!stack->hasBonusOfType(BonusType::NO_SPELLCAST_BY_DEFAULT) && context == MouseHoveredHexContext::OCCUPIED_HEX) + return 1; + else + return 100; //bottom priority + break; + case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL: + return 2; + break; + case PossiblePlayerBattleAction::SHOOT: + return 4; + break; + case PossiblePlayerBattleAction::ATTACK_AND_RETURN: + return 5; + break; + case PossiblePlayerBattleAction::ATTACK: + return 6; + break; + case PossiblePlayerBattleAction::WALK_AND_ATTACK: + return 7; + break; + case PossiblePlayerBattleAction::MOVE_STACK: + return 8; + break; + case PossiblePlayerBattleAction::CATAPULT: + return 9; + break; + case PossiblePlayerBattleAction::HEAL: + return 10; + break; + case PossiblePlayerBattleAction::CREATURE_INFO: + return 11; + break; + case PossiblePlayerBattleAction::HERO_INFO: + return 12; + break; + case PossiblePlayerBattleAction::TELEPORT: + return 13; + break; + default: + assert(0); + return 200; + break; } }; - auto comparer = [&](PossiblePlayerBattleAction const & lhs, PossiblePlayerBattleAction const & rhs) + auto comparer = [&](const PossiblePlayerBattleAction & lhs, const PossiblePlayerBattleAction & rhs) { return assignPriority(lhs) < assignPriority(rhs); }; @@ -358,8 +371,26 @@ void BattleActionsController::actionSetCursor(PossiblePlayerBattleAction action, case PossiblePlayerBattleAction::ATTACK: case PossiblePlayerBattleAction::WALK_AND_ATTACK: case PossiblePlayerBattleAction::ATTACK_AND_RETURN: - owner.fieldController->setBattleCursor(targetHex); + { + static const std::map sectorCursor = { + {BattleHex::TOP_LEFT, Cursor::Combat::HIT_SOUTHEAST}, + {BattleHex::TOP_RIGHT, Cursor::Combat::HIT_SOUTHWEST}, + {BattleHex::RIGHT, Cursor::Combat::HIT_WEST }, + {BattleHex::BOTTOM_RIGHT, Cursor::Combat::HIT_NORTHWEST}, + {BattleHex::BOTTOM_LEFT, Cursor::Combat::HIT_NORTHEAST}, + {BattleHex::LEFT, Cursor::Combat::HIT_EAST }, + {BattleHex::TOP, Cursor::Combat::HIT_SOUTH }, + {BattleHex::BOTTOM, Cursor::Combat::HIT_NORTH } + }; + + auto direction = owner.fieldController->selectAttackDirection(targetHex); + + assert(sectorCursor.count(direction) > 0); + if (sectorCursor.count(direction)) + CCS->curh->set(sectorCursor.at(direction)); + return; + } case PossiblePlayerBattleAction::SHOOT: if (owner.curInt->cb->battleHasShootingPenalty(owner.stacksController->getActiveStack(), targetHex)) @@ -856,7 +887,7 @@ void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterS TConstBonusListPtr bl = casterStack->getBonuses(Selector::type()(BonusType::SPELLCASTER)); - for (auto const & bonus : *bl) + for(const auto & bonus : *bl) { if (bonus->additionalInfo[0] <= 0) creatureSpells.push_back(SpellID(bonus->subtype).toSpell()); diff --git a/client/battle/BattleFieldController.cpp b/client/battle/BattleFieldController.cpp index 33a4892d9..5d8abd9a7 100644 --- a/client/battle/BattleFieldController.cpp +++ b/client/battle/BattleFieldController.cpp @@ -22,6 +22,7 @@ #include "../CGameInfo.h" #include "../CPlayerInterface.h" +#include "../render/CAnimation.h" #include "../render/Canvas.h" #include "../render/IImage.h" #include "../renderSDL/SDL_Extensions.h" @@ -46,6 +47,9 @@ BattleFieldController::BattleFieldController(BattleInterface & owner): cellUnitMovementHighlight = IImage::createFromFile("UnitMovementHighlight.PNG", EImageBlitMode::COLORKEY); cellUnitMaxMovementHighlight = IImage::createFromFile("UnitMaxMovementHighlight.PNG", EImageBlitMode::COLORKEY); + attackCursors = std::make_shared("CRCOMBAT"); + attackCursors->preload(); + if(!owner.siegeController) { auto bfieldType = owner.curInt->cb->battleGetBattlefieldType(); @@ -435,27 +439,7 @@ BattleHex BattleFieldController::getHexAtPosition(Point hoverPos) return BattleHex::INVALID; } -void BattleFieldController::setBattleCursor(BattleHex myNumber) -{ - std::vector sectorCursor = { - Cursor::Combat::HIT_SOUTHEAST, - Cursor::Combat::HIT_SOUTHWEST, - Cursor::Combat::HIT_WEST, - Cursor::Combat::HIT_NORTHWEST, - Cursor::Combat::HIT_NORTHEAST, - Cursor::Combat::HIT_EAST, - Cursor::Combat::HIT_SOUTH, - Cursor::Combat::HIT_NORTH, - }; - - auto direction = static_cast(selectAttackDirection(getHoveredHex(), currentAttackOriginPoint)); - - assert(direction != -1); - if (direction != -1) - CCS->curh->set(sectorCursor[direction]); -} - -BattleHex::EDir BattleFieldController::selectAttackDirection(BattleHex myNumber, const Point & cursorPos) +BattleHex::EDir BattleFieldController::selectAttackDirection(BattleHex myNumber) { const bool doubleWide = owner.stacksController->getActiveStack()->doubleWide(); auto neighbours = myNumber.allNeighbouringTiles(); @@ -518,7 +502,7 @@ BattleHex::EDir BattleFieldController::selectAttackDirection(BattleHex myNumber, for (size_t i = 0; i < 8; ++i) if (attackAvailability[i]) - distance2[i] = (testPoint[i].y - cursorPos.y)*(testPoint[i].y - cursorPos.y) + (testPoint[i].x - cursorPos.x)*(testPoint[i].x - cursorPos.x); + distance2[i] = (testPoint[i].y - currentAttackOriginPoint.y)*(testPoint[i].y - currentAttackOriginPoint.y) + (testPoint[i].x - currentAttackOriginPoint.x)*(testPoint[i].x - currentAttackOriginPoint.x); size_t nearest = -1; for (size_t i = 0; i < 8; ++i) @@ -531,7 +515,7 @@ BattleHex::EDir BattleFieldController::selectAttackDirection(BattleHex myNumber, BattleHex BattleFieldController::fromWhichHexAttack(BattleHex attackTarget) { - BattleHex::EDir direction = selectAttackDirection(getHoveredHex(), currentAttackOriginPoint); + BattleHex::EDir direction = selectAttackDirection(getHoveredHex()); const CStack * attacker = owner.stacksController->getActiveStack(); @@ -637,6 +621,14 @@ void BattleFieldController::show(Canvas & to) CSDL_Ext::CClipRectGuard guard(to.getInternalSurface(), pos); renderBattlefield(to); + + if (isActive() && isPanning() && getHoveredHex() != BattleHex::INVALID) + { + auto cursorIndex = CCS->curh->get(); + auto imageIndex = static_cast(cursorIndex); + + canvas.draw(attackCursors->getImage(imageIndex), hexPositionAbsolute(getHoveredHex()).center() - CCS->curh->getPivotOffsetCombat(imageIndex)); + } } bool BattleFieldController::receiveEvent(const Point & position, int eventType) const diff --git a/client/battle/BattleFieldController.h b/client/battle/BattleFieldController.h index 7c6360010..46a47e798 100644 --- a/client/battle/BattleFieldController.h +++ b/client/battle/BattleFieldController.h @@ -34,6 +34,8 @@ class BattleFieldController : public CIntObject std::shared_ptr cellUnitMaxMovementHighlight; std::shared_ptr cellShade; + std::shared_ptr attackCursors; + /// Canvas that contains background, hex grid (if enabled), absolute obstacles and movement range of active stack std::unique_ptr backgroundWithHexes; @@ -66,8 +68,7 @@ class BattleFieldController : public CIntObject /// Checks whether selected pixel is transparent, uses local coordinates of a hex bool isPixelInHex(Point const & position); - - BattleHex::EDir selectAttackDirection(BattleHex myNumber, const Point & point); + size_t selectBattleCursor(BattleHex myNumber); void panning(bool on, const Point & initialPosition, const Point & finalPosition) override; void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override; @@ -105,6 +106,7 @@ public: /// returns true if stack should render its stack count image in default position - outside own hex bool stackCountOutsideHex(const BattleHex & number) const; - void setBattleCursor(BattleHex myNumber); + BattleHex::EDir selectAttackDirection(BattleHex myNumber); + BattleHex fromWhichHexAttack(BattleHex myNumber); }; diff --git a/client/gui/CursorHandler.h b/client/gui/CursorHandler.h index 9e0338a26..4ef4fe8a5 100644 --- a/client/gui/CursorHandler.h +++ b/client/gui/CursorHandler.h @@ -127,9 +127,6 @@ class CursorHandler final void changeGraphic(Cursor::Type type, size_t index); - Point getPivotOffsetDefault(size_t index); - Point getPivotOffsetMap(size_t index); - Point getPivotOffsetCombat(size_t index); Point getPivotOffsetSpellcast(); Point getPivotOffset(); @@ -168,6 +165,10 @@ public: return static_cast(frame); } + Point getPivotOffsetDefault(size_t index); + Point getPivotOffsetMap(size_t index); + Point getPivotOffsetCombat(size_t index); + void render(); void hide(); diff --git a/client/gui/EventDispatcher.cpp b/client/gui/EventDispatcher.cpp index fcbf7efc9..c95a498a4 100644 --- a/client/gui/EventDispatcher.cpp +++ b/client/gui/EventDispatcher.cpp @@ -221,7 +221,9 @@ void EventDispatcher::dispatchTextEditing(const std::string & text) void EventDispatcher::dispatchGesturePanningStarted(const Point & initialPosition) { - for(auto it : panningInterested) + auto copied = panningInterested; + + for(auto it : copied) { if (it->receiveEvent(initialPosition, AEventsReceiver::GESTURE_PANNING)) { @@ -233,7 +235,9 @@ void EventDispatcher::dispatchGesturePanningStarted(const Point & initialPositio void EventDispatcher::dispatchGesturePanningEnded(const Point & initialPosition, const Point & finalPosition) { - for(auto it : panningInterested) + auto copied = panningInterested; + + for(auto it : copied) { if (it->isPanning()) {