1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00
vcmi/lib/spells/effects/Obstacle.cpp
John Bolton a05ae78e67 Fixed lots of warnings.
Disabled the following (for MSVC only) that couldn't (or shouldn't) be fixed.

4003: not enough actual parameters for macro 'identifier'
4250: 'class1' : inherits 'class2::member' via dominance
4251: 'type' : class 'type1' needs to have dll-interface to be used by clients of class 'type2'
4275: non dll-interface class 'type1' used as base for dll-interface class 'type2'
2020-10-04 02:20:18 -07:00

343 lines
8.4 KiB
C++

/*
* Obstacle.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "Obstacle.h"
#include "Registry.h"
#include "../ISpellMechanics.h"
#include "../../NetPacks.h"
#include "../../battle/IBattleState.h"
#include "../../battle/CBattleInfoCallback.h"
#include "../../serializer/JsonSerializeFormat.h"
static const std::string EFFECT_NAME = "core:obstacle";
namespace spells
{
namespace effects
{
VCMI_REGISTER_SPELL_EFFECT(Obstacle, EFFECT_NAME);
ObstacleSideOptions::ObstacleSideOptions()
: shape(),
range()
{
}
void ObstacleSideOptions::serializeJson(JsonSerializeFormat & handler)
{
serializeRelativeShape(handler, "shape", shape);
serializeRelativeShape(handler, "range", range);
handler.serializeString("appearAnimation", appearAnimation);
handler.serializeString("animation", animation);
handler.serializeInt("offsetY", offsetY);
}
void ObstacleSideOptions::serializeRelativeShape(JsonSerializeFormat & handler, const std::string & fieldName, RelativeShape & value)
{
static const std::vector<std::string> EDirMap =
{
"TL",
"TR",
"R",
"BR",
"BL",
"L",
""
};
{
JsonArraySerializer outer = handler.enterArray(fieldName);
outer.syncSize(value, JsonNode::JsonType::DATA_VECTOR);
for(size_t outerIndex = 0; outerIndex < outer.size(); outerIndex++)
{
JsonArraySerializer inner = outer.enterArray(outerIndex);
inner.syncSize(value.at(outerIndex), JsonNode::JsonType::DATA_STRING);
for(size_t innerIndex = 0; innerIndex < inner.size(); innerIndex++)
{
std::string temp;
if(handler.saving)
{
temp = EDirMap.at(value.at(outerIndex).at(innerIndex));
}
inner.serializeString(innerIndex, temp);
if(!handler.saving)
{
value.at(outerIndex).at(innerIndex) = (BattleHex::EDir) vstd::find_pos(EDirMap, temp);
}
}
}
}
if(!handler.saving)
{
if(value.empty())
value.emplace_back();
if(value.back().empty())
value.back().emplace_back(BattleHex::EDir::NONE);
}
}
Obstacle::Obstacle()
: LocationEffect(),
hidden(false),
passable(false),
trigger(false),
trap(false),
removeOnTrigger(false),
patchCount(1),
turnsRemaining(-1)
{
}
Obstacle::~Obstacle() = default;
void Obstacle::adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const
{
EffectTarget effectTarget = transformTarget(m, spellTarget, spellTarget);
const ObstacleSideOptions & options = sideOptions.at(m->casterSide);
for(auto & destination : effectTarget)
{
for(auto & trasformation : options.shape)
{
BattleHex hex = destination.hexValue;
for(auto direction : trasformation)
hex.moveInDirection(direction, false);
if(hex.isValid())
hexes.insert(hex);
}
}
}
bool Obstacle::applicable(Problem & problem, const Mechanics * m) const
{
return LocationEffect::applicable(problem, m);
}
bool Obstacle::applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const
{
if(!m->isMassive())
{
const bool requiresClearTiles = m->requiresClearTiles();
const ObstacleSideOptions & options = sideOptions.at(m->casterSide);
if(target.empty())
return noRoomToPlace(problem, m);
for(const auto & destination : target)
{
for(auto & trasformation : options.shape)
{
BattleHex hex = destination.hexValue;
for(auto direction : trasformation)
hex.moveInDirection(direction, false);
if(!isHexAvailable(m->cb, hex, requiresClearTiles))
return noRoomToPlace(problem, m);
}
}
}
return LocationEffect::applicable(problem, m, target);
}
EffectTarget Obstacle::transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const
{
const ObstacleSideOptions & options = sideOptions.at(m->casterSide);
EffectTarget ret;
if(!m->isMassive())
{
for(auto & spellDestination : spellTarget)
{
for(auto & rangeShape : options.range)
{
BattleHex hex = spellDestination.hexValue;
for(auto direction : rangeShape)
hex.moveInDirection(direction, false);
ret.emplace_back(hex);
}
}
}
return ret;
}
void Obstacle::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const
{
if(m->isMassive())
{
std::vector<BattleHex> availableTiles;
for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
{
BattleHex hex = i;
if(isHexAvailable(m->cb, hex, true))
availableTiles.push_back(hex);
}
RandomGeneratorUtil::randomShuffle(availableTiles, rng);
const int patchesToPut = std::min(patchCount, (int)availableTiles.size());
EffectTarget randomTarget;
randomTarget.reserve(patchesToPut);
for(int i = 0; i < patchesToPut; i++)
randomTarget.emplace_back(availableTiles.at(i));
placeObstacles(battleState, m, randomTarget);
}
else
{
placeObstacles(battleState, m, target);
}
}
void Obstacle::serializeJsonEffect(JsonSerializeFormat & handler)
{
handler.serializeBool("hidden", hidden);
handler.serializeBool("passable", passable);
handler.serializeBool("trigger", trigger);
handler.serializeBool("trap", trap);
handler.serializeBool("removeOnTrigger", removeOnTrigger);
handler.serializeInt("patchCount", patchCount);
handler.serializeInt("turnsRemaining", turnsRemaining, -1);
handler.serializeStruct("attacker", sideOptions.at(BattleSide::ATTACKER));
handler.serializeStruct("defender", sideOptions.at(BattleSide::DEFENDER));
}
bool Obstacle::isHexAvailable(const CBattleInfoCallback * cb, const BattleHex & hex, const bool mustBeClear)
{
if(!hex.isAvailable())
return false;
if(!mustBeClear)
return true;
if(cb->battleGetUnitByPos(hex, true))
return false;
auto obst = cb->battleGetAllObstaclesOnPos(hex, false);
for(auto & i : obst)
if(i->obstacleType != CObstacleInstance::MOAT)
return false;
if(cb->battleGetSiegeLevel() != 0)
{
EWallPart::EWallPart part = cb->battleHexToWallPart(hex);
if(part == EWallPart::INVALID || part == EWallPart::INDESTRUCTIBLE_PART_OF_GATE)
return true;//no fortification here
else if(static_cast<int>(part) < 0)
return false;//indestructible part (cant be checked by battleGetWallState)
else if(part == EWallPart::BOTTOM_TOWER || part == EWallPart::UPPER_TOWER)
return false;//destructible, but should not be available
else if(cb->battleGetWallState(part) != EWallState::DESTROYED && cb->battleGetWallState(part) != EWallState::NONE)
return false;
}
return true;
}
bool Obstacle::noRoomToPlace(Problem & problem, const Mechanics * m)
{
MetaString text;
text.addTxt(MetaString::GENERAL_TXT, 181);//No room to place %s here
text.addReplacement(m->getSpellName());
problem.add(std::move(text));
return false;
}
void Obstacle::placeObstacles(BattleStateProxy * battleState, const Mechanics * m, const EffectTarget & target) const
{
const ObstacleSideOptions & options = sideOptions.at(m->casterSide);
BattleObstaclesChanged pack;
boost::optional<BattlePerspective::BattlePerspective> perspective;
if(!m->cb->getPlayerID())
perspective = boost::make_optional(BattlePerspective::ALL_KNOWING);
auto all = m->cb->battleGetAllObstacles(perspective);
int obstacleIdToGive = 1;
for(auto & one : all)
if(one->uniqueID >= obstacleIdToGive)
obstacleIdToGive = one->uniqueID + 1;
for(const Destination & destination : target)
{
SpellCreatedObstacle obstacle;
obstacle.uniqueID = obstacleIdToGive++;
obstacle.pos = destination.hexValue;
obstacle.obstacleType = CObstacleInstance::USUAL;
obstacle.ID = m->getSpellIndex();
obstacle.turnsRemaining = turnsRemaining;
obstacle.casterSpellPower = m->getEffectPower();
obstacle.spellLevel = m->getEffectLevel();//todo: level of indirect effect should be also configurable
obstacle.casterSide = m->casterSide;
obstacle.hidden = hidden;
obstacle.passable = passable;
obstacle.trigger = trigger;
obstacle.trap = trap;
obstacle.removeOnTrigger = removeOnTrigger;
obstacle.appearAnimation = options.appearAnimation;
obstacle.animation = options.animation;
obstacle.animationYOffset = options.offsetY;
obstacle.customSize.clear();
obstacle.customSize.reserve(options.shape.size());
for(auto & shape : options.shape)
{
BattleHex hex = destination.hexValue;
for(auto direction : shape)
hex.moveInDirection(direction, false);
obstacle.customSize.emplace_back(hex);
}
pack.changes.emplace_back();
obstacle.toInfo(pack.changes.back());
}
if(!pack.changes.empty())
battleState->apply(&pack);
}
}
}