CampaignBonus.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. * CampaignBonus.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 "CampaignBonus.h"
  12. #include "../filesystem/CBinaryReader.h"
  13. #include "../mapping/MapIdentifiersH3M.h"
  14. #include "../json/JsonNode.h"
  15. #include "../constants/StringConstants.h"
  16. VCMI_LIB_NAMESPACE_BEGIN
  17. static const std::map<std::string, CampaignBonusType> bonusTypeMap = {
  18. {"spell", CampaignBonusType::SPELL},
  19. {"creature", CampaignBonusType::MONSTER},
  20. {"building", CampaignBonusType::BUILDING},
  21. {"artifact", CampaignBonusType::ARTIFACT},
  22. {"scroll", CampaignBonusType::SPELL_SCROLL},
  23. {"primarySkill", CampaignBonusType::PRIMARY_SKILL},
  24. {"secondarySkill", CampaignBonusType::SECONDARY_SKILL},
  25. {"resource", CampaignBonusType::RESOURCE},
  26. {"prevHero", CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO},
  27. {"hero", CampaignBonusType::HERO},
  28. };
  29. static const std::map<std::string, ui16> heroSpecialMap = {
  30. {"strongest", HeroTypeID::CAMP_STRONGEST},
  31. {"generated", HeroTypeID::CAMP_GENERATED},
  32. {"random", HeroTypeID::CAMP_RANDOM}
  33. };
  34. static const std::map<std::string, ui8> resourceTypeMap = {
  35. {"wood", EGameResID::WOOD},
  36. {"mercury", EGameResID::MERCURY},
  37. {"ore", EGameResID::ORE},
  38. {"sulfur", EGameResID::SULFUR},
  39. {"crystal", EGameResID::CRYSTAL},
  40. {"gems", EGameResID::GEMS},
  41. {"gold", EGameResID::GOLD},
  42. {"common", EGameResID::COMMON},
  43. {"rare", EGameResID::RARE}
  44. };
  45. CampaignBonus::CampaignBonus(CBinaryReader & reader, const MapIdentifiersH3M & remapper, CampaignStartOptions mode)
  46. {
  47. switch(mode)
  48. {
  49. case CampaignStartOptions::NONE:
  50. // in this mode game should not attempt to create instances of bonuses
  51. throw std::runtime_error("Attempt to create empty campaign bonus");
  52. case CampaignStartOptions::START_BONUS:
  53. {
  54. auto bonusType = static_cast<CampaignBonusType>(reader.readUInt8());
  55. switch(bonusType)
  56. {
  57. case CampaignBonusType::SPELL:
  58. {
  59. HeroTypeID hero(reader.readInt16());
  60. SpellID spell(reader.readUInt8());
  61. data = CampaignBonusSpell{remapper.remap(hero), spell};
  62. break;
  63. }
  64. case CampaignBonusType::MONSTER:
  65. {
  66. HeroTypeID hero(reader.readInt16());
  67. CreatureID creature(reader.readUInt16());
  68. int32_t amount = reader.readUInt16();
  69. data = CampaignBonusCreatures{remapper.remap(hero), remapper.remap(creature), amount};
  70. break;
  71. }
  72. case CampaignBonusType::BUILDING:
  73. {
  74. BuildingID building(reader.readUInt8());
  75. data = CampaignBonusBuilding{building, remapper.remapBuilding(std::nullopt, building)};
  76. break;
  77. }
  78. case CampaignBonusType::ARTIFACT:
  79. {
  80. HeroTypeID hero(reader.readInt16());
  81. ArtifactID artifact(reader.readUInt16());
  82. data = CampaignBonusArtifact{remapper.remap(hero), remapper.remap(artifact)};
  83. break;
  84. }
  85. case CampaignBonusType::SPELL_SCROLL:
  86. {
  87. HeroTypeID hero(reader.readInt16());
  88. SpellID spell(reader.readUInt8());
  89. data = CampaignBonusSpellScroll{remapper.remap(hero), spell};
  90. break;
  91. }
  92. case CampaignBonusType::PRIMARY_SKILL:
  93. {
  94. HeroTypeID hero(reader.readInt16());
  95. std::array<uint8_t, 4> amounts = {};
  96. for(auto & value : amounts)
  97. value = reader.readUInt8();
  98. data = CampaignBonusPrimarySkill{remapper.remap(hero), amounts};
  99. break;
  100. }
  101. case CampaignBonusType::SECONDARY_SKILL:
  102. {
  103. HeroTypeID hero(reader.readInt16());
  104. SecondarySkill skill(reader.readUInt8());
  105. int32_t skillMastery(reader.readUInt8());
  106. data = CampaignBonusSecondarySkill{remapper.remap(hero), remapper.remap(skill), skillMastery};
  107. break;
  108. }
  109. case CampaignBonusType::RESOURCE:
  110. {
  111. GameResID resource(reader.readInt8());
  112. int32_t amount(reader.readInt32());
  113. data = CampaignBonusStartingResources{resource, amount};
  114. break;
  115. }
  116. default:
  117. throw std::runtime_error("Corrupted or unsupported h3c file");
  118. }
  119. break;
  120. }
  121. case CampaignStartOptions::HERO_CROSSOVER: //reading of players (colors / scenarios ?) player can choose
  122. {
  123. PlayerColor player(reader.readUInt8());
  124. CampaignScenarioID scenario(reader.readUInt8());
  125. data = CampaignBonusHeroesFromScenario{player, scenario};
  126. break;
  127. }
  128. case CampaignStartOptions::HERO_OPTIONS: //heroes player can choose between
  129. {
  130. PlayerColor player(reader.readUInt8());
  131. HeroTypeID hero(reader.readInt16());
  132. data = CampaignBonusStartingHero{player, remapper.remap(hero)};
  133. break;
  134. }
  135. default:
  136. {
  137. throw std::runtime_error("Corrupted or unsupported h3c file");
  138. }
  139. }
  140. }
  141. CampaignBonus::CampaignBonus(const JsonNode & bjson, CampaignStartOptions mode)
  142. {
  143. CampaignBonusType bonusType;
  144. if (!bjson["what"].isNull())
  145. {
  146. bonusType = bonusTypeMap.at(bjson["what"].String());
  147. }
  148. else if (mode == CampaignStartOptions::HERO_CROSSOVER)
  149. {
  150. bonusType = CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO;
  151. }
  152. else if (mode == CampaignStartOptions::HERO_OPTIONS)
  153. {
  154. bonusType = CampaignBonusType::HERO;
  155. }
  156. else
  157. {
  158. throw std::runtime_error("Corrupted or unsupported vcmp file");
  159. }
  160. const auto & decodeHeroTypeID = [](JsonNode heroType) -> HeroTypeID
  161. {
  162. if(vstd::contains(heroSpecialMap, heroType.String()))
  163. return HeroTypeID(heroSpecialMap.at(heroType.String()));
  164. else
  165. return HeroTypeID(HeroTypeID::decode(heroType.String()));
  166. };
  167. switch(bonusType)
  168. {
  169. case CampaignBonusType::SPELL:
  170. {
  171. HeroTypeID hero(decodeHeroTypeID(bjson["hero"]));
  172. SpellID spell(SpellID::decode(bjson["spellType"].String()));
  173. data = CampaignBonusSpell{hero, spell};
  174. break;
  175. }
  176. case CampaignBonusType::MONSTER:
  177. {
  178. HeroTypeID hero(decodeHeroTypeID(bjson["hero"]));
  179. CreatureID creature(CreatureID::decode(bjson["creatureType"].String()));
  180. int32_t amount = bjson["creatureAmount"].Integer();
  181. data = CampaignBonusCreatures{hero, creature, amount};
  182. break;
  183. }
  184. case CampaignBonusType::BUILDING:
  185. {
  186. BuildingID building(vstd::find_pos(EBuildingType::names, bjson["buildingType"].String()));
  187. data = CampaignBonusBuilding{building, building};
  188. break;
  189. }
  190. case CampaignBonusType::ARTIFACT:
  191. {
  192. HeroTypeID hero(decodeHeroTypeID(bjson["hero"]));
  193. ArtifactID artifact(ArtifactID::decode(bjson["artifactType"].String()));
  194. data = CampaignBonusArtifact{hero, artifact};
  195. break;
  196. }
  197. case CampaignBonusType::SPELL_SCROLL:
  198. {
  199. HeroTypeID hero(decodeHeroTypeID(bjson["hero"]));
  200. SpellID spell(SpellID::decode(bjson["spellType"].String()));
  201. data = CampaignBonusSpellScroll{hero, spell};
  202. break;
  203. }
  204. case CampaignBonusType::PRIMARY_SKILL:
  205. {
  206. HeroTypeID hero(decodeHeroTypeID(bjson["hero"]));
  207. std::array<uint8_t, 4> amounts = {};
  208. for(size_t i = 0; i < amounts.size(); ++i)
  209. amounts[i] = bjson[NPrimarySkill::names[i]].Integer();
  210. data = CampaignBonusPrimarySkill{hero, amounts};
  211. break;
  212. }
  213. case CampaignBonusType::SECONDARY_SKILL:
  214. {
  215. HeroTypeID hero(decodeHeroTypeID(bjson["hero"]));
  216. SecondarySkill skill(SecondarySkill::decode(bjson["skillType"].String()));
  217. int32_t skillMastery = bjson["skillMastery"].Integer();
  218. data = CampaignBonusSecondarySkill{hero, skill, skillMastery};
  219. break;
  220. }
  221. case CampaignBonusType::RESOURCE:
  222. {
  223. GameResID resource(resourceTypeMap.at(bjson["resourceType"].String()));
  224. int32_t resourceAmount(bjson["resourceAmount"].Integer());
  225. data = CampaignBonusStartingResources{resource, resourceAmount};
  226. break;
  227. }
  228. case CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO: //reading of players (colors / scenarios ?) player can choose
  229. {
  230. PlayerColor player(SecondarySkill::decode(bjson["playerColor"].String()));
  231. CampaignScenarioID scenario(bjson["scenario"].Integer());
  232. data = CampaignBonusHeroesFromScenario{player, scenario};
  233. break;
  234. }
  235. case CampaignBonusType::HERO: //heroes player can choose between
  236. {
  237. PlayerColor player(SecondarySkill::decode(bjson["playerColor"].String()));
  238. HeroTypeID hero(decodeHeroTypeID(bjson["hero"]));
  239. data = CampaignBonusStartingHero{player, hero};
  240. break;
  241. }
  242. default:
  243. throw std::runtime_error("Corrupted or unsupported h3c file");
  244. }
  245. }
  246. JsonNode CampaignBonus::toJson() const
  247. {
  248. JsonNode bnode;
  249. const auto & encodeHeroTypeID = [](HeroTypeID heroType) -> JsonNode
  250. {
  251. if(vstd::contains(vstd::reverseMap(heroSpecialMap), heroType))
  252. return JsonNode(vstd::reverseMap(heroSpecialMap)[heroType]);
  253. else
  254. return JsonNode(HeroTypeID::encode(heroType));
  255. };
  256. bnode["what"].String() = vstd::reverseMap(bonusTypeMap).at(getType());
  257. switch (getType())
  258. {
  259. case CampaignBonusType::SPELL:
  260. {
  261. const auto & bonusValue = getValue<CampaignBonusSpell>();
  262. bnode["hero"] = encodeHeroTypeID(bonusValue.hero);
  263. bnode["spellType"].String() = SpellID::encode(bonusValue.spell.getNum());
  264. break;
  265. }
  266. case CampaignBonusType::MONSTER:
  267. {
  268. const auto & bonusValue = getValue<CampaignBonusCreatures>();
  269. bnode["hero"] = encodeHeroTypeID(bonusValue.hero);
  270. bnode["creatureType"].String() = CreatureID::encode(bonusValue.creature.getNum());
  271. bnode["creatureAmount"].Integer() = bonusValue.amount;
  272. break;
  273. }
  274. case CampaignBonusType::BUILDING:
  275. {
  276. const auto & bonusValue = getValue<CampaignBonusBuilding>();
  277. bnode["buildingType"].String() = EBuildingType::names[bonusValue.buildingDecoded.getNum()];
  278. break;
  279. }
  280. case CampaignBonusType::ARTIFACT:
  281. {
  282. const auto & bonusValue = getValue<CampaignBonusArtifact>();
  283. bnode["hero"] = encodeHeroTypeID(bonusValue.hero);
  284. bnode["artifactType"].String() = ArtifactID::encode(bonusValue.artifact.getNum());
  285. break;
  286. }
  287. case CampaignBonusType::SPELL_SCROLL:
  288. {
  289. const auto & bonusValue = getValue<CampaignBonusSpellScroll>();
  290. bnode["hero"] = encodeHeroTypeID(bonusValue.hero);
  291. bnode["spellType"].String() = SpellID::encode(bonusValue.spell.getNum());
  292. break;
  293. }
  294. case CampaignBonusType::PRIMARY_SKILL:
  295. {
  296. const auto & bonusValue = getValue<CampaignBonusPrimarySkill>();
  297. bnode["hero"] = encodeHeroTypeID(bonusValue.hero);
  298. for(size_t i = 0; i < bonusValue.amounts.size(); ++i)
  299. bnode[NPrimarySkill::names[i]].Integer() = bonusValue.amounts[i];
  300. break;
  301. }
  302. case CampaignBonusType::SECONDARY_SKILL:
  303. {
  304. const auto & bonusValue = getValue<CampaignBonusSecondarySkill>();
  305. bnode["hero"] = encodeHeroTypeID(bonusValue.hero);
  306. bnode["skillType"].String() = SecondarySkill::encode(bonusValue.skill.getNum());
  307. bnode["skillMastery"].Integer() = bonusValue.mastery;
  308. break;
  309. }
  310. case CampaignBonusType::RESOURCE:
  311. {
  312. const auto & bonusValue = getValue<CampaignBonusStartingResources>();
  313. bnode["resourceType"].String() = vstd::reverseMap(resourceTypeMap)[bonusValue.resource.getNum()];
  314. bnode["resourceAmount"].Integer() = bonusValue.amount;
  315. break;
  316. }
  317. case CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO:
  318. {
  319. const auto & bonusValue = getValue<CampaignBonusHeroesFromScenario>();
  320. bnode["playerColor"].String() = PlayerColor::encode(bonusValue.startingPlayer);
  321. bnode["scenario"].Integer() = bonusValue.scenario.getNum();
  322. break;
  323. }
  324. case CampaignBonusType::HERO:
  325. {
  326. const auto & bonusValue = getValue<CampaignBonusStartingHero>();
  327. bnode["playerColor"].String() = PlayerColor::encode(bonusValue.startingPlayer);
  328. bnode["hero"] = encodeHeroTypeID(bonusValue.hero);
  329. break;
  330. }
  331. }
  332. return bnode;
  333. }
  334. VCMI_LIB_NAMESPACE_END