CArtHandler.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. /*
  2. * CArtHandler.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 "CArtHandler.h"
  12. #include "ArtifactUtils.h"
  13. #include "../../CCreatureHandler.h"
  14. #include "../../ExceptionsCommon.h"
  15. #include "../../GameLibrary.h"
  16. #include "../../IGameSettings.h"
  17. #include "../../json/JsonBonus.h"
  18. #include "../../mapObjectConstructors/AObjectTypeHandler.h"
  19. #include "../../mapObjectConstructors/CObjectClassesHandler.h"
  20. #include "../../modding/IdentifierStorage.h"
  21. #include "../../texts/CGeneralTextHandler.h"
  22. #include "../../texts/CLegacyConfigParser.h"
  23. // Note: list must match entries in ArtTraits.txt
  24. #define ART_POS_LIST \
  25. ART_POS(SPELLBOOK) \
  26. ART_POS(MACH4) \
  27. ART_POS(MACH3) \
  28. ART_POS(MACH2) \
  29. ART_POS(MACH1) \
  30. ART_POS(MISC5) \
  31. ART_POS(MISC4) \
  32. ART_POS(MISC3) \
  33. ART_POS(MISC2) \
  34. ART_POS(MISC1) \
  35. ART_POS(FEET) \
  36. ART_POS(LEFT_RING) \
  37. ART_POS(RIGHT_RING) \
  38. ART_POS(TORSO) \
  39. ART_POS(LEFT_HAND) \
  40. ART_POS(RIGHT_HAND) \
  41. ART_POS(NECK) \
  42. ART_POS(SHOULDERS) \
  43. ART_POS(HEAD)
  44. VCMI_LIB_NAMESPACE_BEGIN
  45. CArtHandler::~CArtHandler() = default;
  46. std::vector<JsonNode> CArtHandler::loadLegacyData()
  47. {
  48. size_t dataSize = LIBRARY->engineSettings()->getInteger(EGameSettings::TEXTS_ARTIFACT);
  49. objects.resize(dataSize);
  50. std::vector<JsonNode> h3Data;
  51. h3Data.reserve(dataSize);
  52. #define ART_POS(x) #x ,
  53. const std::vector<std::string> artSlots = { ART_POS_LIST };
  54. #undef ART_POS
  55. static const std::map<char, std::string> classes =
  56. {{'S',"SPECIAL"}, {'T',"TREASURE"},{'N',"MINOR"},{'J',"MAJOR"},{'R',"RELIC"},};
  57. CLegacyConfigParser parser(TextPath::builtin("DATA/ARTRAITS.TXT"));
  58. CLegacyConfigParser events(TextPath::builtin("DATA/ARTEVENT.TXT"));
  59. parser.endLine(); // header
  60. parser.endLine();
  61. for (size_t i = 0; i < dataSize; i++)
  62. {
  63. JsonNode artData;
  64. artData["text"]["name"].String() = parser.readString();
  65. artData["text"]["event"].String() = events.readString();
  66. artData["value"].Float() = parser.readNumber();
  67. for(const auto & artSlot : artSlots)
  68. {
  69. if(parser.readString() == "x")
  70. {
  71. artData["slot"].Vector().emplace_back(artSlot);
  72. }
  73. }
  74. std::string artClass = parser.readString();
  75. if (classes.count(artClass[0]))
  76. artData["class"].String() = classes.at(artClass[0]);
  77. else
  78. throw DataLoadingException("File ARTRAITS.TXT is invalid or corrupted! Please reinstall Heroes III data files");
  79. artData["text"]["description"].String() = parser.readString();
  80. parser.endLine();
  81. events.endLine();
  82. h3Data.push_back(artData);
  83. }
  84. return h3Data;
  85. }
  86. void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
  87. {
  88. auto object = loadFromJson(scope, data, name, objects.size());
  89. object->iconIndex = object->getIndex() + 5;
  90. objects.emplace_back(object);
  91. registerObject(scope, "artifact", name, object->id.getNum());
  92. }
  93. void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
  94. {
  95. auto object = loadFromJson(scope, data, name, index);
  96. object->iconIndex = object->getIndex();
  97. assert(objects[index] == nullptr); // ensure that this id was not loaded before
  98. objects[index] = object;
  99. registerObject(scope, "artifact", name, object->id.getNum());
  100. }
  101. const std::vector<std::string> & CArtHandler::getTypeNames() const
  102. {
  103. static const std::vector<std::string> typeNames = { "artifact" };
  104. return typeNames;
  105. }
  106. std::shared_ptr<CArtifact> CArtHandler::loadFromJson(const std::string & scope, const JsonNode & node, const std::string & identifier, size_t index)
  107. {
  108. assert(identifier.find(':') == std::string::npos);
  109. assert(!scope.empty());
  110. auto art = std::make_shared<CArtifact>();
  111. if(!node["growing"].isNull())
  112. {
  113. for(auto bonus : node["growing"]["bonusesPerLevel"].Vector())
  114. {
  115. art->bonusesPerLevel.emplace_back(static_cast<ui16>(bonus["level"].Float()), Bonus());
  116. JsonUtils::parseBonus(bonus["bonus"], &art->bonusesPerLevel.back().second);
  117. }
  118. for(auto bonus : node["growing"]["thresholdBonuses"].Vector())
  119. {
  120. art->thresholdBonuses.emplace_back(static_cast<ui16>(bonus["level"].Float()), Bonus());
  121. JsonUtils::parseBonus(bonus["bonus"], &art->thresholdBonuses.back().second);
  122. }
  123. }
  124. art->id = ArtifactID(index);
  125. art->identifier = identifier;
  126. art->modScope = scope;
  127. const JsonNode & text = node["text"];
  128. LIBRARY->generaltexth->registerString(scope, art->getNameTextID(), text["name"]);
  129. LIBRARY->generaltexth->registerString(scope, art->getDescriptionTextID(), text["description"]);
  130. LIBRARY->generaltexth->registerString(scope, art->getEventTextID(), text["event"]);
  131. const JsonNode & graphics = node["graphics"];
  132. art->image = graphics["image"].String();
  133. if(!graphics["large"].isNull())
  134. art->large = graphics["large"].String();
  135. else
  136. art->large = art->image;
  137. art->advMapDef = graphics["map"].String();
  138. art->price = static_cast<ui32>(node["value"].Float());
  139. art->onlyOnWaterMap = node["onlyOnWaterMap"].Bool();
  140. loadSlots(art.get(), node);
  141. loadClass(art.get(), node);
  142. loadType(art.get(), node);
  143. loadComponents(art.get(), node);
  144. for(const auto & b : node["bonuses"].Vector())
  145. {
  146. auto bonus = JsonUtils::parseBonus(b);
  147. art->addNewBonus(bonus);
  148. }
  149. const JsonNode & warMachine = node["warMachine"];
  150. if(!warMachine.isNull())
  151. {
  152. LIBRARY->identifiers()->requestIdentifier("creature", warMachine, [=](si32 id)
  153. {
  154. art->warMachine = CreatureID(id);
  155. //this assumes that creature object is stored before registration
  156. LIBRARY->creh->objects.at(id)->warMachine = art->id;
  157. });
  158. }
  159. LIBRARY->identifiers()->requestIdentifier(scope, "object", "artifact", [=](si32 index)
  160. {
  161. JsonNode conf;
  162. conf.setModScope(scope);
  163. LIBRARY->objtypeh->loadSubObject(art->identifier, conf, Obj::ARTIFACT, art->getIndex());
  164. if(!art->advMapDef.empty())
  165. {
  166. JsonNode templ;
  167. templ["animation"].String() = art->advMapDef;
  168. templ.setModScope(scope);
  169. // add new template.
  170. // Necessary for objects added via mods that don't have any templates in H3
  171. LIBRARY->objtypeh->getHandlerFor(Obj::ARTIFACT, art->getIndex())->addTemplate(templ);
  172. }
  173. });
  174. if(art->isTradable())
  175. art->possibleSlots.at(ArtBearer::ALTAR).push_back(ArtifactPosition::ALTAR);
  176. return art;
  177. }
  178. int32_t ArtifactPositionBase::decode(const std::string & slotName)
  179. {
  180. #define ART_POS(x) { #x, ArtifactPosition::x },
  181. static const std::map<std::string, ArtifactPosition> artifactPositionMap = { ART_POS_LIST };
  182. #undef ART_POS
  183. auto it = artifactPositionMap.find (slotName);
  184. if (it != artifactPositionMap.end())
  185. return it->second;
  186. else
  187. return PRE_FIRST;
  188. }
  189. void CArtHandler::addSlot(CArtifact * art, const std::string & slotID) const
  190. {
  191. static const std::vector<ArtifactPosition> miscSlots =
  192. {
  193. ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5
  194. };
  195. static const std::vector<ArtifactPosition> ringSlots =
  196. {
  197. ArtifactPosition::RIGHT_RING, ArtifactPosition::LEFT_RING
  198. };
  199. if (slotID == "MISC")
  200. {
  201. vstd::concatenate(art->possibleSlots[ArtBearer::HERO], miscSlots);
  202. }
  203. else if (slotID == "RING")
  204. {
  205. vstd::concatenate(art->possibleSlots[ArtBearer::HERO], ringSlots);
  206. }
  207. else
  208. {
  209. auto slot = ArtifactPosition::decode(slotID);
  210. if (slot != ArtifactPosition::PRE_FIRST)
  211. art->possibleSlots[ArtBearer::HERO].push_back(slot);
  212. }
  213. }
  214. void CArtHandler::loadSlots(CArtifact * art, const JsonNode & node) const
  215. {
  216. if (!node["slot"].isNull()) //we assume non-hero slots are irrelevant?
  217. {
  218. if (node["slot"].getType() == JsonNode::JsonType::DATA_STRING)
  219. addSlot(art, node["slot"].String());
  220. else
  221. {
  222. for (const JsonNode & slot : node["slot"].Vector())
  223. addSlot(art, slot.String());
  224. }
  225. std::sort(art->possibleSlots.at(ArtBearer::HERO).begin(), art->possibleSlots.at(ArtBearer::HERO).end());
  226. }
  227. }
  228. EArtifactClass::Type CArtHandler::stringToClass(const std::string & className)
  229. {
  230. static const std::map<std::string, EArtifactClass::Type> artifactClassMap =
  231. {
  232. {"TREASURE", EArtifactClass::ART_TREASURE},
  233. {"MINOR", EArtifactClass::ART_MINOR},
  234. {"MAJOR", EArtifactClass::ART_MAJOR},
  235. {"RELIC", EArtifactClass::ART_RELIC},
  236. {"SPECIAL", EArtifactClass::ART_SPECIAL}
  237. };
  238. auto it = artifactClassMap.find (className);
  239. if (it != artifactClassMap.end())
  240. return it->second;
  241. logMod->warn("Warning! Artifact rarity %s not recognized!", className);
  242. return EArtifactClass::ART_SPECIAL;
  243. }
  244. void CArtHandler::loadClass(CArtifact * art, const JsonNode & node) const
  245. {
  246. art->aClass = stringToClass(node["class"].String());
  247. }
  248. void CArtHandler::loadType(CArtifact * art, const JsonNode & node) const
  249. {
  250. #define ART_BEARER(x) { #x, ArtBearer::x },
  251. static const std::map<std::string, int> artifactBearerMap = { ART_BEARER_LIST };
  252. #undef ART_BEARER
  253. for (const JsonNode & b : node["type"].Vector())
  254. {
  255. auto it = artifactBearerMap.find (b.String());
  256. if (it != artifactBearerMap.end())
  257. {
  258. int bearerType = it->second;
  259. switch (bearerType)
  260. {
  261. case ArtBearer::HERO://TODO: allow arts having several possible bearers
  262. break;
  263. case ArtBearer::COMMANDER:
  264. makeItCommanderArt (art); //original artifacts should have only one bearer type
  265. break;
  266. case ArtBearer::CREATURE:
  267. makeItCreatureArt (art);
  268. break;
  269. }
  270. }
  271. else
  272. logMod->warn("Warning! Artifact type %s not recognized!", b.String());
  273. }
  274. }
  275. void CArtHandler::loadComponents(CArtifact * art, const JsonNode & node)
  276. {
  277. if(!node["components"].isNull())
  278. {
  279. for(const auto & component : node["components"].Vector())
  280. {
  281. LIBRARY->identifiers()->requestIdentifier("artifact", component, [this, art](int32_t id)
  282. {
  283. // when this code is called both combinational art as well as component are loaded
  284. // so it is safe to access any of them
  285. art->constituents.push_back(ArtifactID(id).toArtifact());
  286. objects[id]->partOf.insert(art);
  287. });
  288. }
  289. }
  290. if(!node["fusedComponents"].isNull())
  291. art->setFused(node["fusedComponents"].Bool());
  292. }
  293. void CArtHandler::makeItCreatureArt(CArtifact * a, bool onlyCreature)
  294. {
  295. if (onlyCreature)
  296. {
  297. a->possibleSlots[ArtBearer::HERO].clear();
  298. a->possibleSlots[ArtBearer::COMMANDER].clear();
  299. }
  300. a->possibleSlots[ArtBearer::CREATURE].push_back(ArtifactPosition::CREATURE_SLOT);
  301. }
  302. void CArtHandler::makeItCommanderArt(CArtifact * a, bool onlyCommander)
  303. {
  304. if (onlyCommander)
  305. {
  306. a->possibleSlots[ArtBearer::HERO].clear();
  307. a->possibleSlots[ArtBearer::CREATURE].clear();
  308. }
  309. for(const auto & slot : ArtifactUtils::commanderSlots())
  310. a->possibleSlots[ArtBearer::COMMANDER].push_back(ArtifactPosition(slot));
  311. }
  312. bool CArtHandler::legalArtifact(const ArtifactID & id) const
  313. {
  314. auto art = id.toArtifact();
  315. //assert ( (!art->constituents) || art->constituents->size() ); //artifacts is not combined or has some components
  316. if(art->isCombined())
  317. return false; //no combo artifacts spawning
  318. if(art->aClass < EArtifactClass::ART_TREASURE || art->aClass > EArtifactClass::ART_RELIC)
  319. return false; // invalid class
  320. if(art->possibleSlots.count(ArtBearer::HERO) && !art->possibleSlots.at(ArtBearer::HERO).empty())
  321. return true;
  322. if(art->possibleSlots.count(ArtBearer::CREATURE) && !art->possibleSlots.at(ArtBearer::CREATURE).empty() && LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_ARTIFACT))
  323. return true;
  324. if(art->possibleSlots.count(ArtBearer::COMMANDER) && !art->possibleSlots.at(ArtBearer::COMMANDER).empty() && LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_COMMANDERS))
  325. return true;
  326. return false;
  327. }
  328. std::set<ArtifactID> CArtHandler::getDefaultAllowed() const
  329. {
  330. std::set<ArtifactID> allowedArtifacts;
  331. for (const auto & artifact : objects)
  332. {
  333. if (!artifact->isCombined())
  334. allowedArtifacts.insert(artifact->getId());
  335. }
  336. return allowedArtifacts;
  337. }
  338. void CArtHandler::afterLoadFinalization()
  339. {
  340. //All artifacts have their id, so we can properly update their bonuses' source ids.
  341. for(auto &art : objects)
  342. {
  343. for(auto &bonus : art->getExportedBonusList())
  344. {
  345. assert(bonus->source == BonusSource::ARTIFACT);
  346. bonus->sid = BonusSourceID(art->id);
  347. }
  348. art->nodeHasChanged();
  349. }
  350. }
  351. VCMI_LIB_NAMESPACE_END