CBattleInfoEssentials.cpp 13 KB

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