From 70eac47f08dae01a0f5803440fac33dec0a09383 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 17 Dec 2022 00:10:12 +0200 Subject: [PATCH] Fixes #1051, #1042 - highlight creatures that will be affected by spell --- client/battle/BattleActionsController.cpp | 17 ++-- client/battle/BattleInterface.cpp | 2 +- client/battle/BattleStacksController.cpp | 112 ++++++++++++++++------ client/battle/BattleStacksController.h | 14 ++- 4 files changed, 100 insertions(+), 45 deletions(-) diff --git a/client/battle/BattleActionsController.cpp b/client/battle/BattleActionsController.cpp index 1e367af51..1617f3586 100644 --- a/client/battle/BattleActionsController.cpp +++ b/client/battle/BattleActionsController.cpp @@ -230,13 +230,13 @@ void BattleActionsController::castThisSpell(SpellID spellID) } } - -void BattleActionsController::handleHex(BattleHex myNumber, int eventType) -{ - if (!owner.myTurn) //we are not permit to do anything - return; - - // This function handles mouse move over hexes and l-clicking on them. + +void BattleActionsController::handleHex(BattleHex myNumber, int eventType) +{ + if (!owner.myTurn) //we are not permit to do anything + return; + + // This function handles mouse move over hexes and l-clicking on them. // First we decide what happens if player clicks on this hex and set appropriately // consoleMsg, cursorFrame/Type and prepare lambda realizeAction. // @@ -263,9 +263,6 @@ void BattleActionsController::handleHex(BattleHex myNumber, int eventType) if (shere) ourStack = shere->owner == owner.curInt->playerID; - //stack may have changed, update selection border - owner.stacksController->setHoveredStack(shere); - localActions.clear(); illegalActions.clear(); diff --git a/client/battle/BattleInterface.cpp b/client/battle/BattleInterface.cpp index f68217a48..b960fae5e 100644 --- a/client/battle/BattleInterface.cpp +++ b/client/battle/BattleInterface.cpp @@ -902,7 +902,7 @@ void BattleInterface::show(SDL_Surface *to) fieldController->renderBattlefield(canvas); - stacksController->updateBattleAnimations(); + stacksController->update(); SDL_SetClipRect(to, &buf); //restoring previous clip_rect diff --git a/client/battle/BattleStacksController.cpp b/client/battle/BattleStacksController.cpp index c247074cd..a02fee59d 100644 --- a/client/battle/BattleStacksController.cpp +++ b/client/battle/BattleStacksController.cpp @@ -13,6 +13,7 @@ #include "BattleSiegeController.h" #include "BattleInterfaceClasses.h" #include "BattleInterface.h" +#include "BattleActionsController.h" #include "BattleAnimationClasses.h" #include "BattleFieldController.h" #include "BattleEffectsController.h" @@ -27,6 +28,7 @@ #include "../gui/CAnimation.h" #include "../gui/CGuiHandler.h" #include "../gui/Canvas.h" +#include "../../lib/spells/ISpellMechanics.h" #include "../../CCallback.h" #include "../../lib/battle/BattleHex.h" @@ -69,7 +71,6 @@ static void onAnimationFinished(const CStack *stack, std::weak_ptrblockUI(activeStack == nullptr); } -void BattleStacksController::setHoveredStack(const CStack *stack) -{ - if ( stack == mouseHoveredStack ) - return; - - if (mouseHoveredStack) - stackAnimation[mouseHoveredStack->ID]->setBorderColor(AnimationControls::getNoBorder()); - - // stack must be alive and not active (which uses gold border instead) - if (stack && stack->alive() && stack != activeStack) - { - mouseHoveredStack = stack; - - if (mouseHoveredStack && !mouseHoveredStack->isFrozen()) - { - stackAnimation[mouseHoveredStack->ID]->setBorderColor(AnimationControls::getBlueBorder()); - if (stackAnimation[mouseHoveredStack->ID]->framesInGroup(ECreatureAnimType::MOUSEON) > 0) - stackAnimation[mouseHoveredStack->ID]->playOnce(ECreatureAnimType::MOUSEON); - } - } - else - mouseHoveredStack = nullptr; -} - bool BattleStacksController::stackNeedsAmountBox(const CStack * stack) const { BattleHex currentActionTarget; @@ -279,9 +256,6 @@ bool BattleStacksController::stackNeedsAmountBox(const CStack * stack) const if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON) && stack->getCount() == 1) //do not show box for singular war machines, stacked war machines with box shown are supported as extension feature return false; - if (!owner.battleActionsStarted) // do not perform any further checks since they are related to actions that will only occur after intro music - return true; - if(!stack->alive()) return false; @@ -377,6 +351,12 @@ void BattleStacksController::showStack(Canvas & canvas, const CStack * stack) stackAnimation[stack->ID]->incrementFrame(float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000); } +void BattleStacksController::update() +{ + updateHoveredStacks(); + updateBattleAnimations(); +} + void BattleStacksController::updateBattleAnimations() { for (auto & elem : currentAnimations) @@ -390,7 +370,6 @@ void BattleStacksController::updateBattleAnimations() elem->tryInitialize(); } - bool hadAnimations = !currentAnimations.empty(); vstd::erase(currentAnimations, nullptr); @@ -674,7 +653,6 @@ void BattleStacksController::endAction(const BattleAction* action) void BattleStacksController::startAction(const BattleAction* action) { - setHoveredStack(nullptr); removeExpiredColorFilters(); } @@ -819,3 +797,77 @@ void BattleStacksController::removeExpiredColorFilters() return true; }); } + +void BattleStacksController::updateHoveredStacks() +{ + auto newStacks = selectHoveredStacks(); + + for (auto const * stack : mouseHoveredStacks) + { + if (vstd::contains(newStacks, stack)) + continue; + + if (stack == activeStack) + stackAnimation[stack->ID]->setBorderColor(AnimationControls::getGoldBorder()); + else + stackAnimation[stack->ID]->setBorderColor(AnimationControls::getNoBorder()); + } + + for (auto const * stack : newStacks) + { + if (vstd::contains(mouseHoveredStacks, stack)) + continue; + + stackAnimation[stack->ID]->setBorderColor(AnimationControls::getBlueBorder()); + if (stackAnimation[stack->ID]->framesInGroup(ECreatureAnimType::MOUSEON) > 0) + stackAnimation[stack->ID]->playOnce(ECreatureAnimType::MOUSEON); + + } + + mouseHoveredStacks = newStacks; +} + +std::vector BattleStacksController::selectHoveredStacks() +{ + auto hoveredHex = owner.fieldController->getHoveredHex(); + + if (!hoveredHex.isValid()) + return {}; + + const spells::Caster *caster = nullptr; + const CSpell *spell = nullptr; + + spells::Mode mode = spells::Mode::HERO; + + if(owner.actionsController->spellcastingModeActive())//hero casts spell + { + spell = owner.actionsController->selectedSpell().toSpell(); + caster = owner.getActiveHero(); + } + else if(owner.stacksController->activeStackSpellToCast() != SpellID::NONE)//stack casts spell + { + spell = SpellID(owner.stacksController->activeStackSpellToCast()).toSpell(); + caster = owner.stacksController->getActiveStack(); + mode = spells::Mode::CREATURE_ACTIVE; + } + + if(caster && spell) //when casting spell + { + spells::Target target; + target.emplace_back(hoveredHex); + + spells::BattleCast event(owner.curInt->cb.get(), caster, mode, spell); + auto mechanics = spell->battleMechanics(&event); + return mechanics->getAffectedStacks(target); + } + + if(hoveredHex.isValid()) + { + const CStack * const stack = owner.curInt->cb->battleGetStackByPos(hoveredHex, true); + + if (stack) + return {stack}; + } + + return {}; +} diff --git a/client/battle/BattleStacksController.h b/client/battle/BattleStacksController.h index 333ab4a67..d4df153a4 100644 --- a/client/battle/BattleStacksController.h +++ b/client/battle/BattleStacksController.h @@ -70,8 +70,8 @@ class BattleStacksController /// currently active stack; nullptr - no one const CStack *activeStack; - /// stack below mouse pointer, used for border animation - const CStack *mouseHoveredStack; + /// stacks 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 const CStack *stackToActivate; @@ -94,6 +94,12 @@ class BattleStacksController void executeAttackAnimations(); void removeExpiredColorFilters(); + + void updateBattleAnimations(); + void updateHoveredStacks(); + + std::vector selectHoveredStacks(); + public: BattleStacksController(BattleInterface & owner); @@ -117,7 +123,6 @@ public: void activateStack(); //sets activeStack to stackToActivate etc. //FIXME: No, it's not clear at all void setActiveStack(const CStack *stack); - void setHoveredStack(const CStack *stack); void setSelectedStack(const CStack *stack); void showAliveStack(Canvas & canvas, const CStack * stack); @@ -130,11 +135,12 @@ public: /// If effect from same (target, source) already exists, it will be updated void setStackColorFilter(const ColorFilter & effect, const CStack * target, const CSpell *source, bool persistent); void addNewAnim(BattleAnimation *anim); //adds new anim to pendingAnims - void updateBattleAnimations(); const CStack* getActiveStack() const; const CStack* getSelectedStack() const; + void update(); + /// returns position of animation needed to place stack in specific hex Point getStackPositionAtHex(BattleHex hexNum, const CStack * creature) const;