1
0
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:
Konstantin 2023-03-16 01:49:40 +03:00
parent d90d00eeac
commit 57c35f39ca
8 changed files with 188 additions and 26 deletions

View File

@ -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

View File

@ -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" : [[""]],

View File

@ -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

View File

@ -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
View 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
View 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

View File

@ -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);
}
}
}

View File

@ -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;
};
}