mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
Teleport: can trigger obstacles now
This commit is contained in:
parent
384ee99834
commit
f11fa8f0c8
@ -14,9 +14,13 @@
|
||||
|
||||
#include "../CStack.h"
|
||||
#include "BattleInfo.h"
|
||||
#include "CObstacleInstance.h"
|
||||
#include "DamageCalculator.h"
|
||||
#include "PossiblePlayerBattleAction.h"
|
||||
#include "../NetPacks.h"
|
||||
#include "../spells/ObstacleCasterProxy.h"
|
||||
#include "../spells/ISpellMechanics.h"
|
||||
#include "../spells/Problem.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../mapObjects/CGTownInstance.h"
|
||||
#include "../BattleFieldHandler.h"
|
||||
@ -826,6 +830,66 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAl
|
||||
return affectedObstacles;
|
||||
}
|
||||
|
||||
bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const std::set<BattleHex> & passed) const
|
||||
{
|
||||
if(!unit.alive())
|
||||
return false;
|
||||
bool movementStopped = false;
|
||||
for(auto & obstacle : getAllAffectedObstaclesByStack(&unit, passed))
|
||||
{
|
||||
//helper info
|
||||
const SpellCreatedObstacle * spellObstacle = dynamic_cast<const SpellCreatedObstacle *>(obstacle.get());
|
||||
|
||||
if(spellObstacle)
|
||||
{
|
||||
auto revealObstacles = [&](const SpellCreatedObstacle & spellObstacle) -> void
|
||||
{
|
||||
// For the hidden spell created obstacles, e.g. QuickSand, it should be revealed after taking damage
|
||||
auto operation = ObstacleChanges::EOperation::UPDATE;
|
||||
if (spellObstacle.removeOnTrigger)
|
||||
operation = ObstacleChanges::EOperation::REMOVE;
|
||||
|
||||
SpellCreatedObstacle changedObstacle;
|
||||
changedObstacle.uniqueID = spellObstacle.uniqueID;
|
||||
changedObstacle.revealed = true;
|
||||
|
||||
BattleObstaclesChanged bocp;
|
||||
bocp.changes.emplace_back(spellObstacle.uniqueID, operation);
|
||||
changedObstacle.toInfo(bocp.changes.back(), operation);
|
||||
spellEnv.apply(&bocp);
|
||||
};
|
||||
const auto side = unit.unitSide();
|
||||
auto shouldReveal = !spellObstacle->hidden || !battleIsObstacleVisibleForSide(*obstacle, (BattlePerspective::BattlePerspective)side);
|
||||
const auto * hero = battleGetFightingHero(spellObstacle->casterSide);
|
||||
auto caster = spells::ObstacleCasterProxy(getBattle()->getSidePlayer(spellObstacle->casterSide), hero, *spellObstacle);
|
||||
const auto * sp = obstacle->getTrigger().toSpell();
|
||||
if(obstacle->triggersEffects() && sp)
|
||||
{
|
||||
auto cast = spells::BattleCast(this, &caster, spells::Mode::PASSIVE, sp);
|
||||
spells::detail::ProblemImpl ignored;
|
||||
auto target = spells::Target(1, spells::Destination(&unit));
|
||||
if(sp->battleMechanics(&cast)->canBeCastAt(target, ignored)) // Obstacles should not be revealed by immune creatures
|
||||
{
|
||||
if(shouldReveal) { //hidden obstacle triggers effects after revealed
|
||||
revealObstacles(*spellObstacle);
|
||||
cast.cast(&spellEnv, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(shouldReveal)
|
||||
revealObstacles(*spellObstacle);
|
||||
}
|
||||
|
||||
if(!unit.alive())
|
||||
return false;
|
||||
|
||||
if(obstacle->stopsMovement())
|
||||
movementStopped = true;
|
||||
}
|
||||
|
||||
return unit.alive() && !movementStopped;
|
||||
}
|
||||
|
||||
AccessibilityInfo CBattleInfoCallback::getAccesibility() const
|
||||
{
|
||||
AccessibilityInfo ret;
|
||||
|
@ -20,6 +20,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
class CGHeroInstance;
|
||||
class CStack;
|
||||
class ISpellCaster;
|
||||
class SpellCastEnvironment;
|
||||
class CSpell;
|
||||
struct CObstacleInstance;
|
||||
class IBonusBearer;
|
||||
@ -61,6 +62,8 @@ public:
|
||||
|
||||
std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const override;
|
||||
std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const override;
|
||||
//Handle obstacle damage here, requires SpellCastEnvironment
|
||||
bool handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const std::set<BattleHex> & passed = {}) const;
|
||||
|
||||
const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const;
|
||||
|
||||
|
@ -25,14 +25,13 @@ class DLL_LINKAGE CCallbackBase
|
||||
{
|
||||
const IBattleInfo * battle = nullptr; //battle to which the player is engaged, nullptr if none or not applicable
|
||||
|
||||
const IBattleInfo * getBattle() const;
|
||||
|
||||
protected:
|
||||
boost::optional<PlayerColor> player; // not set gives access to all information, otherwise callback provides only information "visible" for player
|
||||
|
||||
CCallbackBase(boost::optional<PlayerColor> Player);
|
||||
CCallbackBase() = default;
|
||||
|
||||
const IBattleInfo * getBattle() const;
|
||||
void setBattle(const IBattleInfo * B);
|
||||
bool duringBattle() const;
|
||||
|
||||
|
@ -83,10 +83,17 @@ void Teleport::apply(ServerCallback * server, const Mechanics * m, const EffectT
|
||||
pack.tilesToMove = tiles;
|
||||
pack.teleporting = true;
|
||||
server->apply(&pack);
|
||||
|
||||
if(triggerObstacles)
|
||||
{
|
||||
auto spellEnv = dynamic_cast<SpellCastEnvironment*>(server);
|
||||
m->battle()->handleObstacleTriggersForUnit(*spellEnv, *targetUnit);
|
||||
}
|
||||
}
|
||||
|
||||
void Teleport::serializeJsonUnitEffect(JsonSerializeFormat & handler)
|
||||
{
|
||||
handler.serializeBool("triggerObstacles", triggerObstacles);
|
||||
handler.serializeBool("isWallPassable", isWallPassable);
|
||||
handler.serializeBool("isMoatPassable", isMoatPassable);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ protected:
|
||||
void serializeJsonUnitEffect(JsonSerializeFormat & handler) override;
|
||||
|
||||
private:
|
||||
bool triggerObstacles;
|
||||
bool isWallPassable;
|
||||
bool isMoatPassable;
|
||||
};
|
||||
|
@ -1527,7 +1527,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
||||
{
|
||||
if(stackIsMoving && start != curStack->getPosition())
|
||||
{
|
||||
stackIsMoving = handleDamageFromObstacle(curStack, passed);
|
||||
stackIsMoving = handleObstacleTriggersForUnit(*spellEnv, *curStack, passed);
|
||||
passed.insert(curStack->getPosition());
|
||||
if(curStack->doubleWide())
|
||||
passed.insert(curStack->occupiedHex());
|
||||
@ -1565,7 +1565,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
||||
passed.clear(); //Just empty passed, obstacles will handled automatically
|
||||
}
|
||||
//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
|
||||
handleDamageFromObstacle(curStack, passed);
|
||||
handleObstacleTriggersForUnit(*spellEnv, *curStack, passed);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -4933,7 +4933,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
}
|
||||
if(ba.actionType == EActionType::WAIT || ba.actionType == EActionType::DEFEND
|
||||
|| ba.actionType == EActionType::SHOOT || ba.actionType == EActionType::MONSTER_SPELL)
|
||||
handleDamageFromObstacle(stack);
|
||||
handleObstacleTriggersForUnit(*spellEnv, *stack);
|
||||
if(ba.stackNumber == gs->curB->activeStack || battleResult.get()) //active stack has moved or battle has finished
|
||||
battleMadeAction.setn(true);
|
||||
return ok;
|
||||
@ -5289,66 +5289,6 @@ void CGameHandler::stackTurnTrigger(const CStack *st)
|
||||
}
|
||||
}
|
||||
|
||||
bool CGameHandler::handleDamageFromObstacle(const battle::Unit * curStack, const std::set<BattleHex> & passed)
|
||||
{
|
||||
if(!curStack->alive())
|
||||
return false;
|
||||
bool movementStopped = false;
|
||||
for(auto & obstacle : getAllAffectedObstaclesByStack(curStack, passed))
|
||||
{
|
||||
//helper info
|
||||
const SpellCreatedObstacle * spellObstacle = dynamic_cast<const SpellCreatedObstacle *>(obstacle.get());
|
||||
|
||||
if(spellObstacle)
|
||||
{
|
||||
auto revealObstacles = [&](const SpellCreatedObstacle & spellObstacle) -> void
|
||||
{
|
||||
// For the hidden spell created obstacles, e.g. QuickSand, it should be revealed after taking damage
|
||||
auto operation = ObstacleChanges::EOperation::UPDATE;
|
||||
if (spellObstacle.removeOnTrigger)
|
||||
operation = ObstacleChanges::EOperation::REMOVE;
|
||||
|
||||
SpellCreatedObstacle changedObstacle;
|
||||
changedObstacle.uniqueID = spellObstacle.uniqueID;
|
||||
changedObstacle.revealed = true;
|
||||
|
||||
BattleObstaclesChanged bocp;
|
||||
bocp.changes.emplace_back(spellObstacle.uniqueID, operation);
|
||||
changedObstacle.toInfo(bocp.changes.back(), operation);
|
||||
sendAndApply(&bocp);
|
||||
};
|
||||
const auto side = curStack->unitSide();
|
||||
auto shouldReveal = !spellObstacle->hidden || !gs->curB->battleIsObstacleVisibleForSide(*obstacle, (BattlePerspective::BattlePerspective)side);
|
||||
const auto * hero = gs->curB->battleGetFightingHero(spellObstacle->casterSide);
|
||||
auto caster = spells::ObstacleCasterProxy(gs->curB->getSidePlayer(spellObstacle->casterSide), hero, *spellObstacle);
|
||||
const auto * sp = obstacle->getTrigger().toSpell();
|
||||
if(obstacle->triggersEffects() && sp)
|
||||
{
|
||||
auto cast = spells::BattleCast(gs->curB, &caster, spells::Mode::PASSIVE, sp);
|
||||
spells::detail::ProblemImpl ignored;
|
||||
auto target = spells::Target(1, spells::Destination(curStack));
|
||||
if(sp->battleMechanics(&cast)->canBeCastAt(target, ignored)) // Obstacles should not be revealed by immune creatures
|
||||
{
|
||||
if(shouldReveal) { //hidden obstacle triggers effects after revealed
|
||||
revealObstacles(*spellObstacle);
|
||||
cast.cast(spellEnv, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(shouldReveal)
|
||||
revealObstacles(*spellObstacle);
|
||||
}
|
||||
|
||||
if(!curStack->alive())
|
||||
return false;
|
||||
|
||||
if(obstacle->stopsMovement())
|
||||
movementStopped = true;
|
||||
}
|
||||
|
||||
return curStack->alive() && !movementStopped;
|
||||
}
|
||||
|
||||
void CGameHandler::handleTimeEvents()
|
||||
{
|
||||
gs->map->events.sort(evntCmp);
|
||||
|
@ -236,7 +236,6 @@ public:
|
||||
bool makeCustomAction(BattleAction &ba);
|
||||
void stackEnchantedTrigger(const CStack * stack);
|
||||
void stackTurnTrigger(const CStack *stack);
|
||||
bool handleDamageFromObstacle(const battle::Unit * curStack, const std::set<BattleHex> & passed = {}); //checks if obstacle is land mine and handles possible consequences
|
||||
|
||||
void removeObstacle(const CObstacleInstance &obstacle);
|
||||
bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player );
|
||||
|
Loading…
Reference in New Issue
Block a user