CArtHandler.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  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. std::string ArtifactPositionBase::encode(int32_t index)
  190. {
  191. #define ART_POS(x) #x ,
  192. const std::vector<std::string> artSlots = { ART_POS_LIST };
  193. #undef ART_POS
  194. return artSlots.at(index);
  195. }
  196. std::string ArtifactPositionBase::entityType()
  197. {
  198. return "artifactSlot";
  199. }
  200. void CArtHandler::addSlot(CArtifact * art, const std::string & slotID) const
  201. {
  202. static const std::vector<ArtifactPosition> miscSlots =
  203. {
  204. ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5
  205. };
  206. static const std::vector<ArtifactPosition> ringSlots =
  207. {
  208. ArtifactPosition::RIGHT_RING, ArtifactPosition::LEFT_RING
  209. };
  210. if (slotID == "MISC")
  211. {
  212. vstd::concatenate(art->possibleSlots[ArtBearer::HERO], miscSlots);
  213. }
  214. else if (slotID == "RING")
  215. {
  216. vstd::concatenate(art->possibleSlots[ArtBearer::HERO], ringSlots);
  217. }
  218. else
  219. {
  220. auto slot = ArtifactPosition::decode(slotID);
  221. if (slot != ArtifactPosition::PRE_FIRST)
  222. art->possibleSlots[ArtBearer::HERO].push_back(slot);
  223. }
  224. }
  225. void CArtHandler::loadSlots(CArtifact * art, const JsonNode & node) const
  226. {
  227. if (!node["slot"].isNull()) //we assume non-hero slots are irrelevant?
  228. {
  229. if (node["slot"].getType() == JsonNode::JsonType::DATA_STRING)
  230. addSlot(art, node["slot"].String());
  231. else
  232. {
  233. for (const JsonNode & slot : node["slot"].Vector())
  234. addSlot(art, slot.String());
  235. }
  236. std::sort(art->possibleSlots.at(ArtBearer::HERO).begin(), art->possibleSlots.at(ArtBearer::HERO).end());
  237. }
  238. }
  239. EArtifactClass::Type CArtHandler::stringToClass(const std::string & className)
  240. {
  241. static const std::map<std::string, EArtifactClass::Type> artifactClassMap =
  242. {
  243. {"TREASURE", EArtifactClass::ART_TREASURE},
  244. {"MINOR", EArtifactClass::ART_MINOR},
  245. {"MAJOR", EArtifactClass::ART_MAJOR},
  246. {"RELIC", EArtifactClass::ART_RELIC},
  247. {"SPECIAL", EArtifactClass::ART_SPECIAL}
  248. };
  249. auto it = artifactClassMap.find (className);
  250. if (it != artifactClassMap.end())
  251. return it->second;
  252. logMod->warn("Warning! Artifact rarity %s not recognized!", className);
  253. return EArtifactClass::ART_SPECIAL;
  254. }
  255. void CArtHandler::loadClass(CArtifact * art, const JsonNode & node) const
  256. {
  257. art->aClass = stringToClass(node["class"].String());
  258. }
  259. void CArtHandler::loadType(CArtifact * art, const JsonNode & node) const
  260. {
  261. #define ART_BEARER(x) { #x, ArtBearer::x },
  262. static const std::map<std::string, int> artifactBearerMap = { ART_BEARER_LIST };
  263. #undef ART_BEARER
  264. for (const JsonNode & b : node["type"].Vector())
  265. {
  266. auto it = artifactBearerMap.find (b.String());
  267. if (it != artifactBearerMap.end())
  268. {
  269. int bearerType = it->second;
  270. switch (bearerType)
  271. {
  272. case ArtBearer::HERO://TODO: allow arts having several possible bearers
  273. break;
  274. case ArtBearer::COMMANDER:
  275. makeItCommanderArt (art); //original artifacts should have only one bearer type
  276. break;
  277. case ArtBearer::CREATURE:
  278. makeItCreatureArt (art);
  279. break;
  280. }
  281. }
  282. else
  283. logMod->warn("Warning! Artifact type %s not recognized!", b.String());
  284. }
  285. }
  286. void CArtHandler::loadComponents(CArtifact * art, const JsonNode & node)
  287. {
  288. if(!node["components"].isNull())
  289. {
  290. for(const auto & component : node["components"].Vector())
  291. {
  292. LIBRARY->identifiers()->requestIdentifier("artifact", component, [this, art](int32_t id)
  293. {
  294. // when this code is called both combinational art as well as component are loaded
  295. // so it is safe to access any of them
  296. art->constituents.push_back(ArtifactID(id).toArtifact());
  297. objects[id]->partOf.insert(art);
  298. });
  299. }
  300. }
  301. if(!node["fusedComponents"].isNull())
  302. art->setFused(node["fusedComponents"].Bool());
  303. }
  304. void CArtHandler::makeItCreatureArt(CArtifact * a, bool onlyCreature)
  305. {
  306. if (onlyCreature)
  307. {
  308. a->possibleSlots[ArtBearer::HERO].clear();
  309. a->possibleSlots[ArtBearer::COMMANDER].clear();
  310. }
  311. a->possibleSlots[ArtBearer::CREATURE].push_back(ArtifactPosition::CREATURE_SLOT);
  312. }
  313. void CArtHandler::makeItCommanderArt(CArtifact * a, bool onlyCommander)
  314. {
  315. if (onlyCommander)
  316. {
  317. a->possibleSlots[ArtBearer::HERO].clear();
  318. a->possibleSlots[ArtBearer::CREATURE].clear();
  319. }
  320. for(const auto & slot : ArtifactUtils::commanderSlots())
  321. a->possibleSlots[ArtBearer::COMMANDER].push_back(ArtifactPosition(slot));
  322. }
  323. bool CArtHandler::legalArtifact(const ArtifactID & id) const
  324. {
  325. auto art = id.toArtifact();
  326. //assert ( (!art->constituents) || art->constituents->size() ); //artifacts is not combined or has some components
  327. if(art->isCombined())
  328. return false; //no combo artifacts spawning
  329. if(art->aClass < EArtifactClass::ART_TREASURE || art->aClass > EArtifactClass::ART_RELIC)
  330. return false; // invalid class
  331. if(art->possibleSlots.count(ArtBearer::HERO) && !art->possibleSlots.at(ArtBearer::HERO).empty())
  332. return true;
  333. if(art->possibleSlots.count(ArtBearer::CREATURE) && !art->possibleSlots.at(ArtBearer::CREATURE).empty() && LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_ARTIFACT))
  334. return true;
  335. if(art->possibleSlots.count(ArtBearer::COMMANDER) && !art->possibleSlots.at(ArtBearer::COMMANDER).empty() && LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_COMMANDERS))
  336. return true;
  337. return false;
  338. }
  339. std::set<ArtifactID> CArtHandler::getDefaultAllowed() const
  340. {
  341. std::set<ArtifactID> allowedArtifacts;
  342. for (const auto & artifact : objects)
  343. {
  344. if (!artifact->isCombined())
  345. allowedArtifacts.insert(artifact->getId());
  346. }
  347. return allowedArtifacts;
  348. }
  349. void CArtHandler::afterLoadFinalization()
  350. {
  351. //All artifacts have their id, so we can properly update their bonuses' source ids.
  352. for(auto &art : objects)
  353. {
  354. for(auto &bonus : art->getExportedBonusList())
  355. {
  356. assert(bonus->source == BonusSource::ARTIFACT);
  357. bonus->sid = BonusSourceID(art->id);
  358. }
  359. art->nodeHasChanged();
  360. }
  361. }
  362. VCMI_LIB_NAMESPACE_END