| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 | /* * 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_BEGINObstacleSet::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
 |