mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +02:00
Teleport: can trigger obstacles now
This commit is contained in:
@@ -14,9 +14,13 @@
|
|||||||
|
|
||||||
#include "../CStack.h"
|
#include "../CStack.h"
|
||||||
#include "BattleInfo.h"
|
#include "BattleInfo.h"
|
||||||
|
#include "CObstacleInstance.h"
|
||||||
#include "DamageCalculator.h"
|
#include "DamageCalculator.h"
|
||||||
#include "PossiblePlayerBattleAction.h"
|
#include "PossiblePlayerBattleAction.h"
|
||||||
#include "../NetPacks.h"
|
#include "../NetPacks.h"
|
||||||
|
#include "../spells/ObstacleCasterProxy.h"
|
||||||
|
#include "../spells/ISpellMechanics.h"
|
||||||
|
#include "../spells/Problem.h"
|
||||||
#include "../spells/CSpellHandler.h"
|
#include "../spells/CSpellHandler.h"
|
||||||
#include "../mapObjects/CGTownInstance.h"
|
#include "../mapObjects/CGTownInstance.h"
|
||||||
#include "../BattleFieldHandler.h"
|
#include "../BattleFieldHandler.h"
|
||||||
@@ -826,6 +830,66 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAl
|
|||||||
return affectedObstacles;
|
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 CBattleInfoCallback::getAccesibility() const
|
||||||
{
|
{
|
||||||
AccessibilityInfo ret;
|
AccessibilityInfo ret;
|
||||||
|
@@ -20,6 +20,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
class CGHeroInstance;
|
class CGHeroInstance;
|
||||||
class CStack;
|
class CStack;
|
||||||
class ISpellCaster;
|
class ISpellCaster;
|
||||||
|
class SpellCastEnvironment;
|
||||||
class CSpell;
|
class CSpell;
|
||||||
struct CObstacleInstance;
|
struct CObstacleInstance;
|
||||||
class IBonusBearer;
|
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>> 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;
|
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;
|
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 * battle = nullptr; //battle to which the player is engaged, nullptr if none or not applicable
|
||||||
|
|
||||||
const IBattleInfo * getBattle() const;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
boost::optional<PlayerColor> player; // not set gives access to all information, otherwise callback provides only information "visible" for player
|
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(boost::optional<PlayerColor> Player);
|
||||||
CCallbackBase() = default;
|
CCallbackBase() = default;
|
||||||
|
|
||||||
|
const IBattleInfo * getBattle() const;
|
||||||
void setBattle(const IBattleInfo * B);
|
void setBattle(const IBattleInfo * B);
|
||||||
bool duringBattle() const;
|
bool duringBattle() const;
|
||||||
|
|
||||||
|
@@ -83,10 +83,17 @@ void Teleport::apply(ServerCallback * server, const Mechanics * m, const EffectT
|
|||||||
pack.tilesToMove = tiles;
|
pack.tilesToMove = tiles;
|
||||||
pack.teleporting = true;
|
pack.teleporting = true;
|
||||||
server->apply(&pack);
|
server->apply(&pack);
|
||||||
|
|
||||||
|
if(triggerObstacles)
|
||||||
|
{
|
||||||
|
auto spellEnv = dynamic_cast<SpellCastEnvironment*>(server);
|
||||||
|
m->battle()->handleObstacleTriggersForUnit(*spellEnv, *targetUnit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Teleport::serializeJsonUnitEffect(JsonSerializeFormat & handler)
|
void Teleport::serializeJsonUnitEffect(JsonSerializeFormat & handler)
|
||||||
{
|
{
|
||||||
|
handler.serializeBool("triggerObstacles", triggerObstacles);
|
||||||
handler.serializeBool("isWallPassable", isWallPassable);
|
handler.serializeBool("isWallPassable", isWallPassable);
|
||||||
handler.serializeBool("isMoatPassable", isMoatPassable);
|
handler.serializeBool("isMoatPassable", isMoatPassable);
|
||||||
}
|
}
|
||||||
|
@@ -36,6 +36,7 @@ protected:
|
|||||||
void serializeJsonUnitEffect(JsonSerializeFormat & handler) override;
|
void serializeJsonUnitEffect(JsonSerializeFormat & handler) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool triggerObstacles;
|
||||||
bool isWallPassable;
|
bool isWallPassable;
|
||||||
bool isMoatPassable;
|
bool isMoatPassable;
|
||||||
};
|
};
|
||||||
|
@@ -1527,7 +1527,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
|||||||
{
|
{
|
||||||
if(stackIsMoving && start != curStack->getPosition())
|
if(stackIsMoving && start != curStack->getPosition())
|
||||||
{
|
{
|
||||||
stackIsMoving = handleDamageFromObstacle(curStack, passed);
|
stackIsMoving = handleObstacleTriggersForUnit(*spellEnv, *curStack, passed);
|
||||||
passed.insert(curStack->getPosition());
|
passed.insert(curStack->getPosition());
|
||||||
if(curStack->doubleWide())
|
if(curStack->doubleWide())
|
||||||
passed.insert(curStack->occupiedHex());
|
passed.insert(curStack->occupiedHex());
|
||||||
@@ -1565,7 +1565,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
|||||||
passed.clear(); //Just empty passed, obstacles will handled automatically
|
passed.clear(); //Just empty passed, obstacles will handled automatically
|
||||||
}
|
}
|
||||||
//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
|
//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
|
||||||
handleDamageFromObstacle(curStack, passed);
|
handleObstacleTriggersForUnit(*spellEnv, *curStack, passed);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -4933,7 +4933,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
|||||||
}
|
}
|
||||||
if(ba.actionType == EActionType::WAIT || ba.actionType == EActionType::DEFEND
|
if(ba.actionType == EActionType::WAIT || ba.actionType == EActionType::DEFEND
|
||||||
|| ba.actionType == EActionType::SHOOT || ba.actionType == EActionType::MONSTER_SPELL)
|
|| 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
|
if(ba.stackNumber == gs->curB->activeStack || battleResult.get()) //active stack has moved or battle has finished
|
||||||
battleMadeAction.setn(true);
|
battleMadeAction.setn(true);
|
||||||
return ok;
|
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()
|
void CGameHandler::handleTimeEvents()
|
||||||
{
|
{
|
||||||
gs->map->events.sort(evntCmp);
|
gs->map->events.sort(evntCmp);
|
||||||
|
@@ -236,7 +236,6 @@ public:
|
|||||||
bool makeCustomAction(BattleAction &ba);
|
bool makeCustomAction(BattleAction &ba);
|
||||||
void stackEnchantedTrigger(const CStack * stack);
|
void stackEnchantedTrigger(const CStack * stack);
|
||||||
void stackTurnTrigger(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);
|
void removeObstacle(const CObstacleInstance &obstacle);
|
||||||
bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player );
|
bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player );
|
||||||
|
Reference in New Issue
Block a user