diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index edc6b7e7e..d4326a8f7 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -828,20 +828,19 @@ std::pair CBattleInfoCallback::battleEstimateDamage(CRandomGenerator return ret; } -std::shared_ptr CBattleInfoCallback::battleGetObstacleOnPos(BattleHex tile, bool onlyBlocking /*= true*/) const +std::vector> CBattleInfoCallback::battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking) const { - RETURN_IF_NOT_BATTLE(std::shared_ptr()); - - for(auto &obs : battleGetAllObstacles()) + std::vector> obstacles = std::vector>(); + RETURN_IF_NOT_BATTLE(obstacles); + for(auto & obs : battleGetAllObstacles()) { if(vstd::contains(obs->getBlockedTiles(), tile) - || (!onlyBlocking && vstd::contains(obs->getAffectedTiles(), tile))) + || (!onlyBlocking && vstd::contains(obs->getAffectedTiles(), tile))) { - return obs; + obstacles.push_back(obs); } } - - return std::shared_ptr(); + return obstacles; } AccessibilityInfo CBattleInfoCallback::getAccesibility() const diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index 947cf5319..f43143349 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/lib/battle/CBattleInfoCallback.h @@ -40,7 +40,8 @@ public: //battle boost::optional battleIsFinished() const; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw - std::shared_ptr battleGetObstacleOnPos(BattleHex tile, bool onlyBlocking = true) const; //blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands) + std::vector> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const; //blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands) + const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const; //returns stack info by given pos void battleGetStackQueue(std::vector &out, const int howMany, const int turn = 0, int lastMoved = -1) const; void battleGetStackCountOutsideHexes(bool *ac) const; // returns hexes which when in front of a stack cause us to move the amount box back diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index ddbfad26d..51c638540 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -438,15 +438,17 @@ bool ObstacleMechanics::isHexAviable(const CBattleInfoCallback * cb, const Battl if(cb->battleGetStackByPos(hex, true)) return false; - auto obst = cb->battleGetObstacleOnPos(hex, false); - if(obst /*&& obst->type != CObstacleInstance::MOAT*/)//todo: issue 2366, uncomment once obstacle tile sharing implemented - return false; + auto obst = cb->battleGetAllObstaclesOnPos(hex, false); - if(nullptr != cb->battleGetDefendedTown() && CGTownInstance::NONE != cb->battleGetDefendedTown()->fortLevel()) + for(auto & i : obst) + if(i->obstacleType != CObstacleInstance::MOAT) + return false; + + if(cb->battleGetDefendedTown() != nullptr && cb->battleGetDefendedTown()->fortLevel() != CGTownInstance::NONE) { EWallPart::EWallPart part = cb->battleHexToWallPart(hex); - if(part == EWallPart::INVALID) + if(part == EWallPart::INVALID || part == EWallPart::INDESTRUCTIBLE_PART_OF_GATE) return true;//no fortification here else if(static_cast(part) < 0) return false;//indestuctible part (cant be checked by battleGetWallState) @@ -631,18 +633,23 @@ void ForceFieldMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const ///RemoveObstacleMechanics void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { - if(auto obstacleToRemove = parameters.cb->battleGetObstacleOnPos(parameters.getFirstDestinationHex(), false)) + auto obstacleToRemove = parameters.cb->battleGetAllObstaclesOnPos(parameters.getFirstDestinationHex(), false); + if(!obstacleToRemove.empty()) { - if(canRemove(obstacleToRemove.get(), parameters.spellLvl)) + ObstaclesRemoved obr; + bool complain = true; + for(auto & i : obstacleToRemove) { - ObstaclesRemoved obr; - obr.obstacles.insert(obstacleToRemove->uniqueID); + if(canRemove(i.get(), parameters.spellLvl)) + { + obr.obstacles.insert(i->uniqueID); + complain = false; + } + } + if(!complain) env->sendAndApply(&obr); - } - else - { + else if(complain || obr.obstacles.empty()) env->complain("Cant remove this obstacle!"); - } } else env->complain("There's no obstacle to remove!"); @@ -667,9 +674,11 @@ ESpellCastProblem::ESpellCastProblem RemoveObstacleMechanics::canBeCast(const CB ESpellCastProblem::ESpellCastProblem RemoveObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { - if(auto obstacle = cb->battleGetObstacleOnPos(ctx.destination, false)) - if(canRemove(obstacle.get(), ctx.schoolLvl)) - return ESpellCastProblem::OK; + auto obstacles = cb->battleGetAllObstaclesOnPos(ctx.destination, false); + if(!obstacles.empty()) + for(auto & i : obstacles) + if(canRemove(i.get(), ctx.schoolLvl)) + return ESpellCastProblem::OK; return ESpellCastProblem::NO_APPROPRIATE_TARGET; } @@ -734,7 +743,7 @@ ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCast(const CBattle //therefore we do not need to check caster and casting mode //TODO: check that we really should check immunity for both stacks ESpellCastProblem::ESpellCastProblem res = owner->internalIsImmune(caster, stack); - const bool immune = ESpellCastProblem::OK != res && ESpellCastProblem::NOT_DECIDED != res; + const bool immune = ESpellCastProblem::OK != res && ESpellCastProblem::NOT_DECIDED != res; const bool casterStack = stack->owner == caster->getOwner(); if(!immune && casterStack) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index c5fa7c4c0..629b70713 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1231,7 +1231,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest) } else //for non-flying creatures { - std::shared_ptr obstacle, obstacle2; //obstacle that interrupted movement + std::vector> obstacle, obstacle2; //obstacle that interrupted movement std::vector tiles; const int tilesToMove = std::max((int)(path.first.size() - creSpeed), 0); int v = path.first.size()-1; @@ -1330,7 +1330,8 @@ int CGameHandler::moveStack(int stack, BattleHex dest) } //if we walked onto something, finalize this portion of stack movement check into obstacle - if ((obstacle = battleGetObstacleOnPos(hex, false))) + obstacle = battleGetAllObstaclesOnPos(hex, false); + if(!obstacle.empty()) obstacleHit = true; if (curStack->doubleWide()) @@ -1338,7 +1339,8 @@ int CGameHandler::moveStack(int stack, BattleHex dest) BattleHex otherHex = curStack->occupiedHex(hex); //two hex creature hit obstacle by backside - if (otherHex.isValid() && ((obstacle2 = battleGetObstacleOnPos(otherHex, false)))) + obstacle2 = battleGetAllObstaclesOnPos(otherHex, false); + if(otherHex.isValid() && !obstacle2.empty()) obstacleHit = true; } } @@ -1359,17 +1361,18 @@ int CGameHandler::moveStack(int stack, BattleHex dest) //we don't handle obstacle at the destination tile -> it's handled separately in the if at the end if (curStack->position != dest) { - auto processObstacle = [&](std::shared_ptr & obs) + auto processObstacle = [&](std::vector> & obs) { - if (obs) + if(!obs.empty()) { - handleDamageFromObstacle(*obs, curStack); - - //if stack die in explosion or interrupted by obstacle, abort movement - if (obs->stopsMovement() || !curStack->alive()) - stackIsMoving = false; - - obs.reset(); + for(auto & i : obs) + { + handleDamageFromObstacle(*i, curStack); + //if stack die in explosion or interrupted by obstacle, abort movement + if(i->stopsMovement() || !curStack->alive()) + stackIsMoving = false; + i.reset(); + } } }; @@ -1406,22 +1409,23 @@ int CGameHandler::moveStack(int stack, BattleHex dest) //handling obstacle on the final field (separate, because it affects both flying and walking stacks) if (curStack->alive()) { - if (auto theLastObstacle = battleGetObstacleOnPos(curStack->position, false)) - { - handleDamageFromObstacle(*theLastObstacle, curStack); - } + auto theLastObstacle = battleGetAllObstaclesOnPos(curStack->position, false); + for(auto & i : theLastObstacle) + if(curStack->alive()) + handleDamageFromObstacle(*i, curStack); } if (curStack->alive() && curStack->doubleWide()) { BattleHex otherHex = curStack->occupiedHex(curStack->position); - if (otherHex.isValid()) - if (auto theLastObstacle = battleGetObstacleOnPos(otherHex, false)) - { - //two hex creature hit obstacle by backside - handleDamageFromObstacle(*theLastObstacle, curStack); - } + { + //two hex creature hit obstacle by backside + auto theLastObstacle = battleGetAllObstaclesOnPos(otherHex, false); + for(auto & i : theLastObstacle) + if(curStack->alive()) + handleDamageFromObstacle(*i, curStack); + } } return ret; }