1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Allow filtering obstacles by faction aligmnment

This commit is contained in:
Tomasz Zieliński 2024-04-11 18:10:14 +02:00
parent 6c9d18a85c
commit a356fdaf2a
7 changed files with 122 additions and 23 deletions

View File

@ -27,6 +27,20 @@
}
]
},
"alignment" : {
"anyOf": [
{
"type" : "string",
"enum" : ["good", "evil", "neutral"],
"description" : "Alignment of faction of the zone"
},
{
"type" : "array",
"items" : { "type" : "string" },
"description" : "Alignment of faction of the zone"
}
]
}
}
},

View File

@ -13,7 +13,8 @@ VCMI_LIB_NAMESPACE_BEGIN
enum class EAlignment : int8_t
{
GOOD,
ANY = -1,
GOOD = 0,
EVIL,
NEUTRAL
};

View File

@ -12,6 +12,7 @@
#include "ObstacleSetHandler.h"
#include "../modding/IdentifierStorage.h"
#include "../constants/stringConstants.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -32,21 +33,41 @@ void ObstacleSet::addObstacle(std::shared_ptr<const ObjectTemplate> obstacle)
obstacles.push_back(obstacle);
}
ObstacleSetFilter::ObstacleSetFilter(std::vector<ObstacleSet::EObstacleType> allowedTypes, TerrainId terrain = TerrainId::ANY_TERRAIN):
ObstacleSetFilter::ObstacleSetFilter(std::vector<ObstacleSet::EObstacleType> allowedTypes, TerrainId terrain = TerrainId::ANY_TERRAIN, EAlignment alignment = EAlignment::ANY):
allowedTypes(allowedTypes),
terrain(terrain)
terrain(terrain),
alignment(alignment)
{
}
ObstacleSetFilter::ObstacleSetFilter(ObstacleSet::EObstacleType allowedType, TerrainId terrain = TerrainId::ANY_TERRAIN):
ObstacleSetFilter::ObstacleSetFilter(ObstacleSet::EObstacleType allowedType, TerrainId terrain = TerrainId::ANY_TERRAIN, EAlignment alignment = EAlignment::ANY):
allowedTypes({allowedType}),
terrain(terrain)
terrain(terrain),
alignment(alignment)
{
}
bool ObstacleSetFilter::filter(const ObstacleSet &set) const
{
return (vstd::contains(set.getTerrains(), terrain) || terrain == TerrainId::ANY_TERRAIN);
if (terrain != TerrainId::ANY_TERRAIN && !vstd::contains(set.getTerrains(), terrain))
{
return false;
}
// TODO: Also check specific factions
auto alignments = set.getAlignments();
if (alignment != EAlignment::ANY && !alignments.empty() && !vstd::contains(alignments, alignment))
{
return false;
}
return true;
}
TerrainId ObstacleSetFilter::getTerrain() const
{
return terrain;
}
std::set<TerrainId> ObstacleSet::getTerrains() const
@ -69,6 +90,16 @@ void ObstacleSet::addTerrain(TerrainId terrain)
this->allowedTerrains.insert(terrain);
}
void ObstacleSet::addAlignment(EAlignment alignment)
{
this->allowedAlignments.insert(alignment);
}
std::set<EAlignment> ObstacleSet::getAlignments() const
{
return allowedAlignments;
}
ObstacleSet::EObstacleType ObstacleSet::getType() const
{
return type;
@ -185,6 +216,16 @@ std::vector<ObstacleSet::EObstacleType> ObstacleSetFilter::getAllowedTypes() con
return allowedTypes;
}
void ObstacleSetFilter::setType(ObstacleSet::EObstacleType type)
{
allowedTypes = {type};
}
void ObstacleSetFilter::setTypes(std::vector<ObstacleSet::EObstacleType> types)
{
this->allowedTypes = types;
}
std::vector<JsonNode> ObstacleSetHandler::loadLegacyData()
{
return {};
@ -248,6 +289,29 @@ std::shared_ptr<ObstacleSet> ObstacleSetHandler::loadFromJson(const std::string
}
}
// TODO: Move this parser to some utils
auto parseAlignment = [](const std::string & str) ->EAlignment
{
int alignment = vstd::find_pos(GameConstants::ALIGNMENT_NAMES, str);
if (alignment == -1)
logGlobal->error("Incorrect alignment: ", str);
else
return static_cast<EAlignment>(alignment);
};
if (json["alignment"].isString())
{
os->addAlignment(parseAlignment(json["alignment"].String()));
}
else if (json["alignment"].isVector())
{
auto alignments = json["alignment"].Vector();
for (const auto & node : alignments)
{
os->addAlignment(parseAlignment(node.String()));
}
}
auto templates = json["templates"].Vector();
for (const auto & node : templates)
{

View File

@ -50,6 +50,8 @@ public:
void setTerrain(TerrainId terrain);
void setTerrains(const std::set<TerrainId> & terrains);
void addTerrain(TerrainId terrain);
std::set<EAlignment> getAlignments() const;
void addAlignment(EAlignment alignment);
static EObstacleType typeFromString(const std::string &str);
std::string toString() const;
@ -59,6 +61,7 @@ public:
private:
EObstacleType type;
std::set<TerrainId> allowedTerrains;
std::set<EAlignment> allowedAlignments; // Empty means all
std::vector<std::shared_ptr<const ObjectTemplate>> obstacles;
};
@ -67,17 +70,22 @@ typedef std::vector<std::shared_ptr<ObstacleSet>> TObstacleTypes;
class DLL_LINKAGE ObstacleSetFilter
{
public:
ObstacleSetFilter(ObstacleSet::EObstacleType allowedType, TerrainId terrain);
ObstacleSetFilter(std::vector<ObstacleSet::EObstacleType> allowedTypes, TerrainId terrain);
ObstacleSetFilter(ObstacleSet::EObstacleType allowedType, TerrainId terrain , EAlignment alignment);
ObstacleSetFilter(std::vector<ObstacleSet::EObstacleType> allowedTypes, TerrainId terrain, EAlignment alignment);
bool filter(const ObstacleSet &set) const;
void setType(ObstacleSet::EObstacleType type);
void setTypes(std::vector<ObstacleSet::EObstacleType> types);
std::vector<ObstacleSet::EObstacleType> getAllowedTypes() const;
TerrainId getTerrain() const;
void setAlignment(EAlignment alignment);
private:
std::vector<ObstacleSet::EObstacleType> allowedTypes;
// TODO: Filter by faction, alignment, surface/underground, etc.
EAlignment alignment;
// TODO: Filter by faction, surface/underground, etc.
const TerrainId terrain;
};

