mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
WIP: Moat placer
This commit is contained in:
parent
d90d00eeac
commit
57c35f39ca
@ -147,6 +147,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/spells/effects/Effects.cpp
|
||||
${MAIN_LIB_DIR}/spells/effects/Heal.cpp
|
||||
${MAIN_LIB_DIR}/spells/effects/LocationEffect.cpp
|
||||
${MAIN_LIB_DIR}/spells/effects/Moat.cpp
|
||||
${MAIN_LIB_DIR}/spells/effects/Obstacle.cpp
|
||||
${MAIN_LIB_DIR}/spells/effects/Registry.cpp
|
||||
${MAIN_LIB_DIR}/spells/effects/UnitEffect.cpp
|
||||
|
@ -139,7 +139,6 @@
|
||||
"passable" : false,
|
||||
"trap" : false,
|
||||
"trigger" : false,
|
||||
"patchCount" : 1,
|
||||
"turnsRemaining" : 2,
|
||||
"attacker" :{
|
||||
"range" : [[""]],
|
||||
@ -213,7 +212,6 @@
|
||||
"passable" : true,
|
||||
"trap" : false,
|
||||
"trigger" : true,
|
||||
"patchCount" : 1,
|
||||
"turnsRemaining" : 2,
|
||||
"attacker" :{
|
||||
"shape" : [[""]],
|
||||
|
@ -97,7 +97,8 @@ SpellCreatedObstacle::SpellCreatedObstacle()
|
||||
trap(false),
|
||||
removeOnTrigger(false),
|
||||
revealed(false),
|
||||
animationYOffset(0)
|
||||
animationYOffset(0),
|
||||
nativeVisible(true)
|
||||
{
|
||||
obstacleType = SPELL_CREATED;
|
||||
}
|
||||
@ -107,8 +108,11 @@ bool SpellCreatedObstacle::visibleForSide(ui8 side, bool hasNativeStack) const
|
||||
//we hide mines and not discovered quicksands
|
||||
//quicksands are visible to the caster or if owned unit stepped into that particular patch
|
||||
//additionally if side has a native unit, mines/quicksands will be visible
|
||||
//but it is not a case for a moat, so, hasNativeStack should not work for moats
|
||||
|
||||
return casterSide == side || !hidden || revealed || hasNativeStack;
|
||||
auto nativeVis = hasNativeStack && nativeVisible;
|
||||
|
||||
return casterSide == side || !hidden || revealed || nativeVis;
|
||||
}
|
||||
|
||||
bool SpellCreatedObstacle::blocksTiles() const
|
||||
@ -163,6 +167,7 @@ void SpellCreatedObstacle::serializeJson(JsonSerializeFormat & handler)
|
||||
handler.serializeBool("trigger", trigger);
|
||||
handler.serializeBool("trap", trap);
|
||||
handler.serializeBool("removeOnTrigger", removeOnTrigger);
|
||||
handler.serializeBool("nativeVisible", nativeVisible);
|
||||
|
||||
handler.serializeString("appearSound", appearSound);
|
||||
handler.serializeString("appearAnimation", appearAnimation);
|
||||
@ -204,9 +209,4 @@ int SpellCreatedObstacle::getAnimationYOffset(int imageHeight) const
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::vector<BattleHex> MoatObstacle::getAffectedTiles() const
|
||||
{
|
||||
return (*VLC->townh)[ID]->town->moatHexes;
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -77,6 +77,7 @@ struct DLL_LINKAGE SpellCreatedObstacle : CObstacleInstance
|
||||
bool removeOnTrigger;
|
||||
|
||||
bool revealed;
|
||||
bool nativeVisible; //Should native terrain creatures reveal obstacle
|
||||
|
||||
std::string appearSound;
|
||||
std::string appearAnimation;
|
||||
@ -115,6 +116,7 @@ struct DLL_LINKAGE SpellCreatedObstacle : CObstacleInstance
|
||||
h & casterSide;
|
||||
|
||||
h & hidden;
|
||||
h & nativeVisible;
|
||||
h & passable;
|
||||
h & trigger;
|
||||
h & trap;
|
||||
|
119
lib/spells/effects/Moat.cpp
Normal file
119
lib/spells/effects/Moat.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Moat.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 "Moat.h"
|
||||
|
||||
#include "Registry.h"
|
||||
#include "../ISpellMechanics.h"
|
||||
|
||||
#include "../../NetPacks.h"
|
||||
#include "../../mapObjects/CGTownInstance.h"
|
||||
#include "../../battle/IBattleState.h"
|
||||
#include "../../battle/CBattleInfoCallback.h"
|
||||
#include "../../serializer/JsonSerializeFormat.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
static const std::string EFFECT_NAME = "core:moat";
|
||||
|
||||
namespace spells
|
||||
{
|
||||
namespace effects
|
||||
{
|
||||
|
||||
VCMI_REGISTER_SPELL_EFFECT(Moat, EFFECT_NAME);
|
||||
|
||||
void Moat::serializeJsonEffect(JsonSerializeFormat & handler)
|
||||
{
|
||||
handler.serializeBool("hidden", hidden);
|
||||
handler.serializeBool("passable", passable);
|
||||
handler.serializeBool("trigger", trigger);
|
||||
handler.serializeBool("trap", trap);
|
||||
handler.serializeBool("removeOnTrigger", removeOnTrigger);
|
||||
handler.serializeBool("dispellable", dispellable);
|
||||
{
|
||||
JsonArraySerializer customSizeJson = handler.enterArray("moatHexes");
|
||||
customSizeJson.syncSize(moatHexes, JsonNode::JsonType::DATA_INTEGER);
|
||||
|
||||
for(size_t index = 0; index < customSizeJson.size(); index++)
|
||||
customSizeJson.serializeInt(index, moatHexes.at(index));
|
||||
}
|
||||
handler.serializeStruct("defender", sideOptions); //Moats are defender only
|
||||
}
|
||||
|
||||
void Moat::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const
|
||||
{
|
||||
assert(m->isMassive());
|
||||
assert(m->battle()->battleGetDefendedTown());
|
||||
if(m->isMassive() && m->battle()->battleGetSiegeLevel() >= CGTownInstance::CITADEL)
|
||||
{
|
||||
EffectTarget moat;
|
||||
moat.reserve(moatHexes.size());
|
||||
for(const auto & tile : moatHexes)
|
||||
moat.emplace_back(tile);
|
||||
|
||||
placeObstacles(server, m, moat);
|
||||
}
|
||||
}
|
||||
|
||||
void Moat::placeObstacles(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const
|
||||
{
|
||||
assert(m->battle()->battleGetDefendedTown());
|
||||
assert(m->casterSide == BattleSide::DEFENDER); // Moats are always cast by defender
|
||||
assert(turnsRemaining < 0); // Moats should lasts infininte number of turns
|
||||
|
||||
BattleObstaclesChanged pack;
|
||||
|
||||
auto all = m->battle()->battleGetAllObstacles(BattlePerspective::ALL_KNOWING);
|
||||
|
||||
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 = dispellable ? CObstacleInstance::SPELL_CREATED : CObstacleInstance::MOAT;
|
||||
obstacle.ID = m->battle()->battleGetDefendedTown()->subID;
|
||||
|
||||
obstacle.turnsRemaining = -1; //Moat cannot be expired
|
||||
obstacle.casterSpellPower = m->getEffectPower();
|
||||
obstacle.spellLevel = m->getEffectLevel(); //todo: level of indirect effect should be also configurable
|
||||
obstacle.casterSide = BattleSide::DEFENDER; // Moats are always cast by defender
|
||||
obstacle.hidden = hidden;
|
||||
obstacle.passable = true; //Moats always passable
|
||||
obstacle.trigger = trigger;
|
||||
obstacle.trap = trap;
|
||||
obstacle.removeOnTrigger = removeOnTrigger;
|
||||
obstacle.nativeVisible = false; //Moats is invisible for native terrain
|
||||
|
||||
// Moats should not have appear sound and appear animation (they are always exists)
|
||||
// Only trigger animation may exists
|
||||
obstacle.triggerSound = sideOptions.triggerSound;
|
||||
obstacle.triggerAnimation = sideOptions.triggerAnimation;
|
||||
obstacle.animation = sideOptions.animation;
|
||||
|
||||
obstacle.animationYOffset = sideOptions.offsetY;
|
||||
pack.changes.emplace_back();
|
||||
obstacle.toInfo(pack.changes.back());
|
||||
}
|
||||
|
||||
if(!pack.changes.empty())
|
||||
server->apply(&pack);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
38
lib/spells/effects/Moat.h
Normal file
38
lib/spells/effects/Moat.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Moat.h, 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Obstacle.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
namespace spells
|
||||
{
|
||||
namespace effects
|
||||
{
|
||||
|
||||
class Moat : public Obstacle
|
||||
{
|
||||
private:
|
||||
ObstacleSideOptions sideOptions; //Defender only
|
||||
std::vector<BattleHex> moatHexes;
|
||||
bool dispellable; //For Tower landmines
|
||||
public:
|
||||
void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override;
|
||||
protected:
|
||||
void serializeJsonEffect(JsonSerializeFormat & handler) override;
|
||||
void placeObstacles(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
@ -181,30 +181,33 @@ EffectTarget Obstacle::transformTarget(const Mechanics * m, const Target & aimPo
|
||||
|
||||
void Obstacle::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const
|
||||
{
|
||||
if(m->isMassive())
|
||||
if(patchCount > 0)
|
||||
{
|
||||
std::vector<BattleHex> availableTiles;
|
||||
for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
|
||||
auto insertAvailable = [&m](const BattleHex & hex, std::vector<BattleHex> & availableTiles)
|
||||
{
|
||||
BattleHex hex = i;
|
||||
if(isHexAvailable(m->battle(), hex, true))
|
||||
availableTiles.push_back(hex);
|
||||
}
|
||||
};
|
||||
|
||||
if(m->isMassive())
|
||||
for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
|
||||
insertAvailable(BattleHex(i), availableTiles);
|
||||
else
|
||||
for(const auto & destination : target)
|
||||
insertAvailable(destination.hexValue, availableTiles);
|
||||
|
||||
RandomGeneratorUtil::randomShuffle(availableTiles, *server->getRNG());
|
||||
|
||||
const int patchesToPut = std::min(patchCount, static_cast<int>(availableTiles.size()));
|
||||
|
||||
EffectTarget randomTarget;
|
||||
randomTarget.reserve(patchesToPut);
|
||||
for(int i = 0; i < patchesToPut; i++)
|
||||
randomTarget.emplace_back(availableTiles.at(i));
|
||||
|
||||
placeObstacles(server, m, randomTarget);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
placeObstacles(server, m, target);
|
||||
}
|
||||
|
||||
placeObstacles(server, m, target);
|
||||
}
|
||||
|
||||
void Obstacle::serializeJsonEffect(JsonSerializeFormat & handler)
|
||||
@ -213,7 +216,8 @@ void Obstacle::serializeJsonEffect(JsonSerializeFormat & handler)
|
||||
handler.serializeBool("passable", passable);
|
||||
handler.serializeBool("trigger", trigger);
|
||||
handler.serializeBool("trap", trap);
|
||||
handler.serializeBool("removeOnTrigger", removeOnTrigger);
|
||||
handler.serializeBool("removeOnTrigger", removeOnTrigger);
|
||||
handler.serializeBool("hideNative", hideNative);
|
||||
|
||||
handler.serializeInt("patchCount", patchCount);
|
||||
handler.serializeInt("turnsRemaining", turnsRemaining, -1);
|
||||
@ -293,6 +297,7 @@ void Obstacle::placeObstacles(ServerCallback * server, const Mechanics * m, cons
|
||||
obstacle.spellLevel = m->getEffectLevel();//todo: level of indirect effect should be also configurable
|
||||
obstacle.casterSide = m->casterSide;
|
||||
|
||||
obstacle.nativeVisible = !hideNative;
|
||||
obstacle.hidden = hidden;
|
||||
obstacle.passable = passable;
|
||||
obstacle.trigger = trigger;
|
||||
@ -328,7 +333,6 @@ void Obstacle::placeObstacles(ServerCallback * server, const Mechanics * m, cons
|
||||
server->apply(&pack);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,22 +54,22 @@ public:
|
||||
|
||||
protected:
|
||||
void serializeJsonEffect(JsonSerializeFormat & handler) override;
|
||||
virtual void placeObstacles(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const;
|
||||
|
||||
private:
|
||||
bool hidden = false;
|
||||
bool passable = false;
|
||||
bool trigger = false;
|
||||
bool trap = false;
|
||||
bool removeOnTrigger = false;
|
||||
int32_t patchCount = 1;//random patches to place, only for massive spells
|
||||
bool hideNative = false;
|
||||
private:
|
||||
int32_t patchCount = 0; //random patches to place, for massive spells should be >= 1, for non-massive ones if >= 1, then place only this number inside a target (like H5 landMine)
|
||||
int32_t turnsRemaining = -1;
|
||||
|
||||
std::array<ObstacleSideOptions, 2> sideOptions;
|
||||
|
||||
static bool isHexAvailable(const CBattleInfoCallback * cb, const BattleHex & hex, const bool mustBeClear);
|
||||
static bool noRoomToPlace(Problem & problem, const Mechanics * m);
|
||||
|
||||
void placeObstacles(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user