GatherArmy.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. * GatherArmy.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 "Goals.h"
  12. #include "../VCAI.h"
  13. #include "../AIUtility.h"
  14. #include "../AIhelper.h"
  15. #include "../FuzzyHelper.h"
  16. #include "../ResourceManager.h"
  17. #include "../BuildingManager.h"
  18. #include "../../../lib/mapObjects/CGTownInstance.h"
  19. #include "../../../lib/constants/StringConstants.h"
  20. using namespace Goals;
  21. bool GatherArmy::operator==(const GatherArmy & other) const
  22. {
  23. return other.hero.h == hero.h || town == other.town;
  24. }
  25. std::string GatherArmy::completeMessage() const
  26. {
  27. return "Hero " + hero.get()->getNameTranslated() + " gathered army of value " + std::to_string(value);
  28. }
  29. TSubgoal GatherArmy::whatToDoToAchieve()
  30. {
  31. //TODO: find hero if none set
  32. assert(hero.h);
  33. return fh->chooseSolution(getAllPossibleSubgoals()); //find dwelling. use current hero to prevent him from doing nothing.
  34. }
  35. TGoalVec GatherArmy::getAllPossibleSubgoals()
  36. {
  37. //get all possible towns, heroes and dwellings we may use
  38. TGoalVec ret;
  39. if(!hero.validAndSet())
  40. {
  41. return ret;
  42. }
  43. //TODO: include evaluation of monsters gather in calculation
  44. for(auto t : cb->getTownsInfo())
  45. {
  46. auto waysToVisit = ai->ah->howToVisitObj(hero, t);
  47. if(waysToVisit.size())
  48. {
  49. //grab army from town
  50. if(!t->getVisitingHero() && ai->ah->howManyReinforcementsCanGet(hero.get(), t))
  51. {
  52. if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
  53. vstd::concatenate(ret, waysToVisit);
  54. }
  55. //buy army in town
  56. if (!t->getVisitingHero() || t->getVisitingHero() == hero.get(true))
  57. {
  58. std::vector<int> values = {
  59. value,
  60. (int)ai->ah->howManyReinforcementsCanBuy(t->getUpperArmy(), t),
  61. (int)ai->ah->howManyReinforcementsCanBuy(hero.get(), t) };
  62. int val = *std::min_element(values.begin(), values.end());
  63. if (val)
  64. {
  65. auto goal = sptr(BuyArmy(t, val).sethero(hero));
  66. if(!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
  67. ret.push_back(goal);
  68. else
  69. logAi->debug("Can not buy army, because of ai->ah->containsObjective");
  70. }
  71. }
  72. //build dwelling
  73. //TODO: plan building over multiple turns?
  74. //auto bid = ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + std::size(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
  75. //Do not use below code for now, rely on generic Build. Code below needs to know a lot of town/resource context to do more good than harm
  76. /*auto bid = ai->ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + std::size(unitsSource)), 1);
  77. if (bid.has_value())
  78. {
  79. auto goal = sptr(BuildThis(bid.get(), t).setpriority(priority));
  80. if (!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
  81. ret.push_back(goal);
  82. else
  83. logAi->debug("Can not build a structure, because of ai->ah->containsObjective");
  84. }*/
  85. }
  86. }
  87. auto otherHeroes = cb->getHeroesInfo();
  88. auto heroDummy = hero;
  89. vstd::erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h)
  90. {
  91. if(h == heroDummy.h)
  92. return true;
  93. else if(!ai->isAccessibleForHero(heroDummy->visitablePos(), h, true))
  94. return true;
  95. else if(!ai->ah->canGetArmy(heroDummy.h, h)) //TODO: return actual aiValue
  96. return true;
  97. else if(ai->getGoal(h)->goalType == GATHER_ARMY)
  98. return true;
  99. else
  100. return false;
  101. });
  102. for(auto h : otherHeroes)
  103. {
  104. // Go to the other hero if we are faster
  105. if(!vstd::contains(ai->visitedHeroes[hero], h))
  106. {
  107. vstd::concatenate(ret, ai->ah->howToVisitObj(hero, h, false));
  108. }
  109. // Go to the other hero if we are faster
  110. if(!vstd::contains(ai->visitedHeroes[h], hero))
  111. {
  112. vstd::concatenate(ret, ai->ah->howToVisitObj(h, hero.get(), false));
  113. }
  114. }
  115. std::vector<const CGObjectInstance *> objs;
  116. for(auto obj : ai->visitableObjs)
  117. {
  118. if(obj->ID == Obj::CREATURE_GENERATOR1)
  119. {
  120. auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID);
  121. //Use flagged dwellings only when there are available creatures that we can afford
  122. if(relationToOwner == PlayerRelations::SAME_PLAYER)
  123. {
  124. auto dwelling = dynamic_cast<const CGDwelling *>(obj);
  125. ui32 val = std::min((ui32)value, (ui32)ai->ah->howManyReinforcementsCanBuy(hero.get(), dwelling));
  126. if(val)
  127. {
  128. for(auto & creLevel : dwelling->creatures)
  129. {
  130. if(creLevel.first)
  131. {
  132. for(auto & creatureID : creLevel.second)
  133. {
  134. if(ai->ah->freeResources().canAfford(creatureID.toCreature()->getFullRecruitCost()))
  135. objs.push_back(obj); //TODO: reserve resources?
  136. }
  137. }
  138. }
  139. }
  140. }
  141. }
  142. }
  143. for(auto h : cb->getHeroesInfo())
  144. {
  145. for(auto obj : objs)
  146. {
  147. //find safe dwelling
  148. if(ai->isGoodForVisit(obj, h))
  149. {
  150. vstd::concatenate(ret, ai->ah->howToVisitObj(h, obj));
  151. }
  152. }
  153. }
  154. if(ai->canRecruitAnyHero() && ai->ah->freeGold() > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game
  155. {
  156. if(auto t = ai->findTownWithTavern())
  157. {
  158. for(auto h : cb->getAvailableHeroes(t)) //we assume that all towns have same set of heroes
  159. {
  160. if(h && h->getTotalStrength() > 500) //do not buy heroes with single creatures for GatherArmy
  161. {
  162. ret.push_back(sptr(RecruitHero()));
  163. break;
  164. }
  165. }
  166. }
  167. }
  168. if(ret.empty())
  169. {
  170. const bool allowGatherArmy = false;
  171. if(hero == ai->primaryHero())
  172. ret.push_back(sptr(Explore(allowGatherArmy)));
  173. else
  174. throw cannotFulfillGoalException("No ways to gather army");
  175. }
  176. return ret;
  177. }