View File

@ -52,7 +52,7 @@ void ObstacleProxy::sortObstacles()
});
}
bool ObstacleProxy::prepareBiome(TerrainId terrain, CRandomGenerator & rand)
bool ObstacleProxy::prepareBiome(const ObstacleSetFilter & filter, CRandomGenerator & rand)
{
// FIXME: All the mountains have same ID and mostly same subID, how to differentiate them?
@ -68,7 +68,11 @@ bool ObstacleProxy::prepareBiome(TerrainId terrain, CRandomGenerator & rand)
const size_t MIN_SMALL_SETS = 3;
const size_t MAX_SMALL_SETS = 5;
TObstacleTypes mountainSets = VLC->biomeHandler->getObstacles(ObstacleSetFilter(ObstacleSet::EObstacleType::MOUNTAINS, terrain));
auto terrain = filter.getTerrain();
auto localFilter = filter;
localFilter.setType(ObstacleSet::EObstacleType::MOUNTAINS);
TObstacleTypes mountainSets = VLC->biomeHandler->getObstacles(localFilter);
if (!mountainSets.empty())
{
@ -78,11 +82,12 @@ bool ObstacleProxy::prepareBiome(TerrainId terrain, CRandomGenerator & rand)
}
else
{
logGlobal->warn("No mountain sets found for terrain %s", terrain.encode(terrain.getNum()));
logGlobal->warn("No mountain sets found for terrain %s", TerrainId::encode(terrain.getNum()));
// FIXME: Do we ever want to generate obstacles without any mountains?
}
TObstacleTypes treeSets = VLC->biomeHandler->getObstacles(ObstacleSetFilter(ObstacleSet::EObstacleType::TREES, terrain));
localFilter.setType(ObstacleSet::EObstacleType::TREES);
TObstacleTypes treeSets = VLC->biomeHandler->getObstacles(localFilter);
// 1 or 2 tree sets
size_t treeSetsCount = std::min<size_t>(treeSets.size(), rand.nextInt(1, 2));
@ -94,8 +99,8 @@ bool ObstacleProxy::prepareBiome(TerrainId terrain, CRandomGenerator & rand)
logGlobal->info("Added %d tree sets", treeSetsCount);
// Some obstacle types may be completely missing from water, but it's not a problem
TObstacleTypes largeSets = VLC->biomeHandler->getObstacles(ObstacleSetFilter({ObstacleSet::EObstacleType::LAKES, ObstacleSet::EObstacleType::CRATERS},
terrain));
localFilter.setTypes({ObstacleSet::EObstacleType::LAKES, ObstacleSet::EObstacleType::CRATERS});
TObstacleTypes largeSets = VLC->biomeHandler->getObstacles(localFilter);
// We probably don't want to have lakes and craters at the same time, choose one of them
@ -108,7 +113,8 @@ bool ObstacleProxy::prepareBiome(TerrainId terrain, CRandomGenerator & rand)
logGlobal->info("Added large set of type %s", obstacleSets.back()->getType());
}
TObstacleTypes rockSets = VLC->biomeHandler->getObstacles(ObstacleSetFilter(ObstacleSet::EObstacleType::ROCKS, terrain));
localFilter.setType(ObstacleSet::EObstacleType::ROCKS);
TObstacleTypes rockSets = VLC->biomeHandler->getObstacles(localFilter);
size_t rockSetsCount = std::min<size_t>(rockSets.size(), rand.nextInt(1, 2));
for (size_t i = 0; i < rockSetsCount; i++)
@ -118,7 +124,8 @@ bool ObstacleProxy::prepareBiome(TerrainId terrain, CRandomGenerator & rand)
}
logGlobal->info("Added %d rock sets", rockSetsCount);
TObstacleTypes plantSets = VLC->biomeHandler->getObstacles(ObstacleSetFilter(ObstacleSet::EObstacleType::PLANTS, terrain));
localFilter.setType(ObstacleSet::EObstacleType::PLANTS);
TObstacleTypes plantSets = VLC->biomeHandler->getObstacles(localFilter);
// 1 or 2 sets (3 - rock sets)
size_t plantSetsCount = std::min<size_t>(plantSets.size(), rand.nextInt(1, std::max<size_t>(3 - rockSetsCount, 2)));
@ -138,12 +145,12 @@ bool ObstacleProxy::prepareBiome(TerrainId terrain, CRandomGenerator & rand)
size_t smallSets = rand.nextInt(MIN_SMALL_SETS, maxSmallSets);
TObstacleTypes smallObstacleSets = VLC->biomeHandler->getObstacles(ObstacleSetFilter({ObstacleSet::EObstacleType::STRUCTURES, ObstacleSet::EObstacleType::ANIMALS},
terrain));
localFilter.setTypes({ObstacleSet::EObstacleType::STRUCTURES, ObstacleSet::EObstacleType::ANIMALS});
TObstacleTypes smallObstacleSets = VLC->biomeHandler->getObstacles(localFilter);
RandomGeneratorUtil::randomShuffle(smallObstacleSets, rand);
TObstacleTypes otherSets = VLC->biomeHandler->getObstacles(ObstacleSetFilter(ObstacleSet::EObstacleType::OTHER,
terrain));
localFilter.setType(ObstacleSet::EObstacleType::OTHER);
TObstacleTypes otherSets = VLC->biomeHandler->getObstacles(localFilter);
RandomGeneratorUtil::randomShuffle(otherSets, rand);
while (smallSets > 0)

