CGTownInstance.cpp 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234
  1. /*
  2. * CGTownInstance.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 "CGTownInstance.h"
  12. #include "CGTownBuilding.h"
  13. #include "CObjectClassesHandler.h"
  14. #include "../spells/CSpellHandler.h"
  15. #include "../battle/IBattleInfoCallback.h"
  16. #include "../NetPacks.h"
  17. #include "../CConfigHandler.h"
  18. #include "../CGeneralTextHandler.h"
  19. #include "../CModHandler.h"
  20. #include "../IGameCallback.h"
  21. #include "../CGameState.h"
  22. #include "../mapping/CMap.h"
  23. #include "../CPlayerState.h"
  24. #include "../TerrainHandler.h"
  25. #include "../serializer/JsonSerializeFormat.h"
  26. #include "../HeroBonus.h"
  27. VCMI_LIB_NAMESPACE_BEGIN
  28. std::vector<const CArtifact *> CGTownInstance::merchantArtifacts;
  29. std::vector<int> CGTownInstance::universitySkills;
  30. int CGTownInstance::getSightRadius() const //returns sight distance
  31. {
  32. auto ret = CBuilding::HEIGHT_NO_TOWER;
  33. for(const auto & bid : builtBuildings)
  34. {
  35. if(bid.IsSpecialOrGrail())
  36. {
  37. auto height = town->buildings.at(bid)->height;
  38. if(ret < height)
  39. ret = height;
  40. }
  41. }
  42. return ret;
  43. }
  44. void CGTownInstance::setPropertyDer(ui8 what, ui32 val)
  45. {
  46. ///this is freakin' overcomplicated solution
  47. switch (what)
  48. {
  49. case ObjProperty::STRUCTURE_ADD_VISITING_HERO:
  50. bonusingBuildings[val]->setProperty (ObjProperty::VISITORS, visitingHero->id.getNum());
  51. break;
  52. case ObjProperty::STRUCTURE_CLEAR_VISITORS:
  53. bonusingBuildings[val]->setProperty (ObjProperty::STRUCTURE_CLEAR_VISITORS, 0);
  54. break;
  55. case ObjProperty::STRUCTURE_ADD_GARRISONED_HERO: //add garrisoned hero to visitors
  56. bonusingBuildings[val]->setProperty (ObjProperty::VISITORS, garrisonHero->id.getNum());
  57. break;
  58. case ObjProperty::BONUS_VALUE_FIRST:
  59. bonusValue.first = val;
  60. break;
  61. case ObjProperty::BONUS_VALUE_SECOND:
  62. bonusValue.second = val;
  63. break;
  64. }
  65. }
  66. CGTownInstance::EFortLevel CGTownInstance::fortLevel() const //0 - none, 1 - fort, 2 - citadel, 3 - castle
  67. {
  68. if (hasBuilt(BuildingID::CASTLE))
  69. return CASTLE;
  70. if (hasBuilt(BuildingID::CITADEL))
  71. return CITADEL;
  72. if (hasBuilt(BuildingID::FORT))
  73. return FORT;
  74. return NONE;
  75. }
  76. int CGTownInstance::hallLevel() const // -1 - none, 0 - village, 1 - town, 2 - city, 3 - capitol
  77. {
  78. if (hasBuilt(BuildingID::CAPITOL))
  79. return 3;
  80. if (hasBuilt(BuildingID::CITY_HALL))
  81. return 2;
  82. if (hasBuilt(BuildingID::TOWN_HALL))
  83. return 1;
  84. if (hasBuilt(BuildingID::VILLAGE_HALL))
  85. return 0;
  86. return -1;
  87. }
  88. int CGTownInstance::mageGuildLevel() const
  89. {
  90. if (hasBuilt(BuildingID::MAGES_GUILD_5))
  91. return 5;
  92. if (hasBuilt(BuildingID::MAGES_GUILD_4))
  93. return 4;
  94. if (hasBuilt(BuildingID::MAGES_GUILD_3))
  95. return 3;
  96. if (hasBuilt(BuildingID::MAGES_GUILD_2))
  97. return 2;
  98. if (hasBuilt(BuildingID::MAGES_GUILD_1))
  99. return 1;
  100. return 0;
  101. }
  102. int CGTownInstance::getHordeLevel(const int & HID) const//HID - 0 or 1; returns creature level or -1 if that horde structure is not present
  103. {
  104. return town->hordeLvl.at(HID);
  105. }
  106. int CGTownInstance::creatureGrowth(const int & level) const
  107. {
  108. return getGrowthInfo(level).totalGrowth();
  109. }
  110. GrowthInfo CGTownInstance::getGrowthInfo(int level) const
  111. {
  112. GrowthInfo ret;
  113. if (level<0 || level >=GameConstants::CREATURES_PER_TOWN)
  114. return ret;
  115. if (creatures[level].second.empty())
  116. return ret; //no dwelling
  117. const CCreature *creature = VLC->creh->objects[creatures[level].second.back()];
  118. const int base = creature->getGrowth();
  119. int castleBonus = 0;
  120. ret.entries.emplace_back(VLC->generaltexth->allTexts[590], base); // \n\nBasic growth %d"
  121. if (hasBuilt(BuildingID::CASTLE))
  122. ret.entries.emplace_back(subID, BuildingID::CASTLE, castleBonus = base);
  123. else if (hasBuilt(BuildingID::CITADEL))
  124. ret.entries.emplace_back(subID, BuildingID::CITADEL, castleBonus = base / 2);
  125. if(town->hordeLvl.at(0) == level)//horde 1
  126. if(hasBuilt(BuildingID::HORDE_1))
  127. ret.entries.emplace_back(subID, BuildingID::HORDE_1, creature->getHorde());
  128. if(town->hordeLvl.at(1) == level)//horde 2
  129. if(hasBuilt(BuildingID::HORDE_2))
  130. ret.entries.emplace_back(subID, BuildingID::HORDE_2, creature->getHorde());
  131. //statue-of-legion-like bonus: % to base+castle
  132. TConstBonusListPtr bonuses2 = getBonuses(Selector::type()(Bonus::CREATURE_GROWTH_PERCENT));
  133. for(const auto & b : *bonuses2)
  134. {
  135. const auto growth = b->val * (base + castleBonus) / 100;
  136. ret.entries.emplace_back(growth, b->Description(growth));
  137. }
  138. //other *-of-legion-like bonuses (%d to growth cumulative with grail)
  139. TConstBonusListPtr bonuses = getBonuses(Selector::type()(Bonus::CREATURE_GROWTH).And(Selector::subtype()(level)));
  140. for(const auto & b : *bonuses)
  141. ret.entries.emplace_back(b->val, b->Description());
  142. int dwellingBonus = 0;
  143. if(const PlayerState *p = cb->getPlayerState(tempOwner, false))
  144. {
  145. dwellingBonus = getDwellingBonus(creatures[level].second, p->dwellings);
  146. }
  147. if(dwellingBonus)
  148. ret.entries.emplace_back(VLC->generaltexth->allTexts[591], dwellingBonus); // \nExternal dwellings %+d
  149. if(hasBuilt(BuildingID::GRAIL)) //grail - +50% to ALL (so far added) growth
  150. ret.entries.emplace_back(subID, BuildingID::GRAIL, ret.totalGrowth() / 2);
  151. return ret;
  152. }
  153. int CGTownInstance::getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<ConstTransitivePtr<CGDwelling> >& dwellings) const
  154. {
  155. int totalBonus = 0;
  156. for (const auto& dwelling : dwellings)
  157. {
  158. for (const auto& creature : dwelling->creatures)
  159. {
  160. totalBonus += vstd::contains(creatureIds, creature.second[0]) ? 1 : 0;
  161. }
  162. }
  163. return totalBonus;
  164. }
  165. TResources CGTownInstance::dailyIncome() const
  166. {
  167. TResources ret;
  168. for(const auto & p : town->buildings)
  169. {
  170. BuildingID buildingUpgrade;
  171. for(const auto & p2 : town->buildings)
  172. {
  173. if (p2.second->upgrade == p.first)
  174. {
  175. buildingUpgrade = p2.first;
  176. }
  177. }
  178. if (!hasBuilt(buildingUpgrade)&&(hasBuilt(p.first)))
  179. {
  180. ret += p.second->produce;
  181. }
  182. }
  183. return ret;
  184. }
  185. bool CGTownInstance::hasFort() const
  186. {
  187. return hasBuilt(BuildingID::FORT);
  188. }
  189. bool CGTownInstance::hasCapitol() const
  190. {
  191. return hasBuilt(BuildingID::CAPITOL);
  192. }
  193. CGTownInstance::CGTownInstance():
  194. IShipyard(this),
  195. IMarket(),
  196. town(nullptr),
  197. builded(0),
  198. destroyed(0),
  199. identifier(0),
  200. alignmentToPlayer(PlayerColor::NEUTRAL)
  201. {
  202. this->setNodeType(CBonusSystemNode::TOWN);
  203. }
  204. CGTownInstance::~CGTownInstance()
  205. {
  206. for (auto & elem : bonusingBuildings)
  207. delete elem;
  208. }
  209. int CGTownInstance::spellsAtLevel(int level, bool checkGuild) const
  210. {
  211. if(checkGuild && mageGuildLevel() < level)
  212. return 0;
  213. int ret = 6 - level; //how many spells are available at this level
  214. if (hasBuilt(BuildingSubID::LIBRARY))
  215. ret++;
  216. return ret;
  217. }
  218. bool CGTownInstance::needsLastStack() const
  219. {
  220. return garrisonHero != nullptr;
  221. }
  222. void CGTownInstance::setOwner(const PlayerColor & player) const
  223. {
  224. removeCapitols(player);
  225. cb->setOwner(this, player);
  226. }
  227. void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
  228. {
  229. if(!cb->gameState()->getPlayerRelations( getOwner(), h->getOwner() ))//if this is enemy
  230. {
  231. if(armedGarrison() || visitingHero)
  232. {
  233. const CGHeroInstance * defendingHero = visitingHero ? visitingHero : garrisonHero;
  234. const CArmedInstance * defendingArmy = defendingHero ? (CArmedInstance *)defendingHero : this;
  235. const bool isBattleOutside = isBattleOutsideTown(defendingHero);
  236. if(!isBattleOutside && visitingHero && defendingHero == visitingHero)
  237. {
  238. //we have two approaches to merge armies: mergeGarrisonOnSiege() and used in the CGameHandler::garrisonSwap(ObjectInstanceID tid)
  239. auto * nodeSiege = defendingHero->whereShouldBeAttachedOnSiege(isBattleOutside);
  240. if(nodeSiege == (CBonusSystemNode *)this)
  241. cb->swapGarrisonOnSiege(this->id);
  242. const_cast<CGHeroInstance *>(defendingHero)->inTownGarrison = false; //hack to return visitor from garrison after battle
  243. }
  244. cb->startBattlePrimary(h, defendingArmy, getSightCenter(), h, defendingHero, false, (isBattleOutside ? nullptr : this));
  245. }
  246. else
  247. {
  248. auto heroColor = h->getOwner();
  249. onTownCaptured(heroColor);
  250. if(cb->gameState()->getPlayerStatus(heroColor) == EPlayerStatus::WINNER)
  251. {
  252. return; //we just won game, we do not need to perform any extra actions
  253. //TODO: check how does H3 behave, visiting town on victory can affect campaigns (spells learned, +1 stat building visited)
  254. }
  255. cb->heroVisitCastle(this, h);
  256. }
  257. }
  258. else if(h->visitablePos() == visitablePos())
  259. {
  260. bool commander_recover = h->commander && !h->commander->alive;
  261. if (commander_recover) // rise commander from dead
  262. {
  263. SetCommanderProperty scp;
  264. scp.heroid = h->id;
  265. scp.which = SetCommanderProperty::ALIVE;
  266. scp.amount = 1;
  267. cb->sendAndApply(&scp);
  268. }
  269. cb->heroVisitCastle(this, h);
  270. // TODO(vmarkovtsev): implement payment for rising the commander
  271. if (commander_recover) // info window about commander
  272. {
  273. InfoWindow iw;
  274. iw.player = h->tempOwner;
  275. iw.text << h->commander->getName();
  276. iw.components.emplace_back(*h->commander);
  277. cb->showInfoDialog(&iw);
  278. }
  279. }
  280. else
  281. {
  282. logGlobal->error("%s visits allied town of %s from different pos?", h->getNameTranslated(), name);
  283. }
  284. }
  285. void CGTownInstance::onHeroLeave(const CGHeroInstance * h) const
  286. {
  287. //FIXME: find out why this issue appears on random maps
  288. if(visitingHero == h)
  289. {
  290. cb->stopHeroVisitCastle(this, h);
  291. logGlobal->trace("%s correctly left town %s", h->getNameTranslated(), name);
  292. }
  293. else
  294. logGlobal->warn("Warning, %s tries to leave the town %s but hero is not inside.", h->getNameTranslated(), name);
  295. }
  296. std::string CGTownInstance::getObjectName() const
  297. {
  298. return name + ", " + town->faction->getNameTranslated();
  299. }
  300. bool CGTownInstance::townEnvisagesBuilding(BuildingSubID::EBuildingSubID subId) const
  301. {
  302. return town->getBuildingType(subId) != BuildingID::NONE;
  303. }
  304. void CGTownInstance::initOverriddenBids()
  305. {
  306. for(const auto & bid : builtBuildings)
  307. {
  308. const auto & overrideThem = town->buildings.at(bid)->overrideBids;
  309. for(const auto & overrideIt : overrideThem)
  310. overriddenBuildings.insert(overrideIt);
  311. }
  312. }
  313. bool CGTownInstance::isBonusingBuildingAdded(BuildingID::EBuildingID bid) const
  314. {
  315. auto present = std::find_if(bonusingBuildings.begin(), bonusingBuildings.end(), [&](CGTownBuilding* building)
  316. {
  317. return building->getBuildingType().num == bid;
  318. });
  319. return present != bonusingBuildings.end();
  320. }
  321. void CGTownInstance::addTownBonuses(CRandomGenerator & rand)
  322. {
  323. for(const auto & kvp : town->buildings)
  324. {
  325. if(vstd::contains(overriddenBuildings, kvp.first))
  326. continue;
  327. if(kvp.second->IsVisitingBonus())
  328. bonusingBuildings.push_back(new CTownBonus(kvp.second->bid, kvp.second->subId, this));
  329. if(kvp.second->IsWeekBonus())
  330. bonusingBuildings.push_back(new COPWBonus(kvp.second->bid, kvp.second->subId, this));
  331. if(kvp.second->subId == BuildingSubID::CONFIGURABLE_REWARD)
  332. {
  333. auto * newBuilding = new CTownRewardableBuilding(kvp.second->bid, kvp.second->subId, this);
  334. kvp.second->rewardableObjectInfo.configureObject(newBuilding->configuration, rand);
  335. bonusingBuildings.push_back(newBuilding);
  336. }
  337. }
  338. }
  339. DamageRange CGTownInstance::getTowerDamageRange() const
  340. {
  341. assert(hasBuilt(BuildingID::CASTLE));
  342. // http://heroes.thelazy.net/wiki/Arrow_tower
  343. // base damage, irregardless of town level
  344. static constexpr int baseDamage = 6;
  345. // extra damage, for each building in town
  346. static constexpr int extraDamage = 1;
  347. const int minDamage = baseDamage + extraDamage * getTownLevel();
  348. return {
  349. minDamage,
  350. minDamage * 2
  351. };
  352. }
  353. DamageRange CGTownInstance::getKeepDamageRange() const
  354. {
  355. assert(hasBuilt(BuildingID::CITADEL));
  356. // http://heroes.thelazy.net/wiki/Arrow_tower
  357. // base damage, irregardless of town level
  358. static constexpr int baseDamage = 10;
  359. // extra damage, for each building in town
  360. static constexpr int extraDamage = 2;
  361. const int minDamage = baseDamage + extraDamage * getTownLevel();
  362. return {
  363. minDamage,
  364. minDamage * 2
  365. };
  366. }
  367. void CGTownInstance::deleteTownBonus(BuildingID::EBuildingID bid)
  368. {
  369. size_t i = 0;
  370. CGTownBuilding * freeIt = nullptr;
  371. for(i = 0; i != bonusingBuildings.size(); i++)
  372. {
  373. if(bonusingBuildings[i]->getBuildingType() == bid)
  374. {
  375. freeIt = bonusingBuildings[i];
  376. break;
  377. }
  378. }
  379. if(freeIt == nullptr)
  380. return;
  381. auto building = town->buildings.at(bid);
  382. auto isVisitingBonus = building->IsVisitingBonus();
  383. auto isWeekBonus = building->IsWeekBonus();
  384. if(!isVisitingBonus && !isWeekBonus)
  385. return;
  386. bonusingBuildings.erase(bonusingBuildings.begin() + i);
  387. delete freeIt;
  388. }
  389. void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structures
  390. {
  391. blockVisit = true;
  392. if(townEnvisagesBuilding(BuildingSubID::PORTAL_OF_SUMMONING)) //Dungeon for example
  393. creatures.resize(GameConstants::CREATURES_PER_TOWN + 1);
  394. else
  395. creatures.resize(GameConstants::CREATURES_PER_TOWN);
  396. for (int level = 0; level < GameConstants::CREATURES_PER_TOWN; level++)
  397. {
  398. BuildingID buildID = BuildingID(BuildingID::DWELL_FIRST).advance(level);
  399. int upgradeNum = 0;
  400. for (; town->buildings.count(buildID); upgradeNum++, buildID.advance(GameConstants::CREATURES_PER_TOWN))
  401. {
  402. if (hasBuilt(buildID) && town->creatures.at(level).size() > upgradeNum)
  403. creatures[level].second.push_back(town->creatures[level][upgradeNum]);
  404. }
  405. }
  406. initOverriddenBids();
  407. addTownBonuses(rand); //add special bonuses from buildings to the bonusingBuildings vector.
  408. recreateBuildingsBonuses();
  409. updateAppearance();
  410. }
  411. void CGTownInstance::newTurn(CRandomGenerator & rand) const
  412. {
  413. if (cb->getDate(Date::DAY_OF_WEEK) == 1) //reset on new week
  414. {
  415. //give resources if there's a Mystic Pond
  416. if (hasBuilt(BuildingSubID::MYSTIC_POND)
  417. && cb->getDate(Date::DAY) != 1
  418. && (tempOwner < PlayerColor::PLAYER_LIMIT)
  419. )
  420. {
  421. int resID = rand.nextInt(2, 5); //bonus to random rare resource
  422. resID = (resID==2)?1:resID;
  423. int resVal = rand.nextInt(1, 4);//with size 1..4
  424. cb->giveResource(tempOwner, static_cast<EGameResID>(resID), resVal);
  425. cb->setObjProperty (id, ObjProperty::BONUS_VALUE_FIRST, resID);
  426. cb->setObjProperty (id, ObjProperty::BONUS_VALUE_SECOND, resVal);
  427. }
  428. const auto * manaVortex = getBonusingBuilding(BuildingSubID::MANA_VORTEX);
  429. if (manaVortex != nullptr)
  430. cb->setObjProperty(id, ObjProperty::STRUCTURE_CLEAR_VISITORS, manaVortex->indexOnTV); //reset visitors for Mana Vortex
  431. //get Mana Vortex or Stables bonuses
  432. //same code is in the CGameHandler::buildStructure method
  433. if (visitingHero != nullptr)
  434. cb->visitCastleObjects(this, visitingHero);
  435. if (garrisonHero != nullptr)
  436. cb->visitCastleObjects(this, garrisonHero);
  437. if (tempOwner == PlayerColor::NEUTRAL) //garrison growth for neutral towns
  438. {
  439. std::vector<SlotID> nativeCrits; //slots
  440. for(const auto & elem : Slots())
  441. {
  442. if (elem.second->type->getFaction() == subID) //native
  443. {
  444. nativeCrits.push_back(elem.first); //collect matching slots
  445. }
  446. }
  447. if(!nativeCrits.empty())
  448. {
  449. SlotID pos = *RandomGeneratorUtil::nextItem(nativeCrits, rand);
  450. StackLocation sl(this, pos);
  451. const CCreature *c = getCreature(pos);
  452. if (rand.nextInt(99) < 90 || c->upgrades.empty()) //increase number if no upgrade available
  453. {
  454. cb->changeStackCount(sl, c->getGrowth());
  455. }
  456. else //upgrade
  457. {
  458. cb->changeStackType(sl, VLC->creh->objects[*c->upgrades.begin()]);
  459. }
  460. }
  461. if ((stacksCount() < GameConstants::ARMY_SIZE && rand.nextInt(99) < 25) || Slots().empty()) //add new stack
  462. {
  463. int i = rand.nextInt(std::min(GameConstants::CREATURES_PER_TOWN, cb->getDate(Date::MONTH) << 1) - 1);
  464. if (!town->creatures[i].empty())
  465. {
  466. CreatureID c = town->creatures[i][0];
  467. SlotID n;
  468. TQuantity count = creatureGrowth(i);
  469. if (!count) // no dwelling
  470. count = VLC->creh->objects[c]->getGrowth();
  471. {//no lower tiers or above current month
  472. if ((n = getSlotFor(c)).validSlot())
  473. {
  474. StackLocation sl(this, n);
  475. if (slotEmpty(n))
  476. cb->insertNewStack(sl, VLC->creh->objects[c], count);
  477. else //add to existing
  478. cb->changeStackCount(sl, count);
  479. }
  480. }
  481. }
  482. }
  483. }
  484. }
  485. }
  486. /*
  487. int3 CGTownInstance::getSightCenter() const
  488. {
  489. return pos - int3(2,0,0);
  490. }
  491. */
  492. bool CGTownInstance::passableFor(PlayerColor color) const
  493. {
  494. if (!armedGarrison())//empty castle - anyone can visit
  495. return true;
  496. if ( tempOwner == PlayerColor::NEUTRAL )//neutral guarded - no one can visit
  497. return false;
  498. return cb->getPlayerRelations(tempOwner, color) != PlayerRelations::ENEMIES;
  499. }
  500. void CGTownInstance::getOutOffsets( std::vector<int3> &offsets ) const
  501. {
  502. offsets = {int3(-1,2,0), int3(-3,2,0)};
  503. }
  504. void CGTownInstance::mergeGarrisonOnSiege() const
  505. {
  506. auto getWeakestStackSlot = [&](ui64 powerLimit)
  507. {
  508. std::vector<SlotID> weakSlots;
  509. auto stacksList = visitingHero->stacks;
  510. std::pair<SlotID, CStackInstance *> pair;
  511. while(!stacksList.empty())
  512. {
  513. pair = *vstd::minElementByFun(stacksList, [&](const std::pair<SlotID, CStackInstance *> & elem) { return elem.second->getPower(); });
  514. if(powerLimit > pair.second->getPower() &&
  515. (weakSlots.empty() || pair.second->getPower() == visitingHero->getStack(weakSlots.front()).getPower()))
  516. {
  517. weakSlots.push_back(pair.first);
  518. stacksList.erase(pair.first);
  519. }
  520. else
  521. break;
  522. }
  523. if(!weakSlots.empty())
  524. return *std::max_element(weakSlots.begin(), weakSlots.end());
  525. return SlotID();
  526. };
  527. auto count = static_cast<int>(stacks.size());
  528. for(int i = 0; i < count; i++)
  529. {
  530. auto pair = *vstd::maxElementByFun(stacks, [&](const std::pair<SlotID, CStackInstance *> & elem)
  531. {
  532. ui64 power = elem.second->getPower();
  533. auto dst = visitingHero->getSlotFor(elem.second->getCreatureID());
  534. if(dst.validSlot() && visitingHero->hasStackAtSlot(dst))
  535. power += visitingHero->getStack(dst).getPower();
  536. return power;
  537. });
  538. auto dst = visitingHero->getSlotFor(pair.second->getCreatureID());
  539. if(dst.validSlot())
  540. cb->moveStack(StackLocation(this, pair.first), StackLocation(visitingHero, dst), -1);
  541. else
  542. {
  543. dst = getWeakestStackSlot(static_cast<int>(pair.second->getPower()));
  544. if(dst.validSlot())
  545. cb->swapStacks(StackLocation(this, pair.first), StackLocation(visitingHero, dst));
  546. }
  547. }
  548. }
  549. void CGTownInstance::removeCapitols(const PlayerColor & owner) const
  550. {
  551. if (hasCapitol()) // search if there's an older capitol
  552. {
  553. PlayerState* state = cb->gameState()->getPlayerState(owner); //get all towns owned by player
  554. for (auto i = state->towns.cbegin(); i < state->towns.cend(); ++i)
  555. {
  556. if (*i != this && (*i)->hasCapitol())
  557. {
  558. RazeStructures rs;
  559. rs.tid = id;
  560. rs.bid.insert(BuildingID::CAPITOL);
  561. rs.destroyed = destroyed;
  562. cb->sendAndApply(&rs);
  563. return;
  564. }
  565. }
  566. }
  567. }
  568. void CGTownInstance::clearArmy() const
  569. {
  570. while(!stacks.empty())
  571. {
  572. cb->eraseStack(StackLocation(this, stacks.begin()->first));
  573. }
  574. }
  575. BoatId CGTownInstance::getBoatType() const
  576. {
  577. switch (town->faction->alignment)
  578. {
  579. case EAlignment::EVIL : return EBoatId::BOAT_EVIL;
  580. case EAlignment::GOOD : return EBoatId::BOAT_GOOD;
  581. case EAlignment::NEUTRAL : return EBoatId::BOAT_NEUTRAL;
  582. default: return EBoatId::NONE;
  583. }
  584. }
  585. int CGTownInstance::getMarketEfficiency() const
  586. {
  587. if(!hasBuiltSomeTradeBuilding())
  588. return 0;
  589. const PlayerState *p = cb->getPlayerState(tempOwner);
  590. assert(p);
  591. int marketCount = 0;
  592. for(const CGTownInstance *t : p->towns)
  593. if(t->hasBuiltSomeTradeBuilding())
  594. marketCount++;
  595. return marketCount;
  596. }
  597. bool CGTownInstance::allowsTrade(EMarketMode::EMarketMode mode) const
  598. {
  599. switch(mode)
  600. {
  601. case EMarketMode::RESOURCE_RESOURCE:
  602. case EMarketMode::RESOURCE_PLAYER:
  603. return hasBuilt(BuildingID::MARKETPLACE);
  604. case EMarketMode::ARTIFACT_RESOURCE:
  605. case EMarketMode::RESOURCE_ARTIFACT:
  606. return hasBuilt(BuildingSubID::ARTIFACT_MERCHANT);
  607. case EMarketMode::CREATURE_RESOURCE:
  608. return hasBuilt(BuildingSubID::FREELANCERS_GUILD);
  609. case EMarketMode::CREATURE_UNDEAD:
  610. return hasBuilt(BuildingSubID::CREATURE_TRANSFORMER);
  611. case EMarketMode::RESOURCE_SKILL:
  612. return hasBuilt(BuildingSubID::MAGIC_UNIVERSITY);
  613. default:
  614. assert(0);
  615. return false;
  616. }
  617. }
  618. std::vector<int> CGTownInstance::availableItemsIds(EMarketMode::EMarketMode mode) const
  619. {
  620. if(mode == EMarketMode::RESOURCE_ARTIFACT)
  621. {
  622. std::vector<int> ret;
  623. for(const CArtifact *a : merchantArtifacts)
  624. if(a)
  625. ret.push_back(a->getId());
  626. else
  627. ret.push_back(-1);
  628. return ret;
  629. }
  630. else if ( mode == EMarketMode::RESOURCE_SKILL )
  631. {
  632. return universitySkills;
  633. }
  634. else
  635. return IMarket::availableItemsIds(mode);
  636. }
  637. void CGTownInstance::setType(si32 ID, si32 subID)
  638. {
  639. assert(ID == Obj::TOWN); // just in case
  640. CGObjectInstance::setType(ID, subID);
  641. town = (*VLC->townh)[subID]->town;
  642. randomizeArmy(subID);
  643. updateAppearance();
  644. }
  645. void CGTownInstance::updateAppearance()
  646. {
  647. auto terrain = cb->gameState()->getTile(visitablePos())->terType->getId();
  648. //FIXME: not the best way to do this
  649. auto app = VLC->objtypeh->getHandlerFor(ID, subID)->getOverride(terrain, this);
  650. if (app)
  651. appearance = app;
  652. }
  653. std::string CGTownInstance::nodeName() const
  654. {
  655. return "Town (" + (town ? town->faction->getNameTranslated() : "unknown") + ") of " + name;
  656. }
  657. void CGTownInstance::deserializationFix()
  658. {
  659. attachTo(townAndVis);
  660. //Hero is already handled by CGameState::attachArmedObjects
  661. // if(visitingHero)
  662. // visitingHero->attachTo(&townAndVis);
  663. // if(garrisonHero)
  664. // garrisonHero->attachTo(this);
  665. }
  666. void CGTownInstance::updateMoraleBonusFromArmy()
  667. {
  668. auto b = getExportedBonusList().getFirst(Selector::sourceType()(Bonus::ARMY).And(Selector::type()(Bonus::MORALE)));
  669. if(!b)
  670. {
  671. b = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, 0, -1);
  672. addNewBonus(b);
  673. }
  674. if (garrisonHero)
  675. {
  676. b->val = 0;
  677. CBonusSystemNode::treeHasChanged();
  678. }
  679. else
  680. CArmedInstance::updateMoraleBonusFromArmy();
  681. }
  682. void CGTownInstance::recreateBuildingsBonuses()
  683. {
  684. BonusList bl;
  685. getExportedBonusList().getBonuses(bl, Selector::sourceType()(Bonus::TOWN_STRUCTURE));
  686. for(const auto & b : bl)
  687. removeBonus(b);
  688. for(const auto & bid : builtBuildings)
  689. {
  690. if(vstd::contains(overriddenBuildings, bid)) //tricky! -> checks tavern only if no bratherhood of sword
  691. continue;
  692. auto building = town->buildings.at(bid);
  693. if(building->buildingBonuses.empty())
  694. continue;
  695. for(auto & bonus : building->buildingBonuses)
  696. addNewBonus(bonus);
  697. }
  698. }
  699. void CGTownInstance::setVisitingHero(CGHeroInstance *h)
  700. {
  701. assert(!!visitingHero == !h);
  702. if(h)
  703. {
  704. PlayerState *p = cb->gameState()->getPlayerState(h->tempOwner);
  705. assert(p);
  706. h->detachFrom(*p);
  707. h->attachTo(townAndVis);
  708. visitingHero = h;
  709. h->visitedTown = this;
  710. h->inTownGarrison = false;
  711. }
  712. else
  713. {
  714. PlayerState *p = cb->gameState()->getPlayerState(visitingHero->tempOwner);
  715. visitingHero->visitedTown = nullptr;
  716. visitingHero->detachFrom(townAndVis);
  717. visitingHero->attachTo(*p);
  718. visitingHero = nullptr;
  719. }
  720. }
  721. void CGTownInstance::setGarrisonedHero(CGHeroInstance *h)
  722. {
  723. assert(!!garrisonHero == !h);
  724. if(h)
  725. {
  726. PlayerState *p = cb->gameState()->getPlayerState(h->tempOwner);
  727. assert(p);
  728. h->detachFrom(*p);
  729. h->attachTo(*this);
  730. garrisonHero = h;
  731. h->visitedTown = this;
  732. h->inTownGarrison = true;
  733. }
  734. else
  735. {
  736. PlayerState *p = cb->gameState()->getPlayerState(garrisonHero->tempOwner);
  737. garrisonHero->visitedTown = nullptr;
  738. garrisonHero->inTownGarrison = false;
  739. garrisonHero->detachFrom(*this);
  740. garrisonHero->attachTo(*p);
  741. garrisonHero = nullptr;
  742. }
  743. updateMoraleBonusFromArmy(); //avoid giving morale bonus for same army twice
  744. }
  745. bool CGTownInstance::armedGarrison() const
  746. {
  747. return !stacks.empty() || garrisonHero;
  748. }
  749. const CTown * CGTownInstance::getTown() const
  750. {
  751. if(ID == Obj::RANDOM_TOWN)
  752. return VLC->townh->randomTown;
  753. else
  754. {
  755. if(nullptr == town)
  756. {
  757. return (*VLC->townh)[subID]->town;
  758. }
  759. else
  760. return town;
  761. }
  762. }
  763. int CGTownInstance::getTownLevel() const
  764. {
  765. // count all buildings that are not upgrades
  766. int level = 0;
  767. for(const auto & bid : builtBuildings)
  768. {
  769. if(town->buildings.at(bid)->upgrade == BuildingID::NONE)
  770. level++;
  771. }
  772. return level;
  773. }
  774. CBonusSystemNode & CGTownInstance::whatShouldBeAttached()
  775. {
  776. return townAndVis;
  777. }
  778. std::string CGTownInstance::getNameTranslated() const
  779. {
  780. return name;
  781. }
  782. void CGTownInstance::setNameTranslated( const std::string & newName )
  783. {
  784. name = newName;
  785. }
  786. const CArmedInstance * CGTownInstance::getUpperArmy() const
  787. {
  788. if(garrisonHero)
  789. return garrisonHero;
  790. return this;
  791. }
  792. const CGTownBuilding * CGTownInstance::getBonusingBuilding(BuildingSubID::EBuildingSubID subId) const
  793. {
  794. for(auto * const building : bonusingBuildings)
  795. {
  796. if(building->getBuildingSubtype() == subId)
  797. return building;
  798. }
  799. return nullptr;
  800. }
  801. bool CGTownInstance::hasBuiltSomeTradeBuilding() const
  802. {
  803. for(const auto & bid : builtBuildings)
  804. {
  805. if(town->buildings.at(bid)->IsTradeBuilding())
  806. return true;
  807. }
  808. return false;
  809. }
  810. bool CGTownInstance::hasBuilt(BuildingSubID::EBuildingSubID buildingID) const
  811. {
  812. for(const auto & bid : builtBuildings)
  813. {
  814. if(town->buildings.at(bid)->subId == buildingID)
  815. return true;
  816. }
  817. return false;
  818. }
  819. bool CGTownInstance::hasBuilt(const BuildingID & buildingID) const
  820. {
  821. return vstd::contains(builtBuildings, buildingID);
  822. }
  823. bool CGTownInstance::hasBuilt(const BuildingID & buildingID, int townID) const
  824. {
  825. if (townID == town->faction->getIndex() || townID == ETownType::ANY)
  826. return hasBuilt(buildingID);
  827. return false;
  828. }
  829. TResources CGTownInstance::getBuildingCost(const BuildingID & buildingID) const
  830. {
  831. if (vstd::contains(town->buildings, buildingID))
  832. return town->buildings.at(buildingID)->resources;
  833. else
  834. {
  835. logGlobal->error("Town %s at %s has no possible building %d!", name, pos.toString(), buildingID.toEnum());
  836. return TResources();
  837. }
  838. }
  839. CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID & buildID, bool deep) const
  840. {
  841. const CBuilding * building = town->buildings.at(buildID);
  842. //TODO: find better solution to prevent infinite loops
  843. std::set<BuildingID> processed;
  844. std::function<CBuilding::TRequired::Variant(const BuildingID &)> dependTest =
  845. [&](const BuildingID & id) -> CBuilding::TRequired::Variant
  846. {
  847. const CBuilding * build = town->buildings.at(id);
  848. CBuilding::TRequired::OperatorAll requirements;
  849. if (!hasBuilt(id))
  850. {
  851. if (deep)
  852. requirements.expressions.emplace_back(id);
  853. else
  854. return id;
  855. }
  856. if(!vstd::contains(processed, id))
  857. {
  858. processed.insert(id);
  859. if (build->upgrade != BuildingID::NONE)
  860. requirements.expressions.push_back(dependTest(build->upgrade));
  861. requirements.expressions.push_back(build->requirements.morph(dependTest));
  862. }
  863. return requirements;
  864. };
  865. CBuilding::TRequired::OperatorAll requirements;
  866. if (building->upgrade != BuildingID::NONE)
  867. {
  868. const CBuilding * upgr = town->buildings.at(building->upgrade);
  869. requirements.expressions.push_back(dependTest(upgr->bid));
  870. processed.clear();
  871. }
  872. requirements.expressions.push_back(building->requirements.morph(dependTest));
  873. CBuilding::TRequired::Variant variant(requirements);
  874. CBuilding::TRequired ret(variant);
  875. ret.minimize();
  876. return ret;
  877. }
  878. void CGTownInstance::addHeroToStructureVisitors(const CGHeroInstance *h, si64 structureInstanceID ) const
  879. {
  880. if(visitingHero == h)
  881. cb->setObjProperty(id, ObjProperty::STRUCTURE_ADD_VISITING_HERO, structureInstanceID); //add to visitors
  882. else if(garrisonHero == h)
  883. cb->setObjProperty(id, ObjProperty::STRUCTURE_ADD_GARRISONED_HERO, structureInstanceID); //then it must be garrisoned hero
  884. else
  885. {
  886. //should never ever happen
  887. logGlobal->error("Cannot add hero %s to visitors of structure # %d", h->getNameTranslated(), structureInstanceID);
  888. throw std::runtime_error("internal error");
  889. }
  890. }
  891. void CGTownInstance::battleFinished(const CGHeroInstance * hero, const BattleResult & result) const
  892. {
  893. if(result.winner == BattleSide::ATTACKER)
  894. {
  895. clearArmy();
  896. onTownCaptured(hero->getOwner());
  897. }
  898. }
  899. void CGTownInstance::onTownCaptured(const PlayerColor & winner) const
  900. {
  901. setOwner(winner);
  902. FoWChange fw;
  903. fw.player = winner;
  904. fw.mode = 1;
  905. cb->getTilesInRange(fw.tiles, getSightCenter(), getSightRadius(), winner, 1);
  906. cb->sendAndApply(& fw);
  907. }
  908. void CGTownInstance::afterAddToMap(CMap * map)
  909. {
  910. if(ID == Obj::TOWN)
  911. map->towns.emplace_back(this);
  912. }
  913. void CGTownInstance::afterRemoveFromMap(CMap * map)
  914. {
  915. if (ID == Obj::TOWN)
  916. vstd::erase_if_present(map->towns, this);
  917. }
  918. void CGTownInstance::reset()
  919. {
  920. CGTownInstance::merchantArtifacts.clear();
  921. CGTownInstance::universitySkills.clear();
  922. }
  923. void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
  924. {
  925. static const std::vector<std::string> FORMATIONS = { "wide", "tight" };
  926. CGObjectInstance::serializeJsonOwner(handler);
  927. CCreatureSet::serializeJson(handler, "army", 7);
  928. handler.serializeEnum("tightFormation", formation, FORMATIONS);
  929. handler.serializeString("name", name);
  930. {
  931. auto decodeBuilding = [this](const std::string & identifier) -> si32
  932. {
  933. auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), getTown()->getBuildingScope(), identifier);
  934. if(rawId)
  935. return rawId.value();
  936. else
  937. return -1;
  938. };
  939. auto encodeBuilding = [this](si32 index) -> std::string
  940. {
  941. return getTown()->buildings.at(BuildingID(index))->getJsonKey();
  942. };
  943. const std::set<si32> standard = getTown()->getAllBuildings();//by default all buildings are allowed
  944. JsonSerializeFormat::LICSet buildingsLIC(standard, decodeBuilding, encodeBuilding);
  945. if(handler.saving)
  946. {
  947. bool customBuildings = false;
  948. boost::logic::tribool hasFort(false);
  949. for(const BuildingID & id : forbiddenBuildings)
  950. {
  951. buildingsLIC.none.insert(id);
  952. customBuildings = true;
  953. }
  954. for(const BuildingID & id : builtBuildings)
  955. {
  956. if(id == BuildingID::DEFAULT)
  957. continue;
  958. const CBuilding * building = getTown()->buildings.at(id);
  959. if(building->mode == CBuilding::BUILD_AUTO)
  960. continue;
  961. if(id == BuildingID::FORT)
  962. hasFort = true;
  963. buildingsLIC.all.insert(id);
  964. customBuildings = true;
  965. }
  966. if(customBuildings)
  967. handler.serializeLIC("buildings", buildingsLIC);
  968. else
  969. handler.serializeBool("hasFort",hasFort);
  970. }
  971. else
  972. {
  973. handler.serializeLIC("buildings", buildingsLIC);
  974. builtBuildings.insert(BuildingID::VILLAGE_HALL);
  975. if(buildingsLIC.none.empty() && buildingsLIC.all.empty())
  976. {
  977. builtBuildings.insert(BuildingID::DEFAULT);
  978. bool hasFort = false;
  979. handler.serializeBool("hasFort",hasFort);
  980. if(hasFort)
  981. builtBuildings.insert(BuildingID::FORT);
  982. }
  983. else
  984. {
  985. for(const si32 item : buildingsLIC.none)
  986. forbiddenBuildings.insert(BuildingID(item));
  987. for(const si32 item : buildingsLIC.all)
  988. builtBuildings.insert(BuildingID(item));
  989. }
  990. }
  991. }
  992. {
  993. std::vector<bool> standard = VLC->spellh->getDefaultAllowed();
  994. JsonSerializeFormat::LIC spellsLIC(standard, SpellID::decode, SpellID::encode);
  995. if(handler.saving)
  996. {
  997. for(const SpellID & id : possibleSpells)
  998. spellsLIC.any[id.num] = true;
  999. for(const SpellID & id : obligatorySpells)
  1000. spellsLIC.all[id.num] = true;
  1001. }
  1002. handler.serializeLIC("spells", spellsLIC);
  1003. if(!handler.saving)
  1004. {
  1005. possibleSpells.clear();
  1006. for(si32 idx = 0; idx < spellsLIC.any.size(); idx++)
  1007. {
  1008. if(spellsLIC.any[idx])
  1009. possibleSpells.emplace_back(idx);
  1010. }
  1011. obligatorySpells.clear();
  1012. for(si32 idx = 0; idx < spellsLIC.all.size(); idx++)
  1013. {
  1014. if(spellsLIC.all[idx])
  1015. obligatorySpells.emplace_back(idx);
  1016. }
  1017. }
  1018. }
  1019. }
  1020. FactionID CGTownInstance::getFaction() const
  1021. {
  1022. return town->faction->getId();
  1023. }
  1024. TerrainId CGTownInstance::getNativeTerrain() const
  1025. {
  1026. return town->faction->getNativeTerrain();
  1027. }
  1028. GrowthInfo::Entry::Entry(const std::string &format, int _count)
  1029. : count(_count)
  1030. {
  1031. description = boost::str(boost::format(format) % count);
  1032. }
  1033. GrowthInfo::Entry::Entry(int subID, const BuildingID & building, int _count): count(_count)
  1034. {
  1035. description = boost::str(boost::format("%s %+d") % (*VLC->townh)[subID]->town->buildings.at(building)->getNameTranslated() % count);
  1036. }
  1037. GrowthInfo::Entry::Entry(int _count, std::string fullDescription):
  1038. count(_count),
  1039. description(std::move(fullDescription))
  1040. {
  1041. }
  1042. CTownAndVisitingHero::CTownAndVisitingHero()
  1043. {
  1044. setNodeType(TOWN_AND_VISITOR);
  1045. }
  1046. int GrowthInfo::totalGrowth() const
  1047. {
  1048. int ret = 0;
  1049. for(const Entry &entry : entries)
  1050. ret += entry.count;
  1051. return ret;
  1052. }
  1053. VCMI_LIB_NAMESPACE_END