BuildingManager.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * BuildingManager.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 "BuildingManager.h"
  12. #include "../../CCallback.h"
  13. #include "../../lib/mapObjects/MapObjects.h"
  14. #include "../../lib/entities/building/CBuilding.h"
  15. bool BuildingManager::tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays)
  16. {
  17. if (maxDays == 0)
  18. {
  19. logAi->warn("Request to build building %d in 0 days!", building.toEnum());
  20. return false;
  21. }
  22. if (!vstd::contains(t->getTown()->buildings, building))
  23. return false; // no such building in town
  24. if (t->hasBuilt(building)) //Already built? Shouldn't happen in general
  25. return true;
  26. const CBuilding * buildPtr = t->getTown()->buildings.at(building);
  27. auto toBuild = buildPtr->requirements.getFulfillmentCandidates([&](const BuildingID & buildID)
  28. {
  29. return t->hasBuilt(buildID);
  30. });
  31. toBuild.push_back(building);
  32. for (BuildingID buildID : toBuild)
  33. {
  34. EBuildingState canBuild = cb->canBuildStructure(t, buildID);
  35. if (canBuild == EBuildingState::HAVE_CAPITAL || canBuild == EBuildingState::FORBIDDEN || canBuild == EBuildingState::NO_WATER)
  36. return false; //we won't be able to build this
  37. }
  38. if (maxDays && toBuild.size() > maxDays)
  39. return false;
  40. //TODO: calculate if we have enough resources to build it in maxDays?
  41. for (const auto & buildID : toBuild)
  42. {
  43. const CBuilding * b = t->getTown()->buildings.at(buildID);
  44. EBuildingState canBuild = cb->canBuildStructure(t, buildID);
  45. if (canBuild == EBuildingState::ALLOWED)
  46. {
  47. PotentialBuilding pb;
  48. pb.bid = buildID;
  49. pb.price = t->getBuildingCost(buildID);
  50. immediateBuildings.push_back(pb); //these are checked again in try
  51. return true;
  52. }
  53. else if (canBuild == EBuildingState::PREREQUIRES)
  54. {
  55. // can happen when dependencies have their own missing dependencies
  56. if (tryBuildThisStructure(t, buildID, maxDays - 1))
  57. return true;
  58. }
  59. else if (canBuild == EBuildingState::MISSING_BASE)
  60. {
  61. if (tryBuildThisStructure(t, b->upgrade, maxDays - 1))
  62. return true;
  63. }
  64. else if (canBuild == EBuildingState::NO_RESOURCES)
  65. {
  66. //we may need to gather resources for those
  67. PotentialBuilding pb;
  68. pb.bid = buildID;
  69. pb.price = t->getBuildingCost(buildID);
  70. expensiveBuildings.push_back(pb); //these are checked again in try
  71. return false;
  72. }
  73. else
  74. return false;
  75. }
  76. return false;
  77. }
  78. bool BuildingManager::tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays)
  79. {
  80. for (const auto & building : buildList)
  81. {
  82. if (t->hasBuilt(building))
  83. continue;
  84. return tryBuildThisStructure(t, building, maxDays);
  85. }
  86. return false; //Can't build anything
  87. }
  88. std::optional<BuildingID> BuildingManager::canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const
  89. {
  90. for (const auto & building : buildList)
  91. {
  92. if (t->hasBuilt(building))
  93. continue;
  94. switch (cb->canBuildStructure(t, building))
  95. {
  96. case EBuildingState::ALLOWED:
  97. case EBuildingState::NO_RESOURCES: //TODO: allow this via optional parameter?
  98. return std::optional<BuildingID>(building);
  99. break;
  100. }
  101. }
  102. return std::optional<BuildingID>(); //Can't build anything
  103. }
  104. bool BuildingManager::tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays)
  105. {
  106. for (const auto & building : buildList)
  107. {
  108. if (t->hasBuilt(building))
  109. continue;
  110. return tryBuildThisStructure(t, building, maxDays);
  111. }
  112. return false; //Nothing to build
  113. }
  114. void BuildingManager::init(CPlayerSpecificInfoCallback * CB)
  115. {
  116. cb = CB;
  117. }
  118. void BuildingManager::setAI(VCAI * AI)
  119. {
  120. ai = AI;
  121. }
  122. //Set of buildings for different goals. Does not include any prerequisites.
  123. static const std::vector<BuildingID> essential = { BuildingID::TAVERN, BuildingID::TOWN_HALL };
  124. static const std::vector<BuildingID> basicGoldSource = { BuildingID::TOWN_HALL, BuildingID::CITY_HALL };
  125. static const std::vector<BuildingID> defence = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE };
  126. static const std::vector<BuildingID> capitolAndRequirements = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::CAPITOL };
  127. static const std::vector<BuildingID> unitsSource = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
  128. BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7, BuildingID::DWELL_LVL_8 };
  129. static const std::vector<BuildingID> unitsUpgrade = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP,
  130. BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP, BuildingID::DWELL_LVL_8_UP };
  131. static const std::vector<BuildingID> unitGrowth = { BuildingID::HORDE_1, BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR };
  132. static const std::vector<BuildingID> _spells = { BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3,
  133. BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5 };
  134. static const std::vector<BuildingID> extra = { BuildingID::MARKETPLACE, BuildingID::BLACKSMITH, BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2,
  135. BuildingID::SPECIAL_3, BuildingID::SPECIAL_4, BuildingID::SHIPYARD }; // all remaining buildings
  136. bool BuildingManager::getBuildingOptions(const CGTownInstance * t)
  137. {
  138. //TODO make *real* town development system
  139. //TODO: faction-specific development: use special buildings, build dwellings in better order, etc
  140. //TODO: build resource silo, defences when needed
  141. //Possible - allow "locking" on specific building (build prerequisites and then building itself)
  142. //TODO: There is some disabled building code in GatherTroops and GatherArmy - take it into account when enhancing building. For now AI works best with building only via Build goal.
  143. immediateBuildings.clear();
  144. expensiveBuildings.clear();
  145. //below algorithm focuses on economy growth at start of the game, saving money instead of build rushing is handled by Build goal
  146. //changing code blocks order will alter behavior by changing order of adding elements to immediateBuildings / expensiveBuildings
  147. // TResources currentRes = cb->getResourceAmount();
  148. // TResources currentIncome = t->dailyIncome();
  149. if(tryBuildAnyStructure(t, essential))
  150. return true;
  151. if (cb->getDate(Date::DAY_OF_WEEK) < 5) // first 4 days of week - try to focus on dwellings
  152. {
  153. if (tryBuildNextStructure(t, unitsSource, 4))
  154. return true;
  155. }
  156. if (cb->getDate(Date::DAY_OF_WEEK) > 4) // last 3 days of week - try to focus on growth by building Fort/Citadel/Castle
  157. {
  158. if (tryBuildNextStructure(t, defence, 3))
  159. return true;
  160. }
  161. if (t->hasBuilt(BuildingID::CASTLE))
  162. {
  163. if (tryBuildAnyStructure(t, unitGrowth))
  164. return true;
  165. }
  166. //try to make City Hall
  167. if (tryBuildNextStructure(t, basicGoldSource))
  168. return true;
  169. //workaround for mantis #2696 - build capitol with separate algorithm if it is available
  170. if(t->hasBuilt(BuildingID::CITY_HALL) && getMaxPossibleGoldBuilding(t) == BuildingID::CAPITOL)
  171. {
  172. if(tryBuildNextStructure(t, capitolAndRequirements))
  173. return true;
  174. }
  175. //try to upgrade dwelling
  176. for (int i = 0; i < unitsUpgrade.size(); i++)
  177. {
  178. if (t->hasBuilt(unitsSource[i]) && !t->hasBuilt(unitsUpgrade[i]) && t->hasBuilt(BuildingID::FORT))
  179. {
  180. if (tryBuildThisStructure(t, unitsUpgrade[i]))
  181. return true;
  182. }
  183. }
  184. //remaining tasks
  185. if (tryBuildNextStructure(t, _spells))
  186. return true;
  187. if (tryBuildAnyStructure(t, extra))
  188. return true;
  189. //at the end, try to get and build any extra buildings with nonstandard slots (for example HotA 3rd level dwelling)
  190. std::vector<BuildingID> extraBuildings;
  191. for (auto buildingInfo : t->getTown()->buildings)
  192. {
  193. if (buildingInfo.first.isDwelling() && BuildingID::getUpgradedFromDwelling(buildingInfo.first) > 1)
  194. extraBuildings.push_back(buildingInfo.first);
  195. }
  196. return tryBuildAnyStructure(t, extraBuildings);
  197. }
  198. BuildingID BuildingManager::getMaxPossibleGoldBuilding(const CGTownInstance * t)
  199. {
  200. if(cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
  201. return BuildingID::CAPITOL;
  202. else if(cb->canBuildStructure(t, BuildingID::CITY_HALL) != EBuildingState::FORBIDDEN)
  203. return BuildingID::CITY_HALL;
  204. else if(cb->canBuildStructure(t, BuildingID::TOWN_HALL) != EBuildingState::FORBIDDEN)
  205. return BuildingID::TOWN_HALL;
  206. else
  207. return BuildingID::VILLAGE_HALL;
  208. }
  209. std::optional<PotentialBuilding> BuildingManager::immediateBuilding() const
  210. {
  211. if (immediateBuildings.size())
  212. return std::optional<PotentialBuilding>(immediateBuildings.front()); //back? whatever
  213. else
  214. return std::optional<PotentialBuilding>();
  215. }
  216. std::optional<PotentialBuilding> BuildingManager::expensiveBuilding() const
  217. {
  218. if (expensiveBuildings.size())
  219. return std::optional<PotentialBuilding>(expensiveBuildings.front());
  220. else
  221. return std::optional<PotentialBuilding>();
  222. }