1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-20 20:23:03 +02:00
vcmi/lib/mapObjects/ObstacleSetHandler.cpp
Ivan Savenko 5d6470e527
Merge pull request #4025 from IvanSavenko/shared_ptr_lib
[1.6] Use shared_ptr for library entities
2024-05-30 19:14:06 +03:00

521 lines
11 KiB
C++

/*
* ObstacleSetHandler.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 "ObstacleSetHandler.h"
#include "../modding/IdentifierStorage.h"
#include "../constants/StringConstants.h"
#include "../TerrainHandler.h"
#include "../VCMI_Lib.h"
VCMI_LIB_NAMESPACE_BEGIN
ObstacleSet::ObstacleSet():
type(INVALID),
level(EMapLevel::ANY),
allowedTerrains({TerrainId::NONE})
{
}
ObstacleSet::ObstacleSet(EObstacleType type, TerrainId terrain):
type(type),
level(EMapLevel::ANY),
allowedTerrains({terrain})
{
}
void ObstacleSet::addObstacle(std::shared_ptr<const ObjectTemplate> obstacle)
{
obstacles.push_back(obstacle);
}
void ObstacleSet::removeEmptyTemplates()
{
vstd::erase_if(obstacles, [](const std::shared_ptr<const ObjectTemplate> &tmpl)
{
if (tmpl->getBlockedOffsets().empty())
{
logMod->warn("Obstacle template %s blocks no tiles, removing it", tmpl->stringID);
return true;
}
return false;
});
}
ObstacleSetFilter::ObstacleSetFilter(std::vector<ObstacleSet::EObstacleType> allowedTypes,
TerrainId terrain = TerrainId::ANY_TERRAIN,
EMapLevel level = EMapLevel::ANY,
FactionID faction = FactionID::ANY,
EAlignment alignment = EAlignment::ANY):
allowedTypes(allowedTypes),
faction(faction),
alignment(alignment),
terrain(terrain),
level(level)
{
}
ObstacleSetFilter::ObstacleSetFilter(ObstacleSet::EObstacleType allowedType,
TerrainId terrain = TerrainId::ANY_TERRAIN,
EMapLevel level = EMapLevel::ANY,
FactionID faction = FactionID::ANY,
EAlignment alignment = EAlignment::ANY):
allowedTypes({allowedType}),
faction(faction),
alignment(alignment),
terrain(terrain),
level(level)
{
}
bool ObstacleSetFilter::filter(const ObstacleSet &set) const
{
if (terrain != TerrainId::ANY_TERRAIN && !vstd::contains(set.getTerrains(), terrain))
{
return false;
}
if (level != EMapLevel::ANY && set.getLevel() != EMapLevel::ANY)
{
if (level != set.getLevel())
{
return false;
}
}
if (faction != FactionID::ANY)
{
auto factions = set.getFactions();
if (!factions.empty() && !vstd::contains(factions, faction))
{
return false;
}
}
// TODO: Also check specific factions
if (alignment != EAlignment::ANY)
{
auto alignments = set.getAlignments();
if (!alignments.empty() && !vstd::contains(alignments, alignment))
{
return false;
}
}
return true;
}
TerrainId ObstacleSetFilter::getTerrain() const
{
return terrain;
}
std::set<TerrainId> ObstacleSet::getTerrains() const
{
return allowedTerrains;
}
void ObstacleSet::setTerrain(TerrainId terrain)
{
this->allowedTerrains = {terrain};
}
void ObstacleSet::setTerrains(const std::set<TerrainId> & terrains)
{
this->allowedTerrains = terrains;
}
void ObstacleSet::addTerrain(TerrainId terrain)
{
this->allowedTerrains.insert(terrain);
}
EMapLevel ObstacleSet::getLevel() const
{
return level;
}
void ObstacleSet::setLevel(EMapLevel newLevel)
{
level = newLevel;
}
std::set<FactionID> ObstacleSet::getFactions() const
{
return allowedFactions;
}
void ObstacleSet::addFaction(FactionID faction)
{
this->allowedFactions.insert(faction);
}
void ObstacleSet::addAlignment(EAlignment alignment)
{
this->allowedAlignments.insert(alignment);
}
std::set<EAlignment> ObstacleSet::getAlignments() const
{
return allowedAlignments;
}
ObstacleSet::EObstacleType ObstacleSet::getType() const
{
return type;
}
void ObstacleSet::setType(EObstacleType newType)
{
type = newType;
}
std::vector<std::shared_ptr<const ObjectTemplate>> ObstacleSet::getObstacles() const
{
return obstacles;
}
ObstacleSet::EObstacleType ObstacleSetHandler::convertObstacleClass(MapObjectID id)
{
switch (id)
{
case Obj::MOUNTAIN:
case Obj::VOLCANIC_VENT:
case Obj::VOLCANO:
case Obj::REEF:
return ObstacleSet::MOUNTAINS;
case Obj::OAK_TREES:
case Obj::PINE_TREES:
case Obj::TREES:
case Obj::DEAD_VEGETATION:
case Obj::HEDGE:
case Obj::KELP:
case Obj::WILLOW_TREES:
case Obj::YUCCA_TREES:
return ObstacleSet::TREES;
case Obj::FROZEN_LAKE:
case Obj::LAKE:
case Obj::LAVA_FLOW:
case Obj::LAVA_LAKE:
return ObstacleSet::LAKES;
case Obj::CANYON:
case Obj::CRATER:
case Obj::SAND_PIT:
case Obj::TAR_PIT:
return ObstacleSet::CRATERS;
case Obj::HILL:
case Obj::MOUND:
case Obj::OUTCROPPING:
case Obj::ROCK:
case Obj::SAND_DUNE:
case Obj::STALAGMITE:
return ObstacleSet::ROCKS;
case Obj::BUSH:
case Obj::CACTUS:
case Obj::FLOWERS:
case Obj::MUSHROOMS:
case Obj::LOG:
case Obj::MANDRAKE:
case Obj::MOSS:
case Obj::PLANT:
case Obj::SHRUB:
case Obj::STUMP:
case Obj::VINE:
return ObstacleSet::PLANTS;
case Obj::SKULL:
return ObstacleSet::ANIMALS;
default:
return ObstacleSet::OTHER;
}
}
ObstacleSet::EObstacleType ObstacleSet::typeFromString(const std::string &str)
{
static const std::map<std::string, EObstacleType> OBSTACLE_TYPE_NAMES =
{
{"mountain", MOUNTAINS},
{"tree", TREES},
{"lake", LAKES},
{"crater", CRATERS},
{"rock", ROCKS},
{"plant", PLANTS},
{"structure", STRUCTURES},
{"animal", ANIMALS},
{"other", OTHER}
};
if (OBSTACLE_TYPE_NAMES.find(str) != OBSTACLE_TYPE_NAMES.end())
{
return OBSTACLE_TYPE_NAMES.at(str);
}
// TODO: How to handle that?
throw std::runtime_error("Invalid obstacle type: " + str);
}
std::string ObstacleSet::toString() const
{
static const std::map<EObstacleType, std::string> OBSTACLE_TYPE_STRINGS =
{
{MOUNTAINS, "mountain"},
{TREES, "tree"},
{LAKES, "lake"},
{CRATERS, "crater"},
{ROCKS, "rock"},
{PLANTS, "plant"},
{STRUCTURES, "structure"},
{ANIMALS, "animal"},
{OTHER, "other"}
};
return OBSTACLE_TYPE_STRINGS.at(type);
}
EMapLevel ObstacleSet::levelFromString(const std::string &str)
{
static const std::map<std::string, EMapLevel> LEVEL_NAMES =
{
{"surface", EMapLevel::SURFACE},
{"underground", EMapLevel::UNDERGROUND}
};
if (LEVEL_NAMES.find(str) != LEVEL_NAMES.end())
{
return LEVEL_NAMES.at(str);
}
throw std::runtime_error("Invalid map level: " + str);
}
std::vector<ObstacleSet::EObstacleType> ObstacleSetFilter::getAllowedTypes() const
{
return allowedTypes;
}
void ObstacleSetFilter::setType(ObstacleSet::EObstacleType type)
{
allowedTypes = {type};
}
void ObstacleSetFilter::setTypes(const std::vector<ObstacleSet::EObstacleType> & types)
{
this->allowedTypes = types;
}
std::vector<JsonNode> ObstacleSetHandler::loadLegacyData()
{
return {};
}
void ObstacleSetHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
auto os = loadFromJson(scope, data, name, biomes.size());
if(os)
{
addObstacleSet(os);
// TODO: Define some const array of object types ("biome" etc.)
VLC->identifiersHandler->registerObject(scope, "biome", name, biomes.back()->id);
}
else
{
logMod->error("Failed to load obstacle set: %s", name);
}
}
void ObstacleSetHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{
//Unused
loadObject(scope, name, data);
}
std::shared_ptr<ObstacleSet> ObstacleSetHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name, size_t index)
{
auto os = std::make_shared<ObstacleSet>();
os->id = index;
auto biome = json["biome"].Struct();
os->setType(ObstacleSet::typeFromString(biome["objectType"].String()));
// TODO: Handle any (every) terrain option
if (biome["terrain"].isString())
{
auto terrainName = biome["terrain"].String();
VLC->identifiers()->requestIdentifier(scope, "terrain", terrainName, [os](si32 id)
{
os->setTerrain(TerrainId(id));
});
}
else if (biome["terrain"].isVector())
{
auto terrains = biome["terrain"].Vector();
for (const auto & terrain : terrains)
{
VLC->identifiers()->requestIdentifier(scope, "terrain", terrain.String(), [os](si32 id)
{
os->addTerrain(TerrainId(id));
});
}
}
else
{
logMod->error("No terrain specified for obstacle set %s", name);
}
if (biome["level"].isString())
{
auto level = biome["level"].String();
os->setLevel(ObstacleSet::levelFromString(level));
}
auto handleFaction = [os, scope](const std::string & str)
{
VLC->identifiers()->requestIdentifier(scope, "faction", str, [os](si32 id)
{
os->addFaction(FactionID(id));
});
};
if (biome["faction"].isString())
{
auto factionName = biome["faction"].String();
handleFaction(factionName);
}
else if (biome["faction"].isVector())
{
auto factions = biome["faction"].Vector();
for (const auto & node : factions)
{
handleFaction(node.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)
{
logMod->error("Incorrect alignment: ", str);
return EAlignment::ANY;
}
else
{
return static_cast<EAlignment>(alignment);
}
};
if (biome["alignment"].isString())
{
os->addAlignment(parseAlignment(biome["alignment"].String()));
}
else if (biome["alignment"].isVector())
{
auto alignments = biome["alignment"].Vector();
for (const auto & node : alignments)
{
os->addAlignment(parseAlignment(node.String()));
}
}
auto templates = json["templates"].Vector();
for (const auto & node : templates)
{
logMod->trace("Registering obstacle template: %s in scope %s", node.String(), scope);
auto identifier = boost::algorithm::to_lower_copy(node.String());
auto jsonName = JsonNode(identifier);
VLC->identifiers()->requestIdentifier(node.getModScope(), "obstacleTemplate", identifier, [this, os](si32 id)
{
logMod->trace("Resolved obstacle id: %d", id);
os->addObstacle(obstacleTemplates[id]);
});
}
return os;
}
void ObstacleSetHandler::addTemplate(const std::string & scope, const std::string &name, std::shared_ptr<const ObjectTemplate> tmpl)
{
auto id = obstacleTemplates.size();
auto strippedName = boost::algorithm::to_lower_copy(name);
auto pos = strippedName.find(".def");
if(pos != std::string::npos)
strippedName.erase(pos, 4);
if (VLC->identifiersHandler->getIdentifier(scope, "obstacleTemplate", strippedName, true))
{
logMod->warn("Duplicate obstacle template: %s", strippedName);
return;
}
else
{
// Save by name
VLC->identifiersHandler->registerObject(scope, "obstacleTemplate", strippedName, id);
// Index by id
obstacleTemplates[id] = tmpl;
}
}
void ObstacleSetHandler::addObstacleSet(std::shared_ptr<ObstacleSet> os)
{
biomes.push_back(os);
}
void ObstacleSetHandler::afterLoadFinalization()
{
for(const auto & os : biomes)
os->removeEmptyTemplates();
vstd::erase_if(biomes, [](const std::shared_ptr<ObstacleSet> &os)
{
if (os->getObstacles().empty())
{
logMod->warn("Obstacle set %d is empty, removing it", os->id);
return true;
}
return false;
});
// Populate map
for(const auto & os : biomes)
obstacleSets[os->getType()].push_back(os);
}
TObstacleTypes ObstacleSetHandler::getObstacles( const ObstacleSetFilter &filter) const
{
TObstacleTypes result;
for (const auto &allowedType : filter.getAllowedTypes())
{
auto it = obstacleSets.find(allowedType);
if(it != obstacleSets.end())
{
for (const auto &os : it->second)
{
if (filter.filter(*os))
{
result.push_back(os);
}
}
}
}
return result;
}
VCMI_LIB_NAMESPACE_END