View File

@ -20,6 +20,7 @@ class CGObjectInstance;
class ObjectTemplate;
class CRandomGenerator;
class IGameCallback;
class ObstacleSetFilter;
class DLL_LINKAGE ObstacleProxy
{
@ -29,7 +30,7 @@ public:
virtual ~ObstacleProxy() = default;
void collectPossibleObstacles(TerrainId terrain);
bool prepareBiome(TerrainId terrain, CRandomGenerator & rand);
bool prepareBiome(const ObstacleSetFilter & filter, CRandomGenerator & rand);
void addBlockedTile(const int3 & tile);

View File

@ -25,6 +25,8 @@
#include "../../mapping/CMap.h"
#include "../../mapping/ObstacleProxy.h"
#include "../../mapObjects/CGObjectInstance.h"
#include "../../mapObjects/ObstacleSetHandler.h"
#include "../../CTownHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -34,7 +36,9 @@ void ObstaclePlacer::process()
if(!manager)
return;
if (!prepareBiome(zone.getTerrainType(), zone.getRand()))
ObstacleSetFilter filter(ObstacleSet::EObstacleType::INVALID, zone.getTerrainType(), zone.getTownType().toFaction()->alignment);
if (!prepareBiome(filter, zone.getRand()))
{
logGlobal->warn("Failed to prepare biome, using all possible obstacles");
// Use all if we fail to create proper biome