CBattleInfoEssentials.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. /*
  2. * CBattleInfoEssentials.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 "CBattleInfoEssentials.h"
  12. #include "../CStack.h"
  13. #include "BattleInfo.h"
  14. #include "CObstacleInstance.h"
  15. #include "../constants/EntityIdentifiers.h"
  16. #include "../entities/building/TownFortifications.h"
  17. #include "../gameState/InfoAboutArmy.h"
  18. #include "../mapObjects/CGTownInstance.h"
  19. VCMI_LIB_NAMESPACE_BEGIN
  20. bool CBattleInfoEssentials::duringBattle() const
  21. {
  22. return getBattle() != nullptr;
  23. }
  24. TerrainId CBattleInfoEssentials::battleTerrainType() const
  25. {
  26. RETURN_IF_NOT_BATTLE(TerrainId());
  27. return getBattle()->getTerrainType();
  28. }
  29. BattleField CBattleInfoEssentials::battleGetBattlefieldType() const
  30. {
  31. RETURN_IF_NOT_BATTLE(BattleField::NONE);
  32. return getBattle()->getBattlefieldType();
  33. }
  34. int32_t CBattleInfoEssentials::battleGetEnchanterCounter(BattleSide side) const
  35. {
  36. RETURN_IF_NOT_BATTLE(0);
  37. return getBattle()->getEnchanterCounter(side);
  38. }
  39. std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoEssentials::battleGetAllObstacles(std::optional<BattleSide> perspective) const
  40. {
  41. std::vector<std::shared_ptr<const CObstacleInstance> > ret;
  42. RETURN_IF_NOT_BATTLE(ret);
  43. if(!perspective)
  44. {
  45. //if no particular perspective request, use default one
  46. perspective = std::make_optional(battleGetMySide());
  47. }
  48. else
  49. {
  50. if(!!getPlayerID() && *perspective != battleGetMySide())
  51. logGlobal->warn("Unauthorized obstacles access attempt, assuming massive spell");
  52. }
  53. for(const auto & obstacle : getBattle()->getAllObstacles())
  54. {
  55. if(battleIsObstacleVisibleForSide(*(obstacle), *perspective))
  56. ret.push_back(obstacle);
  57. }
  58. return ret;
  59. }
  60. std::shared_ptr<const CObstacleInstance> CBattleInfoEssentials::battleGetObstacleByID(uint32_t ID) const
  61. {
  62. std::shared_ptr<const CObstacleInstance> ret;
  63. RETURN_IF_NOT_BATTLE(std::shared_ptr<const CObstacleInstance>());
  64. for(auto obstacle : getBattle()->getAllObstacles())
  65. {
  66. if(obstacle->uniqueID == ID)
  67. return obstacle;
  68. }
  69. logGlobal->error("Invalid obstacle ID %d", ID);
  70. return std::shared_ptr<const CObstacleInstance>();
  71. }
  72. bool CBattleInfoEssentials::battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattleSide side) const
  73. {
  74. RETURN_IF_NOT_BATTLE(false);
  75. return side == BattleSide::ALL_KNOWING || coi.visibleForSide(side, battleHasNativeStack(side));
  76. }
  77. bool CBattleInfoEssentials::battleHasNativeStack(BattleSide side) const
  78. {
  79. RETURN_IF_NOT_BATTLE(false);
  80. for(const auto * s : battleGetAllStacks())
  81. {
  82. if(s->unitSide() == side && s->isNativeTerrain(getBattle()->getTerrainType()))
  83. return true;
  84. }
  85. return false;
  86. }
  87. TStacks CBattleInfoEssentials::battleGetAllStacks(bool includeTurrets) const
  88. {
  89. return battleGetStacksIf([=](const CStack * s)
  90. {
  91. return !s->isGhost() && (includeTurrets || !s->isTurret());
  92. });
  93. }
  94. TStacks CBattleInfoEssentials::battleGetStacksIf(const TStackFilter & predicate) const
  95. {
  96. RETURN_IF_NOT_BATTLE(TStacks());
  97. return getBattle()->getStacksIf(std::move(predicate));
  98. }
  99. battle::Units CBattleInfoEssentials::battleGetUnitsIf(const battle::UnitFilter & predicate) const
  100. {
  101. RETURN_IF_NOT_BATTLE(battle::Units());
  102. return getBattle()->getUnitsIf(predicate);
  103. }
  104. const battle::Unit * CBattleInfoEssentials::battleGetUnitByID(uint32_t ID) const
  105. {
  106. RETURN_IF_NOT_BATTLE(nullptr);
  107. //TODO: consider using map ID -> Unit
  108. auto ret = battleGetUnitsIf([=](const battle::Unit * unit)
  109. {
  110. return unit->unitId() == ID;
  111. });
  112. if(ret.empty())
  113. return nullptr;
  114. else
  115. return ret[0];
  116. }
  117. const battle::Unit * CBattleInfoEssentials::battleActiveUnit() const
  118. {
  119. RETURN_IF_NOT_BATTLE(nullptr);
  120. auto id = getBattle()->getActiveStackID();
  121. if(id >= 0)
  122. return battleGetUnitByID(static_cast<uint32_t>(id));
  123. else
  124. return nullptr;
  125. }
  126. uint32_t CBattleInfoEssentials::battleNextUnitId() const
  127. {
  128. return getBattle()->nextUnitId();
  129. }
  130. const CGTownInstance * CBattleInfoEssentials::battleGetDefendedTown() const
  131. {
  132. RETURN_IF_NOT_BATTLE(nullptr);
  133. return getBattle()->getDefendedTown();
  134. }
  135. BattleSide CBattleInfoEssentials::battleGetMySide() const
  136. {
  137. RETURN_IF_NOT_BATTLE(BattleSide::INVALID);
  138. if(!getPlayerID() || getPlayerID()->isSpectator())
  139. return BattleSide::ALL_KNOWING;
  140. if(*getPlayerID() == getBattle()->getSidePlayer(BattleSide::ATTACKER))
  141. return BattleSide::LEFT_SIDE;
  142. if(*getPlayerID() == getBattle()->getSidePlayer(BattleSide::DEFENDER))
  143. return BattleSide::RIGHT_SIDE;
  144. logGlobal->error("Cannot find player %s in battle!", getPlayerID()->toString());
  145. return BattleSide::INVALID;
  146. }
  147. const CStack* CBattleInfoEssentials::battleGetStackByID(int ID, bool onlyAlive) const
  148. {
  149. RETURN_IF_NOT_BATTLE(nullptr);
  150. auto stacks = battleGetStacksIf([=](const CStack * s)
  151. {
  152. return s->unitId() == ID && (!onlyAlive || s->alive());
  153. });
  154. if(stacks.empty())
  155. return nullptr;
  156. else
  157. return stacks[0];
  158. }
  159. bool CBattleInfoEssentials::battleDoWeKnowAbout(BattleSide side) const
  160. {
  161. RETURN_IF_NOT_BATTLE(false);
  162. auto p = battleGetMySide();
  163. return p == BattleSide::ALL_KNOWING || p == side;
  164. }
  165. si8 CBattleInfoEssentials::battleTacticDist() const
  166. {
  167. RETURN_IF_NOT_BATTLE(0);
  168. return getBattle()->getTacticDist();
  169. }
  170. BattleSide CBattleInfoEssentials::battleGetTacticsSide() const
  171. {
  172. RETURN_IF_NOT_BATTLE(BattleSide::NONE);
  173. return getBattle()->getTacticsSide();
  174. }
  175. const CGHeroInstance * CBattleInfoEssentials::battleGetFightingHero(BattleSide side) const
  176. {
  177. RETURN_IF_NOT_BATTLE(nullptr);
  178. if(side != BattleSide::DEFENDER && side != BattleSide::ATTACKER)
  179. {
  180. logGlobal->error("FIXME: %s wrong argument!", __FUNCTION__);
  181. return nullptr;
  182. }
  183. if(!battleDoWeKnowAbout(side))
  184. {
  185. logGlobal->error("FIXME: %s access check ", __FUNCTION__);
  186. return nullptr;
  187. }
  188. return getBattle()->getSideHero(side);
  189. }
  190. const CArmedInstance * CBattleInfoEssentials::battleGetArmyObject(BattleSide side) const
  191. {
  192. RETURN_IF_NOT_BATTLE(nullptr);
  193. if(side != BattleSide::DEFENDER && side != BattleSide::ATTACKER)
  194. {
  195. logGlobal->error("FIXME: %s wrong argument!", __FUNCTION__);
  196. return nullptr;
  197. }
  198. if(!battleDoWeKnowAbout(side))
  199. {
  200. logGlobal->error("FIXME: %s access check!", __FUNCTION__);
  201. return nullptr;
  202. }
  203. return getBattle()->getSideArmy(side);
  204. }
  205. InfoAboutHero CBattleInfoEssentials::battleGetHeroInfo(BattleSide side) const
  206. {
  207. const auto * hero = getBattle()->getSideHero(side);
  208. if(!hero)
  209. {
  210. return InfoAboutHero();
  211. }
  212. InfoAboutHero::EInfoLevel infoLevel = battleDoWeKnowAbout(side) ? InfoAboutHero::EInfoLevel::DETAILED : InfoAboutHero::EInfoLevel::BASIC;
  213. return InfoAboutHero(hero, infoLevel);
  214. }
  215. uint32_t CBattleInfoEssentials::battleCastSpells(BattleSide side) const
  216. {
  217. RETURN_IF_NOT_BATTLE(-1);
  218. return getBattle()->getCastSpells(side);
  219. }
  220. const IBonusBearer * CBattleInfoEssentials::getBonusBearer() const
  221. {
  222. return getBattle()->getBonusBearer();
  223. }
  224. bool CBattleInfoEssentials::battleCanFlee(const PlayerColor & player) const
  225. {
  226. RETURN_IF_NOT_BATTLE(false);
  227. const auto side = playerToSide(player);
  228. if(side == BattleSide::NONE)
  229. return false;
  230. const CGHeroInstance * myHero = battleGetFightingHero(side);
  231. //current player have no hero
  232. if(!myHero)
  233. return false;
  234. //eg. one of heroes is wearing shakles of war
  235. if(myHero->hasBonusOfType(BonusType::BATTLE_NO_FLEEING))
  236. return false;
  237. //we are besieged defender
  238. if(side == BattleSide::DEFENDER && getBattle()->getDefendedTown() != nullptr)
  239. {
  240. const auto * town = battleGetDefendedTown();
  241. if(!town->hasBuilt(BuildingSubID::ESCAPE_TUNNEL))
  242. return false;
  243. }
  244. return true;
  245. }
  246. BattleSide CBattleInfoEssentials::playerToSide(const PlayerColor & player) const
  247. {
  248. RETURN_IF_NOT_BATTLE(BattleSide::NONE);
  249. if(getBattle()->getSidePlayer(BattleSide::ATTACKER) == player)
  250. return BattleSide::ATTACKER;
  251. if(getBattle()->getSidePlayer(BattleSide::DEFENDER) == player)
  252. return BattleSide::DEFENDER;
  253. logGlobal->warn("Cannot find side for player %s", player.toString());
  254. return BattleSide::INVALID;
  255. }
  256. PlayerColor CBattleInfoEssentials::sideToPlayer(BattleSide side) const
  257. {
  258. RETURN_IF_NOT_BATTLE(PlayerColor::CANNOT_DETERMINE);
  259. return getBattle()->getSidePlayer(side);
  260. }
  261. BattleSide CBattleInfoEssentials::otherSide(BattleSide side)
  262. {
  263. if(side == BattleSide::ATTACKER)
  264. return BattleSide::DEFENDER;
  265. else
  266. return BattleSide::ATTACKER;
  267. }
  268. PlayerColor CBattleInfoEssentials::otherPlayer(const PlayerColor & player) const
  269. {
  270. RETURN_IF_NOT_BATTLE(PlayerColor::CANNOT_DETERMINE);
  271. auto side = playerToSide(player);
  272. if(side == BattleSide::NONE)
  273. return PlayerColor::CANNOT_DETERMINE;
  274. return getBattle()->getSidePlayer(otherSide(side));
  275. }
  276. bool CBattleInfoEssentials::playerHasAccessToHeroInfo(const PlayerColor & player, const CGHeroInstance * h) const
  277. {
  278. RETURN_IF_NOT_BATTLE(false);
  279. const auto side = playerToSide(player);
  280. if(side != BattleSide::NONE)
  281. {
  282. auto opponentSide = otherSide(side);
  283. if(getBattle()->getSideHero(opponentSide) == h)
  284. return true;
  285. }
  286. return false;
  287. }
  288. TownFortifications CBattleInfoEssentials::battleGetFortifications() const
  289. {
  290. RETURN_IF_NOT_BATTLE(TownFortifications());
  291. return getBattle()->getDefendedTown() ? getBattle()->getDefendedTown()->fortificationsLevel() : TownFortifications();
  292. }
  293. bool CBattleInfoEssentials::battleCanSurrender(const PlayerColor & player) const
  294. {
  295. RETURN_IF_NOT_BATTLE(false);
  296. const auto side = playerToSide(player);
  297. if(side == BattleSide::NONE)
  298. return false;
  299. bool iAmSiegeDefender = (side == BattleSide::DEFENDER && getBattle()->getDefendedTown() != nullptr);
  300. //conditions like for fleeing (except escape tunnel presence) + enemy must have a hero
  301. return battleCanFlee(player) && !iAmSiegeDefender && battleHasHero(otherSide(side));
  302. }
  303. bool CBattleInfoEssentials::battleHasHero(BattleSide side) const
  304. {
  305. RETURN_IF_NOT_BATTLE(false);
  306. return getBattle()->getSideHero(side) != nullptr;
  307. }
  308. EWallState CBattleInfoEssentials::battleGetWallState(EWallPart partOfWall) const
  309. {
  310. RETURN_IF_NOT_BATTLE(EWallState::NONE);
  311. if(battleGetFortifications().wallsHealth == 0)
  312. return EWallState::NONE;
  313. return getBattle()->getWallState(partOfWall);
  314. }
  315. EGateState CBattleInfoEssentials::battleGetGateState() const
  316. {
  317. RETURN_IF_NOT_BATTLE(EGateState::NONE);
  318. if(battleGetFortifications().wallsHealth == 0)
  319. return EGateState::NONE;
  320. return getBattle()->getGateState();
  321. }
  322. bool CBattleInfoEssentials::battleIsGatePassable() const
  323. {
  324. RETURN_IF_NOT_BATTLE(true);
  325. if(battleGetFortifications().wallsHealth == 0)
  326. return true;
  327. return battleGetGateState() == EGateState::OPENED || battleGetGateState() == EGateState::DESTROYED;
  328. }
  329. PlayerColor CBattleInfoEssentials::battleGetOwner(const battle::Unit * unit) const
  330. {
  331. RETURN_IF_NOT_BATTLE(PlayerColor::CANNOT_DETERMINE);
  332. PlayerColor initialOwner = getBattle()->getSidePlayer(unit->unitSide());
  333. static const CSelector selector = Selector::type()(BonusType::HYPNOTIZED);
  334. if(unit->hasBonus(selector))
  335. return otherPlayer(initialOwner);
  336. else
  337. return initialOwner;
  338. }
  339. const CGHeroInstance * CBattleInfoEssentials::battleGetOwnerHero(const battle::Unit * unit) const
  340. {
  341. RETURN_IF_NOT_BATTLE(nullptr);
  342. const auto side = playerToSide(battleGetOwner(unit));
  343. if(side == BattleSide::NONE)
  344. return nullptr;
  345. return getBattle()->getSideHero(side);
  346. }
  347. bool CBattleInfoEssentials::battleMatchOwner(const battle::Unit * attacker, const battle::Unit * defender, const boost::logic::tribool positivness) const
  348. {
  349. RETURN_IF_NOT_BATTLE(false);
  350. if(boost::logic::indeterminate(positivness))
  351. return true;
  352. else if(attacker->unitId() == defender->unitId())
  353. return (bool)positivness;
  354. else
  355. return battleMatchOwner(battleGetOwner(attacker), defender, positivness);
  356. }
  357. bool CBattleInfoEssentials::battleMatchOwner(const PlayerColor & attacker, const battle::Unit * defender, const boost::logic::tribool positivness) const
  358. {
  359. RETURN_IF_NOT_BATTLE(false);
  360. PlayerColor initialOwner = getBattle()->getSidePlayer(defender->unitSide());
  361. return boost::logic::indeterminate(positivness) || (attacker == initialOwner) == (bool)positivness;
  362. }
  363. VCMI_LIB_NAMESPACE_END