Win.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /*
  2. * Win.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/mapping/CMapHeader.h" //for victory conditions
  19. #include "../../../lib/mapObjects/CGTownInstance.h"
  20. #include "../../../lib/constants/StringConstants.h"
  21. using namespace Goals;
  22. TSubgoal Win::whatToDoToAchieve()
  23. {
  24. auto toBool = [=](const EventCondition &)
  25. {
  26. // TODO: proper implementation
  27. // Right now even already fulfilled goals will be included into generated list
  28. // Proper check should test if event condition is already fulfilled
  29. // Easiest way to do this is to call CGameState::checkForVictory but this function should not be
  30. // used on client side or in AI code
  31. return false;
  32. };
  33. std::vector<EventCondition> goals;
  34. for(const TriggeredEvent & event : cb->getMapHeader()->triggeredEvents)
  35. {
  36. //TODO: try to eliminate human player(s) using loss conditions that have isHuman element
  37. if(event.effect.type == EventEffect::VICTORY)
  38. {
  39. boost::range::copy(event.trigger.getFulfillmentCandidates(toBool), std::back_inserter(goals));
  40. }
  41. }
  42. //TODO: instead of returning first encountered goal AI should generate list of possible subgoals
  43. for(const EventCondition & goal : goals)
  44. {
  45. switch(goal.condition)
  46. {
  47. case EventCondition::HAVE_ARTIFACT:
  48. return sptr(GetArtOfType(goal.objectType.as<ArtifactID>()));
  49. case EventCondition::DESTROY:
  50. {
  51. if(goal.objectID != ObjectInstanceID::NONE)
  52. {
  53. auto obj = cb->getObj(goal.objectID);
  54. if(obj)
  55. if(obj->getOwner() == ai->playerID) //we can't capture our own object
  56. return sptr(Conquer());
  57. return sptr(VisitObj(goal.objectID.getNum()));
  58. }
  59. else
  60. {
  61. // TODO: destroy all objects of type goal.objectType
  62. // This situation represents "kill all creatures" condition from H3
  63. break;
  64. }
  65. }
  66. case EventCondition::HAVE_BUILDING:
  67. {
  68. // TODO build other buildings apart from Grail
  69. // goal.objectType = buildingID to build
  70. // goal.object = optional, town in which building should be built
  71. // Represents "Improve town" condition from H3 (but unlike H3 it consists from 2 separate conditions)
  72. if(goal.objectType.as<BuildingID>() == BuildingID::GRAIL)
  73. {
  74. if(auto h = ai->getHeroWithGrail())
  75. {
  76. //hero is in a town that can host Grail
  77. if(h->getVisitedTown() && !vstd::contains(h->getVisitedTown()->forbiddenBuildings, BuildingID::GRAIL))
  78. {
  79. const CGTownInstance * t = h->getVisitedTown();
  80. return sptr(BuildThis(BuildingID::GRAIL, t).setpriority(10));
  81. }
  82. else
  83. {
  84. auto towns = cb->getTownsInfo();
  85. towns.erase(boost::remove_if(towns,
  86. [](const CGTownInstance * t) -> bool
  87. {
  88. return vstd::contains(t->forbiddenBuildings, BuildingID::GRAIL);
  89. }),
  90. towns.end());
  91. boost::sort(towns, CDistanceSorter(h.get()));
  92. if(towns.size())
  93. {
  94. return sptr(VisitTile(towns.front()->visitablePos()).sethero(h));
  95. }
  96. }
  97. }
  98. double ratio = 0;
  99. // maybe make this check a bit more complex? For example:
  100. // 0.75 -> dig randomly within 3 tiles radius
  101. // 0.85 -> radius now 2 tiles
  102. // 0.95 -> 1 tile radius, position is fully known
  103. // AFAIK H3 AI does something like this
  104. int3 grailPos = cb->getGrailPos(&ratio);
  105. if(ratio > 0.99)
  106. {
  107. return sptr(DigAtTile(grailPos));
  108. } //TODO: use FIND_OBJ
  109. else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
  110. return sptr(VisitObj(obj->id.getNum()));
  111. else
  112. return sptr(Explore());
  113. }
  114. break;
  115. }
  116. case EventCondition::CONTROL:
  117. {
  118. if(goal.objectID != ObjectInstanceID::NONE)
  119. {
  120. auto obj = cb->getObj(goal.objectID);
  121. if(obj && cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES)
  122. {
  123. return sptr(VisitObj(goal.objectID.getNum()));
  124. }
  125. else
  126. {
  127. // TODO: Defance
  128. break;
  129. }
  130. }
  131. else
  132. {
  133. //TODO: control all objects of type "goal.objectType"
  134. // Represents H3 condition "Flag all mines"
  135. break;
  136. }
  137. }
  138. case EventCondition::HAVE_RESOURCES:
  139. //TODO mines? piles? marketplace?
  140. //save?
  141. return sptr(CollectRes(goal.objectType.as<GameResID>(), goal.value));
  142. case EventCondition::HAVE_CREATURES:
  143. return sptr(GatherTroops(goal.objectType.as<CreatureID>(), goal.value));
  144. case EventCondition::TRANSPORT:
  145. {
  146. //TODO. merge with bring Grail to town? So AI will first dig grail, then transport it using this goal and builds it
  147. // Represents "transport artifact" condition:
  148. // goal.objectType = type of artifact
  149. // goal.object = destination-town where artifact should be transported
  150. break;
  151. }
  152. case EventCondition::STANDARD_WIN:
  153. return sptr(Conquer());
  154. // Conditions that likely don't need any implementation
  155. case EventCondition::DAYS_PASSED:
  156. break; // goal.value = number of days for condition to trigger
  157. case EventCondition::DAYS_WITHOUT_TOWN:
  158. break; // goal.value = number of days to trigger this
  159. case EventCondition::IS_HUMAN:
  160. break; // Should be only used in calculation of candidates (see toBool lambda)
  161. case EventCondition::CONST_VALUE:
  162. break;
  163. default:
  164. assert(0);
  165. }
  166. }
  167. return sptr(Invalid());
  168. }