ObstacleSetHandler.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. /*
  2. * ObstacleSetHandler.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "ObstacleSetHandler.h"
  12. #include "../modding/IdentifierStorage.h"
  13. #include "../constants/StringConstants.h"
  14. #include "../TerrainHandler.h"
  15. VCMI_LIB_NAMESPACE_BEGIN
  16. ObstacleSet::ObstacleSet():
  17. type(INVALID),
  18. level(EMapLevel::ANY),
  19. allowedTerrains({TerrainId::NONE})
  20. {
  21. }
  22. ObstacleSet::ObstacleSet(EObstacleType type, TerrainId terrain):
  23. type(type),
  24. level(EMapLevel::ANY),
  25. allowedTerrains({terrain})
  26. {
  27. }
  28. void ObstacleSet::addObstacle(std::shared_ptr<const ObjectTemplate> obstacle)
  29. {
  30. obstacles.push_back(obstacle);
  31. }
  32. void ObstacleSet::removeEmptyTemplates()
  33. {
  34. vstd::erase_if(obstacles, [](const std::shared_ptr<const ObjectTemplate> &tmpl)
  35. {
  36. if (tmpl->getBlockedOffsets().empty())
  37. {
  38. logMod->warn("Obstacle template %s blocks no tiles, removing it", tmpl->stringID);
  39. return true;
  40. }
  41. return false;
  42. });
  43. }
  44. ObstacleSetFilter::ObstacleSetFilter(std::vector<ObstacleSet::EObstacleType> allowedTypes,
  45. TerrainId terrain = TerrainId::ANY_TERRAIN,
  46. EMapLevel level = EMapLevel::ANY,
  47. FactionID faction = FactionID::ANY,
  48. EAlignment alignment = EAlignment::ANY):
  49. allowedTypes(allowedTypes),
  50. faction(faction),
  51. alignment(alignment),
  52. terrain(terrain),
  53. level(level)
  54. {
  55. }
  56. ObstacleSetFilter::ObstacleSetFilter(ObstacleSet::EObstacleType allowedType,
  57. TerrainId terrain = TerrainId::ANY_TERRAIN,
  58. EMapLevel level = EMapLevel::ANY,
  59. FactionID faction = FactionID::ANY,
  60. EAlignment alignment = EAlignment::ANY):
  61. allowedTypes({allowedType}),
  62. faction(faction),
  63. alignment(alignment),
  64. terrain(terrain),
  65. level(level)
  66. {
  67. }
  68. bool ObstacleSetFilter::filter(const ObstacleSet &set) const
  69. {
  70. if (terrain != TerrainId::ANY_TERRAIN && !vstd::contains(set.getTerrains(), terrain))
  71. {
  72. return false;
  73. }
  74. if (level != EMapLevel::ANY && set.getLevel() != EMapLevel::ANY)
  75. {
  76. if (level != set.getLevel())
  77. {
  78. return false;
  79. }
  80. }
  81. if (faction != FactionID::ANY)
  82. {
  83. auto factions = set.getFactions();
  84. if (!factions.empty() && !vstd::contains(factions, faction))
  85. {
  86. return false;
  87. }
  88. }
  89. // TODO: Also check specific factions
  90. if (alignment != EAlignment::ANY)
  91. {
  92. auto alignments = set.getAlignments();
  93. if (!alignments.empty() && !vstd::contains(alignments, alignment))
  94. {
  95. return false;
  96. }
  97. }
  98. return true;
  99. }
  100. TerrainId ObstacleSetFilter::getTerrain() const
  101. {
  102. return terrain;
  103. }
  104. std::set<TerrainId> ObstacleSet::getTerrains() const
  105. {
  106. return allowedTerrains;
  107. }
  108. void ObstacleSet::setTerrain(TerrainId terrain)
  109. {
  110. this->allowedTerrains = {terrain};
  111. }
  112. void ObstacleSet::setTerrains(const std::set<TerrainId> & terrains)
  113. {
  114. this->allowedTerrains = terrains;
  115. }
  116. void ObstacleSet::addTerrain(TerrainId terrain)
  117. {
  118. this->allowedTerrains.insert(terrain);
  119. }
  120. EMapLevel ObstacleSet::getLevel() const
  121. {
  122. return level;
  123. }
  124. void ObstacleSet::setLevel(EMapLevel newLevel)
  125. {
  126. level = newLevel;
  127. }
  128. std::set<FactionID> ObstacleSet::getFactions() const
  129. {
  130. return allowedFactions;
  131. }
  132. void ObstacleSet::addFaction(FactionID faction)
  133. {
  134. this->allowedFactions.insert(faction);
  135. }
  136. void ObstacleSet::addAlignment(EAlignment alignment)
  137. {
  138. this->allowedAlignments.insert(alignment);
  139. }
  140. std::set<EAlignment> ObstacleSet::getAlignments() const
  141. {
  142. return allowedAlignments;
  143. }
  144. ObstacleSet::EObstacleType ObstacleSet::getType() const
  145. {
  146. return type;
  147. }
  148. void ObstacleSet::setType(EObstacleType newType)
  149. {
  150. type = newType;
  151. }
  152. std::vector<std::shared_ptr<const ObjectTemplate>> ObstacleSet::getObstacles() const
  153. {
  154. return obstacles;
  155. }
  156. ObstacleSet::EObstacleType ObstacleSetHandler::convertObstacleClass(MapObjectID id)
  157. {
  158. switch (id)
  159. {
  160. case Obj::MOUNTAIN:
  161. case Obj::VOLCANIC_VENT:
  162. case Obj::VOLCANO:
  163. case Obj::REEF:
  164. return ObstacleSet::MOUNTAINS;
  165. case Obj::OAK_TREES:
  166. case Obj::PINE_TREES:
  167. case Obj::TREES:
  168. case Obj::DEAD_VEGETATION:
  169. case Obj::HEDGE:
  170. case Obj::KELP:
  171. case Obj::WILLOW_TREES:
  172. case Obj::YUCCA_TREES:
  173. return ObstacleSet::TREES;
  174. case Obj::FROZEN_LAKE:
  175. case Obj::LAKE:
  176. case Obj::LAVA_FLOW:
  177. case Obj::LAVA_LAKE:
  178. return ObstacleSet::LAKES;
  179. case Obj::CANYON:
  180. case Obj::CRATER:
  181. case Obj::SAND_PIT:
  182. case Obj::TAR_PIT:
  183. return ObstacleSet::CRATERS;
  184. case Obj::HILL:
  185. case Obj::MOUND:
  186. case Obj::OUTCROPPING:
  187. case Obj::ROCK:
  188. case Obj::SAND_DUNE:
  189. case Obj::STALAGMITE:
  190. return ObstacleSet::ROCKS;
  191. case Obj::BUSH:
  192. case Obj::CACTUS:
  193. case Obj::FLOWERS:
  194. case Obj::MUSHROOMS:
  195. case Obj::LOG:
  196. case Obj::MANDRAKE:
  197. case Obj::MOSS:
  198. case Obj::PLANT:
  199. case Obj::SHRUB:
  200. case Obj::STUMP:
  201. case Obj::VINE:
  202. return ObstacleSet::PLANTS;
  203. case Obj::SKULL:
  204. return ObstacleSet::ANIMALS;
  205. default:
  206. return ObstacleSet::OTHER;
  207. }
  208. }
  209. ObstacleSet::EObstacleType ObstacleSet::typeFromString(const std::string &str)
  210. {
  211. static const std::map<std::string, EObstacleType> OBSTACLE_TYPE_NAMES =
  212. {
  213. {"mountain", MOUNTAINS},
  214. {"tree", TREES},
  215. {"lake", LAKES},
  216. {"crater", CRATERS},
  217. {"rock", ROCKS},
  218. {"plant", PLANTS},
  219. {"structure", STRUCTURES},
  220. {"animal", ANIMALS},
  221. {"other", OTHER}
  222. };
  223. if (OBSTACLE_TYPE_NAMES.find(str) != OBSTACLE_TYPE_NAMES.end())
  224. {
  225. return OBSTACLE_TYPE_NAMES.at(str);
  226. }
  227. // TODO: How to handle that?
  228. throw std::runtime_error("Invalid obstacle type: " + str);
  229. }
  230. std::string ObstacleSet::toString() const
  231. {
  232. static const std::map<EObstacleType, std::string> OBSTACLE_TYPE_STRINGS =
  233. {
  234. {MOUNTAINS, "mountain"},
  235. {TREES, "tree"},
  236. {LAKES, "lake"},
  237. {CRATERS, "crater"},
  238. {ROCKS, "rock"},
  239. {PLANTS, "plant"},
  240. {STRUCTURES, "structure"},
  241. {ANIMALS, "animal"},
  242. {OTHER, "other"}
  243. };
  244. return OBSTACLE_TYPE_STRINGS.at(type);
  245. }
  246. EMapLevel ObstacleSet::levelFromString(const std::string &str)
  247. {
  248. static const std::map<std::string, EMapLevel> LEVEL_NAMES =
  249. {
  250. {"surface", EMapLevel::SURFACE},
  251. {"underground", EMapLevel::UNDERGROUND}
  252. };
  253. if (LEVEL_NAMES.find(str) != LEVEL_NAMES.end())
  254. {
  255. return LEVEL_NAMES.at(str);
  256. }
  257. throw std::runtime_error("Invalid map level: " + str);
  258. }
  259. std::vector<ObstacleSet::EObstacleType> ObstacleSetFilter::getAllowedTypes() const
  260. {
  261. return allowedTypes;
  262. }
  263. void ObstacleSetFilter::setType(ObstacleSet::EObstacleType type)
  264. {
  265. allowedTypes = {type};
  266. }
  267. void ObstacleSetFilter::setTypes(const std::vector<ObstacleSet::EObstacleType> & types)
  268. {
  269. this->allowedTypes = types;
  270. }
  271. std::vector<JsonNode> ObstacleSetHandler::loadLegacyData()
  272. {
  273. return {};
  274. }
  275. void ObstacleSetHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
  276. {
  277. auto os = loadFromJson(scope, data, name, biomes.size());
  278. if(os)
  279. {
  280. addObstacleSet(os);
  281. // TODO: Define some const array of object types ("biome" etc.)
  282. VLC->identifiersHandler->registerObject(scope, "biome", name, biomes.back()->id);
  283. }
  284. else
  285. {
  286. logMod->error("Failed to load obstacle set: %s", name);
  287. }
  288. }
  289. void ObstacleSetHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
  290. {
  291. //Unused
  292. loadObject(scope, name, data);
  293. }
  294. std::shared_ptr<ObstacleSet> ObstacleSetHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name, size_t index)
  295. {
  296. auto os = std::make_shared<ObstacleSet>();
  297. os->id = index;
  298. auto biome = json["biome"].Struct();
  299. os->setType(ObstacleSet::typeFromString(biome["objectType"].String()));
  300. // TODO: Handle any (every) terrain option
  301. if (biome["terrain"].isString())
  302. {
  303. auto terrainName = biome["terrain"].String();
  304. VLC->identifiers()->requestIdentifier(scope, "terrain", terrainName, [os](si32 id)
  305. {
  306. os->setTerrain(TerrainId(id));
  307. });
  308. }
  309. else if (biome["terrain"].isVector())
  310. {
  311. auto terrains = biome["terrain"].Vector();
  312. for (const auto & terrain : terrains)
  313. {
  314. VLC->identifiers()->requestIdentifier(scope, "terrain", terrain.String(), [os](si32 id)
  315. {
  316. os->addTerrain(TerrainId(id));
  317. });
  318. }
  319. }
  320. else
  321. {
  322. logMod->error("No terrain specified for obstacle set %s", name);
  323. }
  324. if (biome["level"].isString())
  325. {
  326. auto level = biome["level"].String();
  327. os->setLevel(ObstacleSet::levelFromString(level));
  328. }
  329. auto handleFaction = [os, scope](const std::string & str)
  330. {
  331. VLC->identifiers()->requestIdentifier(scope, "faction", str, [os](si32 id)
  332. {
  333. os->addFaction(FactionID(id));
  334. });
  335. };
  336. if (biome["faction"].isString())
  337. {
  338. auto factionName = biome["faction"].String();
  339. handleFaction(factionName);
  340. }
  341. else if (biome["faction"].isVector())
  342. {
  343. auto factions = biome["faction"].Vector();
  344. for (const auto & node : factions)
  345. {
  346. handleFaction(node.String());
  347. }
  348. }
  349. // TODO: Move this parser to some utils
  350. auto parseAlignment = [](const std::string & str) ->EAlignment
  351. {
  352. int alignment = vstd::find_pos(GameConstants::ALIGNMENT_NAMES, str);
  353. if (alignment == -1)
  354. {
  355. logMod->error("Incorrect alignment: ", str);
  356. return EAlignment::ANY;
  357. }
  358. else
  359. {
  360. return static_cast<EAlignment>(alignment);
  361. }
  362. };
  363. if (biome["alignment"].isString())
  364. {
  365. os->addAlignment(parseAlignment(biome["alignment"].String()));
  366. }
  367. else if (biome["alignment"].isVector())
  368. {
  369. auto alignments = biome["alignment"].Vector();
  370. for (const auto & node : alignments)
  371. {
  372. os->addAlignment(parseAlignment(node.String()));
  373. }
  374. }
  375. auto templates = json["templates"].Vector();
  376. for (const auto & node : templates)
  377. {
  378. logMod->trace("Registering obstacle template: %s in scope %s", node.String(), scope);
  379. auto identifier = boost::algorithm::to_lower_copy(node.String());
  380. auto jsonName = JsonNode(identifier);
  381. VLC->identifiers()->requestIdentifier(node.getModScope(), "obstacleTemplate", identifier, [this, os](si32 id)
  382. {
  383. logMod->trace("Resolved obstacle id: %d", id);
  384. os->addObstacle(obstacleTemplates[id]);
  385. });
  386. }
  387. return os;
  388. }
  389. void ObstacleSetHandler::addTemplate(const std::string & scope, const std::string &name, std::shared_ptr<const ObjectTemplate> tmpl)
  390. {
  391. auto id = obstacleTemplates.size();
  392. auto strippedName = boost::algorithm::to_lower_copy(name);
  393. auto pos = strippedName.find(".def");
  394. if(pos != std::string::npos)
  395. strippedName.erase(pos, 4);
  396. if (VLC->identifiersHandler->getIdentifier(scope, "obstacleTemplate", strippedName, true))
  397. {
  398. logMod->warn("Duplicate obstacle template: %s", strippedName);
  399. return;
  400. }
  401. else
  402. {
  403. // Save by name
  404. VLC->identifiersHandler->registerObject(scope, "obstacleTemplate", strippedName, id);
  405. // Index by id
  406. obstacleTemplates[id] = tmpl;
  407. }
  408. }
  409. void ObstacleSetHandler::addObstacleSet(std::shared_ptr<ObstacleSet> os)
  410. {
  411. biomes.push_back(os);
  412. }
  413. void ObstacleSetHandler::afterLoadFinalization()
  414. {
  415. for(const auto & os : biomes)
  416. os->removeEmptyTemplates();
  417. vstd::erase_if(biomes, [](const std::shared_ptr<ObstacleSet> &os)
  418. {
  419. if (os->getObstacles().empty())
  420. {
  421. logMod->warn("Obstacle set %d is empty, removing it", os->id);
  422. return true;
  423. }
  424. return false;
  425. });
  426. // Populate map
  427. for(const auto & os : biomes)
  428. obstacleSets[os->getType()].push_back(os);
  429. }
  430. TObstacleTypes ObstacleSetHandler::getObstacles( const ObstacleSetFilter &filter) const
  431. {
  432. TObstacleTypes result;
  433. for (const auto &allowedType : filter.getAllowedTypes())
  434. {
  435. auto it = obstacleSets.find(allowedType);
  436. if(it != obstacleSets.end())
  437. {
  438. for (const auto &os : it->second)
  439. {
  440. if (filter.filter(*os))
  441. {
  442. result.push_back(os);
  443. }
  444. }
  445. }
  446. }
  447. return result;
  448. }
  449. VCMI_LIB_NAMESPACE_END