CTownHandler.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. /*
  2. * CTownHandler.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 "CTownHandler.h"
  12. #include "CTown.h"
  13. #include "CFaction.h"
  14. #include "../building/CBuilding.h"
  15. #include "../../CCreatureHandler.h"
  16. #include "../../CHeroHandler.h"
  17. #include "../../GameSettings.h"
  18. #include "../../TerrainHandler.h"
  19. #include "../../VCMI_Lib.h"
  20. #include "../../bonuses/Propagators.h"
  21. #include "../../constants/StringConstants.h"
  22. #include "../../mapObjectConstructors/AObjectTypeHandler.h"
  23. #include "../../mapObjectConstructors/CObjectClassesHandler.h"
  24. #include "../../modding/IdentifierStorage.h"
  25. #include "../../modding/ModScope.h"
  26. #include "../../spells/CSpellHandler.h"
  27. #include "../../texts/CGeneralTextHandler.h"
  28. #include "../../texts/CLegacyConfigParser.h"
  29. #include "../../json/JsonBonus.h"
  30. VCMI_LIB_NAMESPACE_BEGIN
  31. const int NAMES_PER_TOWN=16; // number of town names per faction in H3 files. Json can define any number
  32. CTownHandler::CTownHandler():
  33. randomTown(new CTown()),
  34. randomFaction(new CFaction())
  35. {
  36. randomFaction->town = randomTown;
  37. randomTown->faction = randomFaction;
  38. randomFaction->identifier = "random";
  39. randomFaction->modScope = "core";
  40. }
  41. CTownHandler::~CTownHandler()
  42. {
  43. delete randomFaction; // will also delete randomTown
  44. }
  45. JsonNode readBuilding(CLegacyConfigParser & parser)
  46. {
  47. JsonNode ret;
  48. JsonNode & cost = ret["cost"];
  49. //note: this code will try to parse mithril as well but wil always return 0 for it
  50. for(const std::string & resID : GameConstants::RESOURCE_NAMES)
  51. cost[resID].Float() = parser.readNumber();
  52. cost.Struct().erase("mithril"); // erase mithril to avoid confusing validator
  53. parser.endLine();
  54. return ret;
  55. }
  56. const TPropagatorPtr & CTownHandler::emptyPropagator()
  57. {
  58. static const TPropagatorPtr emptyProp(nullptr);
  59. return emptyProp;
  60. }
  61. std::vector<JsonNode> CTownHandler::loadLegacyData()
  62. {
  63. size_t dataSize = VLC->settings()->getInteger(EGameSettings::TEXTS_FACTION);
  64. std::vector<JsonNode> dest(dataSize);
  65. objects.resize(dataSize);
  66. auto getBuild = [&](size_t town, size_t building) -> JsonNode &
  67. {
  68. return dest[town]["town"]["buildings"][EBuildingType::names[building]];
  69. };
  70. CLegacyConfigParser parser(TextPath::builtin("DATA/BUILDING.TXT"));
  71. parser.endLine(); // header
  72. parser.endLine();
  73. //Unique buildings
  74. for (size_t town=0; town<dataSize; town++)
  75. {
  76. parser.endLine(); //header
  77. parser.endLine();
  78. int buildID = 17;
  79. do
  80. {
  81. getBuild(town, buildID) = readBuilding(parser);
  82. buildID++;
  83. }
  84. while (!parser.isNextEntryEmpty());
  85. }
  86. // Common buildings
  87. parser.endLine(); // header
  88. parser.endLine();
  89. parser.endLine();
  90. int buildID = 0;
  91. do
  92. {
  93. JsonNode building = readBuilding(parser);
  94. for (size_t town=0; town<dataSize; town++)
  95. getBuild(town, buildID) = building;
  96. buildID++;
  97. }
  98. while (!parser.isNextEntryEmpty());
  99. parser.endLine(); //header
  100. parser.endLine();
  101. //Dwellings
  102. for (size_t town=0; town<dataSize; town++)
  103. {
  104. parser.endLine(); //header
  105. parser.endLine();
  106. for (size_t i=0; i<14; i++)
  107. {
  108. getBuild(town, 30+i) = readBuilding(parser);
  109. }
  110. }
  111. {
  112. CLegacyConfigParser parser(TextPath::builtin("DATA/BLDGNEUT.TXT"));
  113. for(int building=0; building<15; building++)
  114. {
  115. std::string name = parser.readString();
  116. std::string descr = parser.readString();
  117. parser.endLine();
  118. for(int j=0; j<dataSize; j++)
  119. {
  120. getBuild(j, building)["name"].String() = name;
  121. getBuild(j, building)["description"].String() = descr;
  122. }
  123. }
  124. parser.endLine(); // silo
  125. parser.endLine(); // blacksmith //unused entries
  126. parser.endLine(); // moat
  127. //shipyard with the ship
  128. std::string name = parser.readString();
  129. std::string descr = parser.readString();
  130. parser.endLine();
  131. for(int town=0; town<dataSize; town++)
  132. {
  133. getBuild(town, 20)["name"].String() = name;
  134. getBuild(town, 20)["description"].String() = descr;
  135. }
  136. //blacksmith
  137. for(int town=0; town<dataSize; town++)
  138. {
  139. getBuild(town, 16)["name"].String() = parser.readString();
  140. getBuild(town, 16)["description"].String() = parser.readString();
  141. parser.endLine();
  142. }
  143. }
  144. {
  145. CLegacyConfigParser parser(TextPath::builtin("DATA/BLDGSPEC.TXT"));
  146. for(int town=0; town<dataSize; town++)
  147. {
  148. for(int build=0; build<9; build++)
  149. {
  150. getBuild(town, 17 + build)["name"].String() = parser.readString();
  151. getBuild(town, 17 + build)["description"].String() = parser.readString();
  152. parser.endLine();
  153. }
  154. getBuild(town, 26)["name"].String() = parser.readString(); // Grail
  155. getBuild(town, 26)["description"].String() = parser.readString();
  156. parser.endLine();
  157. getBuild(town, 15)["name"].String() = parser.readString(); // Resource silo
  158. getBuild(town, 15)["description"].String() = parser.readString();
  159. parser.endLine();
  160. }
  161. }
  162. {
  163. CLegacyConfigParser parser(TextPath::builtin("DATA/DWELLING.TXT"));
  164. for(int town=0; town<dataSize; town++)
  165. {
  166. for(int build=0; build<14; build++)
  167. {
  168. getBuild(town, 30 + build)["name"].String() = parser.readString();
  169. getBuild(town, 30 + build)["description"].String() = parser.readString();
  170. parser.endLine();
  171. }
  172. }
  173. }
  174. {
  175. CLegacyConfigParser typeParser(TextPath::builtin("DATA/TOWNTYPE.TXT"));
  176. CLegacyConfigParser nameParser(TextPath::builtin("DATA/TOWNNAME.TXT"));
  177. size_t townID=0;
  178. do
  179. {
  180. dest[townID]["name"].String() = typeParser.readString();
  181. for (int i=0; i<NAMES_PER_TOWN; i++)
  182. {
  183. JsonNode name;
  184. name.String() = nameParser.readString();
  185. dest[townID]["town"]["names"].Vector().push_back(name);
  186. nameParser.endLine();
  187. }
  188. townID++;
  189. }
  190. while (typeParser.endLine());
  191. }
  192. return dest;
  193. }
  194. void CTownHandler::loadBuildingRequirements(CBuilding * building, const JsonNode & source, std::vector<BuildingRequirementsHelper> & bidsToLoad) const
  195. {
  196. if (source.isNull())
  197. return;
  198. BuildingRequirementsHelper hlp;
  199. hlp.building = building;
  200. hlp.town = building->town;
  201. hlp.json = source;
  202. bidsToLoad.push_back(hlp);
  203. }
  204. void CTownHandler::addBonusesForVanilaBuilding(CBuilding * building) const
  205. {
  206. std::shared_ptr<Bonus> b;
  207. static const TPropagatorPtr playerPropagator = std::make_shared<CPropagatorNodeType>(CBonusSystemNode::ENodeTypes::PLAYER);
  208. if(building->bid == BuildingID::TAVERN)
  209. {
  210. b = createBonus(building, BonusType::MORALE, +1);
  211. }
  212. switch(building->subId)
  213. {
  214. case BuildingSubID::BROTHERHOOD_OF_SWORD:
  215. b = createBonus(building, BonusType::MORALE, +2);
  216. building->overrideBids.insert(BuildingID::TAVERN);
  217. break;
  218. case BuildingSubID::FOUNTAIN_OF_FORTUNE:
  219. b = createBonus(building, BonusType::LUCK, +2);
  220. break;
  221. case BuildingSubID::SPELL_POWER_GARRISON_BONUS:
  222. b = createBonus(building, BonusType::PRIMARY_SKILL, +2, BonusSubtypeID(PrimarySkill::SPELL_POWER));
  223. break;
  224. case BuildingSubID::ATTACK_GARRISON_BONUS:
  225. b = createBonus(building, BonusType::PRIMARY_SKILL, +2, BonusSubtypeID(PrimarySkill::ATTACK));
  226. break;
  227. case BuildingSubID::DEFENSE_GARRISON_BONUS:
  228. b = createBonus(building, BonusType::PRIMARY_SKILL, +2, BonusSubtypeID(PrimarySkill::DEFENSE));
  229. break;
  230. case BuildingSubID::LIGHTHOUSE:
  231. b = createBonus(building, BonusType::MOVEMENT, +500, BonusCustomSubtype::heroMovementSea, playerPropagator);
  232. break;
  233. }
  234. if(b)
  235. building->addNewBonus(b, building->buildingBonuses);
  236. }
  237. std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, BonusType type, int val) const
  238. {
  239. return createBonus(build, type, val, BonusSubtypeID(), emptyPropagator());
  240. }
  241. std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, BonusType type, int val, BonusSubtypeID subtype) const
  242. {
  243. return createBonus(build, type, val, subtype, emptyPropagator());
  244. }
  245. std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, BonusType type, int val, BonusSubtypeID subtype, const TPropagatorPtr & prop) const
  246. {
  247. auto b = std::make_shared<Bonus>(BonusDuration::PERMANENT, type, BonusSource::TOWN_STRUCTURE, val, build->getUniqueTypeID(), subtype);
  248. b->description.appendTextID(build->getNameTextID());
  249. if(prop)
  250. b->addPropagator(prop);
  251. return b;
  252. }
  253. void CTownHandler::loadSpecialBuildingBonuses(const JsonNode & source, BonusList & bonusList, CBuilding * building)
  254. {
  255. for(const auto & b : source.Vector())
  256. {
  257. auto bonus = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::NONE, BonusSource::TOWN_STRUCTURE, 0, BonusSourceID(building->getUniqueTypeID()));
  258. if(!JsonUtils::parseBonus(b, bonus.get()))
  259. continue;
  260. bonus->description.appendTextID(building->getNameTextID());
  261. //JsonUtils::parseBuildingBonus produces UNKNOWN type propagator instead of empty.
  262. if(bonus->propagator != nullptr
  263. && bonus->propagator->getPropagatorType() == CBonusSystemNode::ENodeTypes::UNKNOWN)
  264. bonus->addPropagator(emptyPropagator());
  265. building->addNewBonus(bonus, bonusList);
  266. }
  267. }
  268. void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, const JsonNode & source)
  269. {
  270. assert(stringID.find(':') == std::string::npos);
  271. assert(!source.getModScope().empty());
  272. auto * ret = new CBuilding();
  273. ret->bid = vstd::find_or(MappedKeys::BUILDING_NAMES_TO_TYPES, stringID, BuildingID::NONE);
  274. ret->subId = BuildingSubID::NONE;
  275. if(ret->bid == BuildingID::NONE && !source["id"].isNull())
  276. {
  277. // FIXME: A lot of false-positives with no clear way to handle them in mods
  278. //logMod->warn("Building %s: id field is deprecated", stringID);
  279. ret->bid = source["id"].isNull() ? BuildingID(BuildingID::NONE) : BuildingID(source["id"].Float());
  280. }
  281. if (ret->bid == BuildingID::NONE)
  282. logMod->error("Building '%s' isn't recognized and won't work properly. Correct the typo or update VCMI.", stringID);
  283. ret->mode = ret->bid == BuildingID::GRAIL
  284. ? CBuilding::BUILD_GRAIL
  285. : vstd::find_or(CBuilding::MODES, source["mode"].String(), CBuilding::BUILD_NORMAL);
  286. ret->height = vstd::find_or(CBuilding::TOWER_TYPES, source["height"].String(), CBuilding::HEIGHT_NO_TOWER);
  287. ret->identifier = stringID;
  288. ret->modScope = source.getModScope();
  289. ret->town = town;
  290. VLC->generaltexth->registerString(source.getModScope(), ret->getNameTextID(), source["name"].String());
  291. VLC->generaltexth->registerString(source.getModScope(), ret->getDescriptionTextID(), source["description"].String());
  292. ret->resources = TResources(source["cost"]);
  293. ret->produce = TResources(source["produce"]);
  294. if(ret->bid == BuildingID::TAVERN)
  295. addBonusesForVanilaBuilding(ret);
  296. else if(ret->bid.IsSpecialOrGrail())
  297. {
  298. loadSpecialBuildingBonuses(source["bonuses"], ret->buildingBonuses, ret);
  299. if(ret->buildingBonuses.empty())
  300. {
  301. ret->subId = vstd::find_or(MappedKeys::SPECIAL_BUILDINGS, source["type"].String(), BuildingSubID::NONE);
  302. addBonusesForVanilaBuilding(ret);
  303. }
  304. loadSpecialBuildingBonuses(source["onVisitBonuses"], ret->onVisitBonuses, ret);
  305. if(!ret->onVisitBonuses.empty())
  306. {
  307. if(ret->subId == BuildingSubID::NONE)
  308. ret->subId = BuildingSubID::CUSTOM_VISITING_BONUS;
  309. for(auto & bonus : ret->onVisitBonuses)
  310. bonus->sid = BonusSourceID(ret->getUniqueTypeID());
  311. }
  312. if(ret->subId == BuildingSubID::CUSTOM_VISITING_REWARD)
  313. ret->rewardableObjectInfo.init(source["configuration"], ret->getBaseTextID());
  314. }
  315. //MODS COMPATIBILITY FOR 0.96
  316. if(!ret->produce.nonZero())
  317. {
  318. switch (ret->bid.toEnum()) {
  319. break; case BuildingID::VILLAGE_HALL: ret->produce[EGameResID::GOLD] = 500;
  320. break; case BuildingID::TOWN_HALL : ret->produce[EGameResID::GOLD] = 1000;
  321. break; case BuildingID::CITY_HALL : ret->produce[EGameResID::GOLD] = 2000;
  322. break; case BuildingID::CAPITOL : ret->produce[EGameResID::GOLD] = 4000;
  323. break; case BuildingID::GRAIL : ret->produce[EGameResID::GOLD] = 5000;
  324. break; case BuildingID::RESOURCE_SILO :
  325. {
  326. switch (ret->town->primaryRes.toEnum())
  327. {
  328. case EGameResID::GOLD:
  329. ret->produce[ret->town->primaryRes] = 500;
  330. break;
  331. case EGameResID::WOOD_AND_ORE:
  332. ret->produce[EGameResID::WOOD] = 1;
  333. ret->produce[EGameResID::ORE] = 1;
  334. break;
  335. default:
  336. ret->produce[ret->town->primaryRes] = 1;
  337. break;
  338. }
  339. }
  340. }
  341. }
  342. loadBuildingRequirements(ret, source["requires"], requirementsToLoad);
  343. if(ret->bid.IsSpecialOrGrail())
  344. loadBuildingRequirements(ret, source["overrides"], overriddenBidsToLoad);
  345. if (!source["upgrades"].isNull())
  346. {
  347. // building id and upgrades can't be the same
  348. if(stringID == source["upgrades"].String())
  349. {
  350. throw std::runtime_error(boost::str(boost::format("Building with ID '%s' of town '%s' can't be an upgrade of the same building.") %
  351. stringID % ret->town->faction->getNameTranslated()));
  352. }
  353. VLC->identifiers()->requestIdentifier(ret->town->getBuildingScope(), source["upgrades"], [=](si32 identifier)
  354. {
  355. ret->upgrade = BuildingID(identifier);
  356. });
  357. }
  358. else
  359. ret->upgrade = BuildingID::NONE;
  360. ret->town->buildings[ret->bid] = ret;
  361. registerObject(source.getModScope(), ret->town->getBuildingScope(), ret->identifier, ret->bid.getNum());
  362. }
  363. void CTownHandler::loadBuildings(CTown * town, const JsonNode & source)
  364. {
  365. if(source.isStruct())
  366. {
  367. for(const auto & node : source.Struct())
  368. {
  369. if (!node.second.isNull())
  370. loadBuilding(town, node.first, node.second);
  371. }
  372. }
  373. }
  374. void CTownHandler::loadStructure(CTown &town, const std::string & stringID, const JsonNode & source) const
  375. {
  376. auto * ret = new CStructure();
  377. ret->building = nullptr;
  378. ret->buildable = nullptr;
  379. VLC->identifiers()->tryRequestIdentifier( source.getModScope(), "building." + town.faction->getJsonKey(), stringID, [=, &town](si32 identifier) mutable
  380. {
  381. ret->building = town.buildings[BuildingID(identifier)];
  382. });
  383. if (source["builds"].isNull())
  384. {
  385. VLC->identifiers()->tryRequestIdentifier( source.getModScope(), "building." + town.faction->getJsonKey(), stringID, [=, &town](si32 identifier) mutable
  386. {
  387. ret->building = town.buildings[BuildingID(identifier)];
  388. });
  389. }
  390. else
  391. {
  392. VLC->identifiers()->requestIdentifier("building." + town.faction->getJsonKey(), source["builds"], [=, &town](si32 identifier) mutable
  393. {
  394. ret->buildable = town.buildings[BuildingID(identifier)];
  395. });
  396. }
  397. ret->identifier = stringID;
  398. ret->pos.x = static_cast<si32>(source["x"].Float());
  399. ret->pos.y = static_cast<si32>(source["y"].Float());
  400. ret->pos.z = static_cast<si32>(source["z"].Float());
  401. ret->hiddenUpgrade = source["hidden"].Bool();
  402. ret->defName = AnimationPath::fromJson(source["animation"]);
  403. ret->borderName = ImagePath::fromJson(source["border"]);
  404. ret->areaName = ImagePath::fromJson(source["area"]);
  405. town.clientInfo.structures.emplace_back(ret);
  406. }
  407. void CTownHandler::loadStructures(CTown &town, const JsonNode & source) const
  408. {
  409. for(const auto & node : source.Struct())
  410. {
  411. if (!node.second.isNull())
  412. loadStructure(town, node.first, node.second);
  413. }
  414. }
  415. void CTownHandler::loadTownHall(CTown &town, const JsonNode & source) const
  416. {
  417. auto & dstSlots = town.clientInfo.hallSlots;
  418. const auto & srcSlots = source.Vector();
  419. dstSlots.resize(srcSlots.size());
  420. for(size_t i=0; i<dstSlots.size(); i++)
  421. {
  422. auto & dstRow = dstSlots[i];
  423. const auto & srcRow = srcSlots[i].Vector();
  424. dstRow.resize(srcRow.size());
  425. for(size_t j=0; j < dstRow.size(); j++)
  426. {
  427. auto & dstBox = dstRow[j];
  428. const auto & srcBox = srcRow[j].Vector();
  429. dstBox.resize(srcBox.size());
  430. for(size_t k=0; k<dstBox.size(); k++)
  431. {
  432. auto & dst = dstBox[k];
  433. const auto & src = srcBox[k];
  434. VLC->identifiers()->requestIdentifier("building." + town.faction->getJsonKey(), src, [&](si32 identifier)
  435. {
  436. dst = BuildingID(identifier);
  437. });
  438. }
  439. }
  440. }
  441. }
  442. Point JsonToPoint(const JsonNode & node)
  443. {
  444. if(!node.isStruct())
  445. return Point::makeInvalid();
  446. Point ret;
  447. ret.x = static_cast<si32>(node["x"].Float());
  448. ret.y = static_cast<si32>(node["y"].Float());
  449. return ret;
  450. }
  451. void CTownHandler::loadSiegeScreen(CTown &town, const JsonNode & source) const
  452. {
  453. town.clientInfo.siegePrefix = source["imagePrefix"].String();
  454. town.clientInfo.towerIconSmall = source["towerIconSmall"].String();
  455. town.clientInfo.towerIconLarge = source["towerIconLarge"].String();
  456. VLC->identifiers()->requestIdentifier("creature", source["shooter"], [&town](si32 creature)
  457. {
  458. auto crId = CreatureID(creature);
  459. if((*VLC->creh)[crId]->animation.missileFrameAngles.empty())
  460. logMod->error("Mod '%s' error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Siege will not work properly!"
  461. , town.faction->getNameTranslated()
  462. , (*VLC->creh)[crId]->getNameSingularTranslated());
  463. town.clientInfo.siegeShooter = crId;
  464. });
  465. auto & pos = town.clientInfo.siegePositions;
  466. pos.resize(21);
  467. pos[8] = JsonToPoint(source["towers"]["top"]["tower"]);
  468. pos[17] = JsonToPoint(source["towers"]["top"]["battlement"]);
  469. pos[20] = JsonToPoint(source["towers"]["top"]["creature"]);
  470. pos[2] = JsonToPoint(source["towers"]["keep"]["tower"]);
  471. pos[15] = JsonToPoint(source["towers"]["keep"]["battlement"]);
  472. pos[18] = JsonToPoint(source["towers"]["keep"]["creature"]);
  473. pos[3] = JsonToPoint(source["towers"]["bottom"]["tower"]);
  474. pos[16] = JsonToPoint(source["towers"]["bottom"]["battlement"]);
  475. pos[19] = JsonToPoint(source["towers"]["bottom"]["creature"]);
  476. pos[9] = JsonToPoint(source["gate"]["gate"]);
  477. pos[10] = JsonToPoint(source["gate"]["arch"]);
  478. pos[7] = JsonToPoint(source["walls"]["upper"]);
  479. pos[6] = JsonToPoint(source["walls"]["upperMid"]);
  480. pos[5] = JsonToPoint(source["walls"]["bottomMid"]);
  481. pos[4] = JsonToPoint(source["walls"]["bottom"]);
  482. pos[13] = JsonToPoint(source["moat"]["moat"]);
  483. pos[14] = JsonToPoint(source["moat"]["bank"]);
  484. pos[11] = JsonToPoint(source["static"]["bottom"]);
  485. pos[12] = JsonToPoint(source["static"]["top"]);
  486. pos[1] = JsonToPoint(source["static"]["background"]);
  487. }
  488. static void readIcon(JsonNode source, std::string & small, std::string & large)
  489. {
  490. if (source.getType() == JsonNode::JsonType::DATA_STRUCT) // don't crash on old format
  491. {
  492. small = source["small"].String();
  493. large = source["large"].String();
  494. }
  495. }
  496. void CTownHandler::loadClientData(CTown &town, const JsonNode & source) const
  497. {
  498. CTown::ClientInfo & info = town.clientInfo;
  499. readIcon(source["icons"]["village"]["normal"], info.iconSmall[0][0], info.iconLarge[0][0]);
  500. readIcon(source["icons"]["village"]["built"], info.iconSmall[0][1], info.iconLarge[0][1]);
  501. readIcon(source["icons"]["fort"]["normal"], info.iconSmall[1][0], info.iconLarge[1][0]);
  502. readIcon(source["icons"]["fort"]["built"], info.iconSmall[1][1], info.iconLarge[1][1]);
  503. if (source["musicTheme"].isVector())
  504. {
  505. for (auto const & entry : source["musicTheme"].Vector())
  506. info.musicTheme.push_back(AudioPath::fromJson(entry));
  507. }
  508. else
  509. {
  510. info.musicTheme.push_back(AudioPath::fromJson(source["musicTheme"]));
  511. }
  512. info.hallBackground = ImagePath::fromJson(source["hallBackground"]);
  513. info.townBackground = ImagePath::fromJson(source["townBackground"]);
  514. info.guildWindow = ImagePath::fromJson(source["guildWindow"]);
  515. info.buildingsIcons = AnimationPath::fromJson(source["buildingsIcons"]);
  516. info.guildBackground = ImagePath::fromJson(source["guildBackground"]);
  517. info.tavernVideo = VideoPath::fromJson(source["tavernVideo"]);
  518. loadTownHall(town, source["hallSlots"]);
  519. loadStructures(town, source["structures"]);
  520. loadSiegeScreen(town, source["siege"]);
  521. }
  522. void CTownHandler::loadTown(CTown * town, const JsonNode & source)
  523. {
  524. const auto * resIter = boost::find(GameConstants::RESOURCE_NAMES, source["primaryResource"].String());
  525. if(resIter == std::end(GameConstants::RESOURCE_NAMES))
  526. town->primaryRes = GameResID(EGameResID::WOOD_AND_ORE); //Wood + Ore
  527. else
  528. town->primaryRes = GameResID(resIter - std::begin(GameConstants::RESOURCE_NAMES));
  529. warMachinesToLoad[town] = source["warMachine"];
  530. town->mageLevel = static_cast<ui32>(source["mageGuild"].Float());
  531. town->namesCount = 0;
  532. for(const auto & name : source["names"].Vector())
  533. {
  534. VLC->generaltexth->registerString(town->faction->modScope, town->getRandomNameTextID(town->namesCount), name.String());
  535. town->namesCount += 1;
  536. }
  537. if (!source["moatAbility"].isNull()) // VCMI 1.2 compatibility code
  538. {
  539. VLC->identifiers()->requestIdentifier( "spell", source["moatAbility"], [=](si32 ability)
  540. {
  541. town->moatAbility = SpellID(ability);
  542. });
  543. }
  544. else
  545. {
  546. VLC->identifiers()->requestIdentifier( source.getModScope(), "spell", "castleMoat", [=](si32 ability)
  547. {
  548. town->moatAbility = SpellID(ability);
  549. });
  550. }
  551. // Horde building creature level
  552. for(const JsonNode &node : source["horde"].Vector())
  553. town->hordeLvl[static_cast<int>(town->hordeLvl.size())] = static_cast<int>(node.Float());
  554. // town needs to have exactly 2 horde entries. Validation will take care of 2+ entries
  555. // but anything below 2 must be handled here
  556. for (size_t i=source["horde"].Vector().size(); i<2; i++)
  557. town->hordeLvl[static_cast<int>(i)] = -1;
  558. const JsonVector & creatures = source["creatures"].Vector();
  559. town->creatures.resize(creatures.size());
  560. for (size_t i=0; i< creatures.size(); i++)
  561. {
  562. const JsonVector & level = creatures[i].Vector();
  563. town->creatures[i].resize(level.size());
  564. for (size_t j=0; j<level.size(); j++)
  565. {
  566. VLC->identifiers()->requestIdentifier("creature", level[j], [=](si32 creature)
  567. {
  568. town->creatures[i][j] = CreatureID(creature);
  569. });
  570. }
  571. }
  572. town->defaultTavernChance = static_cast<ui32>(source["defaultTavern"].Float());
  573. /// set chance of specific hero class to appear in this town
  574. for(const auto & node : source["tavern"].Struct())
  575. {
  576. int chance = static_cast<int>(node.second.Float());
  577. VLC->identifiers()->requestIdentifier(node.second.getModScope(), "heroClass",node.first, [=](si32 classID)
  578. {
  579. VLC->heroclassesh->objects[classID]->selectionProbability[town->faction->getId()] = chance;
  580. });
  581. }
  582. for(const auto & node : source["guildSpells"].Struct())
  583. {
  584. int chance = static_cast<int>(node.second.Float());
  585. VLC->identifiers()->requestIdentifier(node.second.getModScope(), "spell", node.first, [=](si32 spellID)
  586. {
  587. VLC->spellh->objects.at(spellID)->probabilities[town->faction->getId()] = chance;
  588. });
  589. }
  590. for(const JsonNode & d : source["adventureMap"]["dwellings"].Vector())
  591. {
  592. town->dwellings.push_back(d["graphics"].String());
  593. town->dwellingNames.push_back(d["name"].String());
  594. }
  595. loadBuildings(town, source["buildings"]);
  596. loadClientData(*town, source);
  597. }
  598. void CTownHandler::loadPuzzle(CFaction &faction, const JsonNode &source) const
  599. {
  600. faction.puzzleMap.reserve(GameConstants::PUZZLE_MAP_PIECES);
  601. std::string prefix = source["prefix"].String();
  602. for(const JsonNode &piece : source["pieces"].Vector())
  603. {
  604. size_t index = faction.puzzleMap.size();
  605. SPuzzleInfo spi;
  606. spi.position.x = static_cast<si16>(piece["x"].Float());
  607. spi.position.y = static_cast<si16>(piece["y"].Float());
  608. spi.whenUncovered = static_cast<ui16>(piece["index"].Float());
  609. spi.number = static_cast<ui16>(index);
  610. // filename calculation
  611. std::ostringstream suffix;
  612. suffix << std::setfill('0') << std::setw(2) << index;
  613. spi.filename = ImagePath::builtinTODO(prefix + suffix.str());
  614. faction.puzzleMap.push_back(spi);
  615. }
  616. assert(faction.puzzleMap.size() == GameConstants::PUZZLE_MAP_PIECES);
  617. }
  618. std::shared_ptr<CFaction> CTownHandler::loadFromJson(const std::string & scope, const JsonNode & source, const std::string & identifier, size_t index)
  619. {
  620. assert(identifier.find(':') == std::string::npos);
  621. auto faction = std::make_shared<CFaction>();
  622. faction->index = static_cast<FactionID>(index);
  623. faction->modScope = scope;
  624. faction->identifier = identifier;
  625. VLC->generaltexth->registerString(scope, faction->getNameTextID(), source["name"].String());
  626. VLC->generaltexth->registerString(scope, faction->getDescriptionTranslated(), source["description"].String());
  627. faction->creatureBg120 = ImagePath::fromJson(source["creatureBackground"]["120px"]);
  628. faction->creatureBg130 = ImagePath::fromJson(source["creatureBackground"]["130px"]);
  629. faction->boatType = BoatId::CASTLE; //Do not crash
  630. if (!source["boat"].isNull())
  631. {
  632. VLC->identifiers()->requestIdentifier("core:boat", source["boat"], [=](int32_t boatTypeID)
  633. {
  634. faction->boatType = BoatId(boatTypeID);
  635. });
  636. }
  637. int alignment = vstd::find_pos(GameConstants::ALIGNMENT_NAMES, source["alignment"].String());
  638. if (alignment == -1)
  639. faction->alignment = EAlignment::NEUTRAL;
  640. else
  641. faction->alignment = static_cast<EAlignment>(alignment);
  642. auto preferUndergound = source["preferUndergroundPlacement"];
  643. faction->preferUndergroundPlacement = preferUndergound.isNull() ? false : preferUndergound.Bool();
  644. faction->special = source["special"].Bool();
  645. // NOTE: semi-workaround - normally, towns are supposed to have native terrains.
  646. // Towns without one are exceptions. So, vcmi requires nativeTerrain to be defined
  647. // But allows it to be defined with explicit value of "none" if town should not have native terrain
  648. // This is better than allowing such terrain-less towns silently, leading to issues with RMG
  649. faction->nativeTerrain = ETerrainId::NONE;
  650. if ( !source["nativeTerrain"].isNull() && source["nativeTerrain"].String() != "none")
  651. {
  652. VLC->identifiers()->requestIdentifier("terrain", source["nativeTerrain"], [=](int32_t index){
  653. faction->nativeTerrain = TerrainId(index);
  654. auto const & terrain = VLC->terrainTypeHandler->getById(faction->nativeTerrain);
  655. if (!terrain->isSurface() && !terrain->isUnderground())
  656. logMod->warn("Faction %s has terrain %s as native, but terrain is not suitable for either surface or subterranean layers!", faction->getJsonKey(), terrain->getJsonKey());
  657. });
  658. }
  659. if (!source["town"].isNull())
  660. {
  661. faction->town = new CTown();
  662. faction->town->faction = faction.get();
  663. loadTown(faction->town, source["town"]);
  664. }
  665. else
  666. faction->town = nullptr;
  667. if (!source["puzzleMap"].isNull())
  668. loadPuzzle(*faction, source["puzzleMap"]);
  669. return faction;
  670. }
  671. void CTownHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
  672. {
  673. auto object = loadFromJson(scope, data, name, objects.size());
  674. objects.emplace_back(object);
  675. if (object->town)
  676. {
  677. auto & info = object->town->clientInfo;
  678. info.icons[0][0] = 8 + object->index.getNum() * 4 + 0;
  679. info.icons[0][1] = 8 + object->index.getNum() * 4 + 1;
  680. info.icons[1][0] = 8 + object->index.getNum() * 4 + 2;
  681. info.icons[1][1] = 8 + object->index.getNum() * 4 + 3;
  682. VLC->identifiers()->requestIdentifier(scope, "object", "town", [=](si32 index)
  683. {
  684. // register town once objects are loaded
  685. JsonNode config = data["town"]["mapObject"];
  686. config["faction"].String() = name;
  687. config["faction"].setModScope(scope, false);
  688. if (config.getModScope().empty())// MODS COMPATIBILITY FOR 0.96
  689. config.setModScope(scope, false);
  690. VLC->objtypeh->loadSubObject(object->identifier, config, index, object->index);
  691. // MODS COMPATIBILITY FOR 0.96
  692. const auto & advMap = data["town"]["adventureMap"];
  693. if (!advMap.isNull())
  694. {
  695. logMod->warn("Outdated town mod. Will try to generate valid templates out of fort");
  696. JsonNode config;
  697. config["animation"] = advMap["castle"];
  698. VLC->objtypeh->getHandlerFor(index, object->index)->addTemplate(config);
  699. }
  700. });
  701. }
  702. registerObject(scope, "faction", name, object->index.getNum());
  703. }
  704. void CTownHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
  705. {
  706. auto object = loadFromJson(scope, data, name, index);
  707. if (objects.size() > index)
  708. assert(objects[index] == nullptr); // ensure that this id was not loaded before
  709. else
  710. objects.resize(index + 1);
  711. objects[index] = object;
  712. if (object->town)
  713. {
  714. auto & info = object->town->clientInfo;
  715. info.icons[0][0] = (GameConstants::F_NUMBER + object->index.getNum()) * 2 + 0;
  716. info.icons[0][1] = (GameConstants::F_NUMBER + object->index.getNum()) * 2 + 1;
  717. info.icons[1][0] = object->index.getNum() * 2 + 0;
  718. info.icons[1][1] = object->index.getNum() * 2 + 1;
  719. VLC->identifiers()->requestIdentifier(scope, "object", "town", [=](si32 index)
  720. {
  721. // register town once objects are loaded
  722. JsonNode config = data["town"]["mapObject"];
  723. config["faction"].String() = name;
  724. config["faction"].setModScope(scope, false);
  725. VLC->objtypeh->loadSubObject(object->identifier, config, index, object->index);
  726. });
  727. }
  728. registerObject(scope, "faction", name, object->index.getNum());
  729. }
  730. void CTownHandler::loadRandomFaction()
  731. {
  732. JsonNode randomFactionJson(JsonPath::builtin("config/factions/random.json"));
  733. randomFactionJson.setModScope(ModScope::scopeBuiltin(), true);
  734. loadBuildings(randomTown, randomFactionJson["random"]["town"]["buildings"]);
  735. }
  736. void CTownHandler::loadCustom()
  737. {
  738. loadRandomFaction();
  739. }
  740. void CTownHandler::afterLoadFinalization()
  741. {
  742. initializeRequirements();
  743. initializeOverridden();
  744. initializeWarMachines();
  745. }
  746. void CTownHandler::initializeRequirements()
  747. {
  748. // must be done separately after all ID's are known
  749. for (auto & requirement : requirementsToLoad)
  750. {
  751. requirement.building->requirements = CBuilding::TRequired(requirement.json, [&](const JsonNode & node) -> BuildingID
  752. {
  753. if (node.Vector().size() > 1)
  754. {
  755. logMod->error("Unexpected length of town buildings requirements: %d", node.Vector().size());
  756. logMod->error("Entry contains: ");
  757. logMod->error(node.toString());
  758. }
  759. auto index = VLC->identifiers()->getIdentifier(requirement.town->getBuildingScope(), node[0]);
  760. if (!index.has_value())
  761. {
  762. logMod->error("Unknown building in town buildings: %s", node[0].String());
  763. return BuildingID::NONE;
  764. }
  765. return BuildingID(index.value());
  766. });
  767. }
  768. requirementsToLoad.clear();
  769. }
  770. void CTownHandler::initializeOverridden()
  771. {
  772. for(auto & bidHelper : overriddenBidsToLoad)
  773. {
  774. auto jsonNode = bidHelper.json;
  775. auto scope = bidHelper.town->getBuildingScope();
  776. for(const auto & b : jsonNode.Vector())
  777. {
  778. auto bid = BuildingID(VLC->identifiers()->getIdentifier(scope, b).value());
  779. bidHelper.building->overrideBids.insert(bid);
  780. }
  781. }
  782. overriddenBidsToLoad.clear();
  783. }
  784. void CTownHandler::initializeWarMachines()
  785. {
  786. // must be done separately after all objects are loaded
  787. for(auto & p : warMachinesToLoad)
  788. {
  789. CTown * t = p.first;
  790. JsonNode creatureKey = p.second;
  791. auto ret = VLC->identifiers()->getIdentifier("creature", creatureKey, false);
  792. if(ret)
  793. {
  794. const CCreature * creature = CreatureID(*ret).toCreature();
  795. t->warMachine = creature->warMachine;
  796. }
  797. }
  798. warMachinesToLoad.clear();
  799. }
  800. std::set<FactionID> CTownHandler::getDefaultAllowed() const
  801. {
  802. std::set<FactionID> allowedFactions;
  803. for(const auto & town : objects)
  804. if (town->town != nullptr && !town->special)
  805. allowedFactions.insert(town->getId());
  806. return allowedFactions;
  807. }
  808. std::set<FactionID> CTownHandler::getAllowedFactions(bool withTown) const
  809. {
  810. if (withTown)
  811. return getDefaultAllowed();
  812. std::set<FactionID> result;
  813. for(const auto & town : objects)
  814. result.insert(town->getId());
  815. return result;
  816. }
  817. const std::vector<std::string> & CTownHandler::getTypeNames() const
  818. {
  819. static const std::vector<std::string> typeNames = { "faction", "town" };
  820. return typeNames;
  821. }
  822. VCMI_LIB_NAMESPACE_END