BattleInfo.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  1. /*
  2. * BattleInfo.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 "BattleInfo.h"
  12. #include "CObstacleInstance.h"
  13. #include "bonuses/Limiters.h"
  14. #include "bonuses/Updaters.h"
  15. #include "../CStack.h"
  16. #include "../CHeroHandler.h"
  17. #include "../filesystem/Filesystem.h"
  18. #include "../mapObjects/CGTownInstance.h"
  19. #include "../texts/CGeneralTextHandler.h"
  20. #include "../BattleFieldHandler.h"
  21. #include "../ObstacleHandler.h"
  22. #include <vstd/RNG.h>
  23. //TODO: remove
  24. #include "../IGameCallback.h"
  25. VCMI_LIB_NAMESPACE_BEGIN
  26. const SideInBattle & BattleInfo::getSide(BattleSide side) const
  27. {
  28. return sides.at(side);
  29. }
  30. SideInBattle & BattleInfo::getSide(BattleSide side)
  31. {
  32. return sides.at(side);
  33. }
  34. ///BattleInfo
  35. CStack * BattleInfo::generateNewStack(uint32_t id, const CStackInstance & base, BattleSide side, const SlotID & slot, BattleHex position)
  36. {
  37. PlayerColor owner = getSide(side).color;
  38. assert(!owner.isValidPlayer() || (base.armyObj && base.armyObj->tempOwner == owner));
  39. auto * ret = new CStack(&base, owner, id, side, slot);
  40. ret->initialPosition = getAvailableHex(base.getCreatureID(), side, position); //TODO: what if no free tile on battlefield was found?
  41. stacks.push_back(ret);
  42. return ret;
  43. }
  44. CStack * BattleInfo::generateNewStack(uint32_t id, const CStackBasicDescriptor & base, BattleSide side, const SlotID & slot, BattleHex position)
  45. {
  46. PlayerColor owner = getSide(side).color;
  47. auto * ret = new CStack(&base, owner, id, side, slot);
  48. ret->initialPosition = position;
  49. stacks.push_back(ret);
  50. return ret;
  51. }
  52. void BattleInfo::localInit()
  53. {
  54. for(BattleSide i : { BattleSide::ATTACKER, BattleSide::DEFENDER})
  55. {
  56. auto * armyObj = battleGetArmyObject(i);
  57. armyObj->battle = this;
  58. armyObj->attachTo(*this);
  59. }
  60. for(CStack * s : stacks)
  61. s->localInit(this);
  62. exportBonuses();
  63. }
  64. namespace CGH
  65. {
  66. static void readBattlePositions(const JsonNode &node, std::vector< std::vector<int> > & dest)
  67. {
  68. for(const JsonNode &level : node.Vector())
  69. {
  70. std::vector<int> pom;
  71. for(const JsonNode &value : level.Vector())
  72. {
  73. pom.push_back(static_cast<int>(value.Float()));
  74. }
  75. dest.push_back(pom);
  76. }
  77. }
  78. }
  79. //RNG that works like H3 one
  80. struct RandGen
  81. {
  82. ui32 seed;
  83. void srand(ui32 s)
  84. {
  85. seed = s;
  86. }
  87. void srand(const int3 & pos)
  88. {
  89. srand(110291 * static_cast<ui32>(pos.x) + 167801 * static_cast<ui32>(pos.y) + 81569);
  90. }
  91. int rand()
  92. {
  93. seed = 214013 * seed + 2531011;
  94. return (seed >> 16) & 0x7FFF;
  95. }
  96. int rand(int min, int max)
  97. {
  98. if(min == max)
  99. return min;
  100. if(min > max)
  101. return min;
  102. return min + rand() % (max - min + 1);
  103. }
  104. };
  105. struct RangeGenerator
  106. {
  107. class ExhaustedPossibilities : public std::exception
  108. {
  109. };
  110. RangeGenerator(int _min, int _max, std::function<int()> _myRand):
  111. min(_min),
  112. remainingCount(_max - _min + 1),
  113. remaining(remainingCount, true),
  114. myRand(std::move(_myRand))
  115. {
  116. }
  117. int generateNumber() const
  118. {
  119. if(!remainingCount)
  120. throw ExhaustedPossibilities();
  121. if(remainingCount == 1)
  122. return 0;
  123. return myRand() % remainingCount;
  124. }
  125. //get number fulfilling predicate. Never gives the same number twice.
  126. int getSuchNumber(const std::function<bool(int)> & goodNumberPred = nullptr)
  127. {
  128. int ret = -1;
  129. do
  130. {
  131. int n = generateNumber();
  132. int i = 0;
  133. for(;;i++)
  134. {
  135. assert(i < (int)remaining.size());
  136. if(!remaining[i])
  137. continue;
  138. if(!n)
  139. break;
  140. n--;
  141. }
  142. remainingCount--;
  143. remaining[i] = false;
  144. ret = i + min;
  145. } while(goodNumberPred && !goodNumberPred(ret));
  146. return ret;
  147. }
  148. int min;
  149. int remainingCount;
  150. std::vector<bool> remaining;
  151. std::function<int()> myRand;
  152. };
  153. BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const BattleField & battlefieldType, BattleSideArray<const CArmedInstance *> armies, BattleSideArray<const CGHeroInstance *> heroes, bool creatureBank, const CGTownInstance * town)
  154. {
  155. CMP_stack cmpst;
  156. auto * curB = new BattleInfo();
  157. for(auto i : { BattleSide::LEFT_SIDE, BattleSide::RIGHT_SIDE})
  158. curB->sides[i].init(heroes[i], armies[i]);
  159. std::vector<CStack*> & stacks = (curB->stacks);
  160. curB->tile = tile;
  161. curB->battlefieldType = battlefieldType;
  162. curB->round = -2;
  163. curB->activeStack = -1;
  164. curB->creatureBank = creatureBank;
  165. curB->replayAllowed = false;
  166. if(town)
  167. {
  168. curB->town = town;
  169. curB->terrainType = town->getNativeTerrain();
  170. }
  171. else
  172. {
  173. curB->town = nullptr;
  174. curB->terrainType = terrain;
  175. }
  176. //setting up siege obstacles
  177. if (town && town->hasFort())
  178. {
  179. curB->si.gateState = EGateState::CLOSED;
  180. curB->si.wallState[EWallPart::GATE] = EWallState::INTACT;
  181. for(const auto wall : {EWallPart::BOTTOM_WALL, EWallPart::BELOW_GATE, EWallPart::OVER_GATE, EWallPart::UPPER_WALL})
  182. {
  183. if (town->hasBuilt(BuildingID::CASTLE))
  184. curB->si.wallState[wall] = EWallState::REINFORCED;
  185. else
  186. curB->si.wallState[wall] = EWallState::INTACT;
  187. }
  188. if (town->hasBuilt(BuildingID::CITADEL))
  189. curB->si.wallState[EWallPart::KEEP] = EWallState::INTACT;
  190. if (town->hasBuilt(BuildingID::CASTLE))
  191. {
  192. curB->si.wallState[EWallPart::UPPER_TOWER] = EWallState::INTACT;
  193. curB->si.wallState[EWallPart::BOTTOM_TOWER] = EWallState::INTACT;
  194. }
  195. }
  196. //randomize obstacles
  197. if (town == nullptr && !creatureBank) //do it only when it's not siege and not creature bank
  198. {
  199. RandGen r{};
  200. auto ourRand = [&](){ return r.rand(); };
  201. r.srand(tile);
  202. r.rand(1,8); //battle sound ID to play... can't do anything with it here
  203. int tilesToBlock = r.rand(5,12);
  204. std::vector<BattleHex> blockedTiles;
  205. auto appropriateAbsoluteObstacle = [&](int id)
  206. {
  207. const auto * info = Obstacle(id).getInfo();
  208. return info && info->isAbsoluteObstacle && info->isAppropriate(curB->terrainType, battlefieldType);
  209. };
  210. auto appropriateUsualObstacle = [&](int id)
  211. {
  212. const auto * info = Obstacle(id).getInfo();
  213. return info && !info->isAbsoluteObstacle && info->isAppropriate(curB->terrainType, battlefieldType);
  214. };
  215. if(r.rand(1,100) <= 40) //put cliff-like obstacle
  216. {
  217. try
  218. {
  219. RangeGenerator obidgen(0, VLC->obstacleHandler->size() - 1, ourRand);
  220. auto obstPtr = std::make_shared<CObstacleInstance>();
  221. obstPtr->obstacleType = CObstacleInstance::ABSOLUTE_OBSTACLE;
  222. obstPtr->ID = obidgen.getSuchNumber(appropriateAbsoluteObstacle);
  223. obstPtr->uniqueID = static_cast<si32>(curB->obstacles.size());
  224. curB->obstacles.push_back(obstPtr);
  225. for(BattleHex blocked : obstPtr->getBlockedTiles())
  226. blockedTiles.push_back(blocked);
  227. tilesToBlock -= Obstacle(obstPtr->ID).getInfo()->blockedTiles.size() / 2;
  228. }
  229. catch(RangeGenerator::ExhaustedPossibilities &)
  230. {
  231. //silently ignore, if we can't place absolute obstacle, we'll go with the usual ones
  232. logGlobal->debug("RangeGenerator::ExhaustedPossibilities exception occurred - cannot place absolute obstacle");
  233. }
  234. }
  235. try
  236. {
  237. while(tilesToBlock > 0)
  238. {
  239. RangeGenerator obidgen(0, VLC->obstacleHandler->size() - 1, ourRand);
  240. auto tileAccessibility = curB->getAccessibility();
  241. const int obid = obidgen.getSuchNumber(appropriateUsualObstacle);
  242. const ObstacleInfo &obi = *Obstacle(obid).getInfo();
  243. auto validPosition = [&](BattleHex pos) -> bool
  244. {
  245. if(obi.height >= pos.getY())
  246. return false;
  247. if(pos.getX() == 0)
  248. return false;
  249. if(pos.getX() + obi.width > 15)
  250. return false;
  251. if(vstd::contains(blockedTiles, pos))
  252. return false;
  253. for(BattleHex blocked : obi.getBlocked(pos))
  254. {
  255. if(tileAccessibility[blocked] == EAccessibility::UNAVAILABLE) //for ship-to-ship battlefield - exclude hardcoded unavailable tiles
  256. return false;
  257. if(vstd::contains(blockedTiles, blocked))
  258. return false;
  259. int x = blocked.getX();
  260. if(x <= 2 || x >= 14)
  261. return false;
  262. }
  263. return true;
  264. };
  265. RangeGenerator posgenerator(18, 168, ourRand);
  266. auto obstPtr = std::make_shared<CObstacleInstance>();
  267. obstPtr->ID = obid;
  268. obstPtr->pos = posgenerator.getSuchNumber(validPosition);
  269. obstPtr->uniqueID = static_cast<si32>(curB->obstacles.size());
  270. curB->obstacles.push_back(obstPtr);
  271. for(BattleHex blocked : obstPtr->getBlockedTiles())
  272. blockedTiles.push_back(blocked);
  273. tilesToBlock -= static_cast<int>(obi.blockedTiles.size());
  274. }
  275. }
  276. catch(RangeGenerator::ExhaustedPossibilities &)
  277. {
  278. logGlobal->debug("RangeGenerator::ExhaustedPossibilities exception occurred - cannot place usual obstacle");
  279. }
  280. }
  281. //reading battleStartpos - add creatures AFTER random obstacles are generated
  282. //TODO: parse once to some structure
  283. BattleSideArray<std::vector<std::vector<int>>> looseFormations;
  284. BattleSideArray<std::vector<std::vector<int>>> tightFormations;
  285. BattleSideArray<std::vector<std::vector<int>>> creBankFormations;
  286. BattleSideArray<int> commanderField;
  287. BattleSideArray<int> commanderBank;
  288. const JsonNode config(JsonPath::builtin("config/battleStartpos.json"));
  289. const JsonVector &positions = config["battle_positions"].Vector();
  290. CGH::readBattlePositions(positions[0]["levels"], looseFormations[BattleSide::ATTACKER]);
  291. CGH::readBattlePositions(positions[1]["levels"], looseFormations[BattleSide::DEFENDER]);
  292. CGH::readBattlePositions(positions[2]["levels"], tightFormations[BattleSide::ATTACKER]);
  293. CGH::readBattlePositions(positions[3]["levels"], tightFormations[BattleSide::DEFENDER]);
  294. CGH::readBattlePositions(positions[4]["levels"], creBankFormations[BattleSide::ATTACKER]);
  295. CGH::readBattlePositions(positions[5]["levels"], creBankFormations[BattleSide::DEFENDER]);
  296. commanderField[BattleSide::ATTACKER] = config["commanderPositions"]["field"][0].Integer();
  297. commanderField[BattleSide::DEFENDER] = config["commanderPositions"]["field"][1].Integer();
  298. commanderBank[BattleSide::ATTACKER] = config["commanderPositions"]["creBank"][0].Integer();
  299. commanderBank[BattleSide::DEFENDER] = config["commanderPositions"]["creBank"][1].Integer();
  300. //adding war machines
  301. if(!creatureBank)
  302. {
  303. //Checks if hero has artifact and create appropriate stack
  304. auto handleWarMachine = [&](BattleSide side, const ArtifactPosition & artslot, BattleHex hex)
  305. {
  306. const CArtifactInstance * warMachineArt = heroes[side]->getArt(artslot);
  307. if(nullptr != warMachineArt)
  308. {
  309. CreatureID cre = warMachineArt->artType->getWarMachine();
  310. if(cre != CreatureID::NONE)
  311. curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(cre, 1), side, SlotID::WAR_MACHINES_SLOT, hex);
  312. }
  313. };
  314. if(heroes[BattleSide::ATTACKER])
  315. {
  316. handleWarMachine(BattleSide::ATTACKER, ArtifactPosition::MACH1, 52);
  317. handleWarMachine(BattleSide::ATTACKER, ArtifactPosition::MACH2, 18);
  318. handleWarMachine(BattleSide::ATTACKER, ArtifactPosition::MACH3, 154);
  319. if(town && town->hasFort())
  320. handleWarMachine(BattleSide::ATTACKER, ArtifactPosition::MACH4, 120);
  321. }
  322. if(heroes[BattleSide::DEFENDER])
  323. {
  324. if(!town) //defending hero shouldn't receive ballista (bug #551)
  325. handleWarMachine(BattleSide::DEFENDER, ArtifactPosition::MACH1, 66);
  326. handleWarMachine(BattleSide::DEFENDER, ArtifactPosition::MACH2, 32);
  327. handleWarMachine(BattleSide::DEFENDER, ArtifactPosition::MACH3, 168);
  328. }
  329. }
  330. //war machines added
  331. //battleStartpos read
  332. for(BattleSide side : {BattleSide::ATTACKER, BattleSide::DEFENDER})
  333. {
  334. int formationNo = armies[side]->stacksCount() - 1;
  335. vstd::abetween(formationNo, 0, GameConstants::ARMY_SIZE - 1);
  336. int k = 0; //stack serial
  337. for(auto i = armies[side]->Slots().begin(); i != armies[side]->Slots().end(); i++, k++)
  338. {
  339. std::vector<int> *formationVector = nullptr;
  340. if(armies[side]->formation == EArmyFormation::TIGHT )
  341. formationVector = &tightFormations[side][formationNo];
  342. else
  343. formationVector = &looseFormations[side][formationNo];
  344. if(creatureBank)
  345. formationVector = &creBankFormations[side][formationNo];
  346. BattleHex pos = (k < formationVector->size() ? formationVector->at(k) : 0);
  347. if(creatureBank && i->second->type->isDoubleWide())
  348. pos += side == BattleSide::RIGHT_SIDE ? BattleHex::LEFT : BattleHex::RIGHT;
  349. curB->generateNewStack(curB->nextUnitId(), *i->second, side, i->first, pos);
  350. }
  351. }
  352. //adding commanders
  353. for(BattleSide i : {BattleSide::ATTACKER, BattleSide::DEFENDER})
  354. {
  355. if (heroes[i] && heroes[i]->commander && heroes[i]->commander->alive)
  356. {
  357. curB->generateNewStack(curB->nextUnitId(), *heroes[i]->commander, i, SlotID::COMMANDER_SLOT_PLACEHOLDER, creatureBank ? commanderBank[i] : commanderField[i]);
  358. }
  359. }
  360. if (curB->town && curB->town->fortLevel() >= CGTownInstance::CITADEL)
  361. {
  362. // keep tower
  363. curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_CENTRAL_TOWER);
  364. if (curB->town->fortLevel() >= CGTownInstance::CASTLE)
  365. {
  366. // lower tower + upper tower
  367. curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_UPPER_TOWER);
  368. curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), BattleSide::DEFENDER, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_BOTTOM_TOWER);
  369. }
  370. //Moat generating is done on server
  371. }
  372. std::stable_sort(stacks.begin(),stacks.end(),cmpst);
  373. auto neutral = std::make_shared<CreatureAlignmentLimiter>(EAlignment::NEUTRAL);
  374. auto good = std::make_shared<CreatureAlignmentLimiter>(EAlignment::GOOD);
  375. auto evil = std::make_shared<CreatureAlignmentLimiter>(EAlignment::EVIL);
  376. const auto * bgInfo = VLC->battlefields()->getById(battlefieldType);
  377. for(const std::shared_ptr<Bonus> & bonus : bgInfo->bonuses)
  378. {
  379. curB->addNewBonus(bonus);
  380. }
  381. //native terrain bonuses
  382. auto nativeTerrain = std::make_shared<CreatureTerrainLimiter>();
  383. curB->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::STACKS_SPEED, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID())->addLimiter(nativeTerrain));
  384. curB->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID(), BonusSubtypeID(PrimarySkill::ATTACK))->addLimiter(nativeTerrain));
  385. curB->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, BonusSourceID(), BonusSubtypeID(PrimarySkill::DEFENSE))->addLimiter(nativeTerrain));
  386. //////////////////////////////////////////////////////////////////////////
  387. //tactics
  388. bool isTacticsAllowed = !creatureBank; //no tactics in creature banks
  389. BattleSideArray<int> battleRepositionHex = {};
  390. BattleSideArray<int> battleRepositionHexBlock = {};
  391. for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER})
  392. {
  393. if(heroes[i])
  394. {
  395. battleRepositionHex[i] += heroes[i]->valOfBonuses(Selector::type()(BonusType::BEFORE_BATTLE_REPOSITION));
  396. battleRepositionHexBlock[i] += heroes[i]->valOfBonuses(Selector::type()(BonusType::BEFORE_BATTLE_REPOSITION_BLOCK));
  397. }
  398. }
  399. int tacticsSkillDiffAttacker = battleRepositionHex[BattleSide::ATTACKER] - battleRepositionHexBlock[BattleSide::DEFENDER];
  400. int tacticsSkillDiffDefender = battleRepositionHex[BattleSide::DEFENDER] - battleRepositionHexBlock[BattleSide::ATTACKER];
  401. /* for current tactics, we need to choose one side, so, we will choose side when first - second > 0, and ignore sides
  402. when first - second <= 0. If there will be situations when both > 0, attacker will be chosen. Anyway, in OH3 this
  403. will not happen because tactics block opposite tactics on same value.
  404. TODO: For now, it is an error to use BEFORE_BATTLE_REPOSITION bonus without counterpart, but it can be changed if
  405. double tactics will be implemented.
  406. */
  407. if(isTacticsAllowed)
  408. {
  409. if(tacticsSkillDiffAttacker > 0 && tacticsSkillDiffDefender > 0)
  410. logGlobal->warn("Double tactics is not implemented, only attacker will have tactics!");
  411. if(tacticsSkillDiffAttacker > 0)
  412. {
  413. curB->tacticsSide = BattleSide::ATTACKER;
  414. //bonus specifies distance you can move beyond base row; this allows 100% compatibility with HMM3 mechanics
  415. curB->tacticDistance = 1 + tacticsSkillDiffAttacker;
  416. }
  417. else if(tacticsSkillDiffDefender > 0)
  418. {
  419. curB->tacticsSide = BattleSide::DEFENDER;
  420. //bonus specifies distance you can move beyond base row; this allows 100% compatibility with HMM3 mechanics
  421. curB->tacticDistance = 1 + tacticsSkillDiffDefender;
  422. }
  423. else
  424. curB->tacticDistance = 0;
  425. }
  426. return curB;
  427. }
  428. const CGHeroInstance * BattleInfo::getHero(const PlayerColor & player) const
  429. {
  430. for(const auto & side : sides)
  431. if(side.color == player)
  432. return side.hero;
  433. logGlobal->error("Player %s is not in battle!", player.toString());
  434. return nullptr;
  435. }
  436. BattleSide BattleInfo::whatSide(const PlayerColor & player) const
  437. {
  438. for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER})
  439. if(sides[i].color == player)
  440. return i;
  441. logGlobal->warn("BattleInfo::whatSide: Player %s is not in battle!", player.toString());
  442. return BattleSide::NONE;
  443. }
  444. CStack * BattleInfo::getStack(int stackID, bool onlyAlive)
  445. {
  446. return const_cast<CStack *>(battleGetStackByID(stackID, onlyAlive));
  447. }
  448. BattleInfo::BattleInfo():
  449. round(-1),
  450. activeStack(-1),
  451. town(nullptr),
  452. tile(-1,-1,-1),
  453. battlefieldType(BattleField::NONE),
  454. tacticsSide(BattleSide::NONE),
  455. tacticDistance(0)
  456. {
  457. setNodeType(BATTLE);
  458. }
  459. BattleID BattleInfo::getBattleID() const
  460. {
  461. return battleID;
  462. }
  463. const IBattleInfo * BattleInfo::getBattle() const
  464. {
  465. return this;
  466. }
  467. std::optional<PlayerColor> BattleInfo::getPlayerID() const
  468. {
  469. return std::nullopt;
  470. }
  471. BattleInfo::~BattleInfo()
  472. {
  473. for (auto & elem : stacks)
  474. delete elem;
  475. for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER})
  476. if(auto * _armyObj = battleGetArmyObject(i))
  477. _armyObj->battle = nullptr;
  478. }
  479. int32_t BattleInfo::getActiveStackID() const
  480. {
  481. return activeStack;
  482. }
  483. TStacks BattleInfo::getStacksIf(const TStackFilter & predicate) const
  484. {
  485. TStacks ret;
  486. vstd::copy_if(stacks, std::back_inserter(ret), predicate);
  487. return ret;
  488. }
  489. battle::Units BattleInfo::getUnitsIf(const battle::UnitFilter & predicate) const
  490. {
  491. battle::Units ret;
  492. vstd::copy_if(stacks, std::back_inserter(ret), predicate);
  493. return ret;
  494. }
  495. BattleField BattleInfo::getBattlefieldType() const
  496. {
  497. return battlefieldType;
  498. }
  499. TerrainId BattleInfo::getTerrainType() const
  500. {
  501. return terrainType;
  502. }
  503. IBattleInfo::ObstacleCList BattleInfo::getAllObstacles() const
  504. {
  505. ObstacleCList ret;
  506. for(const auto & obstacle : obstacles)
  507. ret.push_back(obstacle);
  508. return ret;
  509. }
  510. PlayerColor BattleInfo::getSidePlayer(BattleSide side) const
  511. {
  512. return getSide(side).color;
  513. }
  514. const CArmedInstance * BattleInfo::getSideArmy(BattleSide side) const
  515. {
  516. return getSide(side).armyObject;
  517. }
  518. const CGHeroInstance * BattleInfo::getSideHero(BattleSide side) const
  519. {
  520. return getSide(side).hero;
  521. }
  522. uint8_t BattleInfo::getTacticDist() const
  523. {
  524. return tacticDistance;
  525. }
  526. BattleSide BattleInfo::getTacticsSide() const
  527. {
  528. return tacticsSide;
  529. }
  530. const CGTownInstance * BattleInfo::getDefendedTown() const
  531. {
  532. return town;
  533. }
  534. EWallState BattleInfo::getWallState(EWallPart partOfWall) const
  535. {
  536. return si.wallState.at(partOfWall);
  537. }
  538. EGateState BattleInfo::getGateState() const
  539. {
  540. return si.gateState;
  541. }
  542. uint32_t BattleInfo::getCastSpells(BattleSide side) const
  543. {
  544. return getSide(side).castSpellsCount;
  545. }
  546. int32_t BattleInfo::getEnchanterCounter(BattleSide side) const
  547. {
  548. return getSide(side).enchanterCounter;
  549. }
  550. const IBonusBearer * BattleInfo::getBonusBearer() const
  551. {
  552. return this;
  553. }
  554. int64_t BattleInfo::getActualDamage(const DamageRange & damage, int32_t attackerCount, vstd::RNG & rng) const
  555. {
  556. if(damage.min != damage.max)
  557. {
  558. int64_t sum = 0;
  559. auto howManyToAv = std::min<int32_t>(10, attackerCount);
  560. for(int32_t g = 0; g < howManyToAv; ++g)
  561. sum += rng.nextInt64(damage.min, damage.max);
  562. return sum / howManyToAv;
  563. }
  564. else
  565. {
  566. return damage.min;
  567. }
  568. }
  569. int3 BattleInfo::getLocation() const
  570. {
  571. return tile;
  572. }
  573. bool BattleInfo::isCreatureBank() const
  574. {
  575. return creatureBank;
  576. }
  577. std::vector<SpellID> BattleInfo::getUsedSpells(BattleSide side) const
  578. {
  579. return getSide(side).usedSpellsHistory;
  580. }
  581. void BattleInfo::nextRound()
  582. {
  583. for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER})
  584. {
  585. sides.at(i).castSpellsCount = 0;
  586. vstd::amax(--sides.at(i).enchanterCounter, 0);
  587. }
  588. round += 1;
  589. for(CStack * s : stacks)
  590. {
  591. // new turn effects
  592. s->reduceBonusDurations(Bonus::NTurns);
  593. s->afterNewRound();
  594. }
  595. for(auto & obst : obstacles)
  596. obst->battleTurnPassed();
  597. }
  598. void BattleInfo::nextTurn(uint32_t unitId)
  599. {
  600. activeStack = unitId;
  601. CStack * st = getStack(activeStack);
  602. //remove bonuses that last until when stack gets new turn
  603. st->removeBonusesRecursive(Bonus::UntilGetsTurn);
  604. st->afterGetsTurn();
  605. }
  606. void BattleInfo::addUnit(uint32_t id, const JsonNode & data)
  607. {
  608. battle::UnitInfo info;
  609. info.load(id, data);
  610. CStackBasicDescriptor base(info.type, info.count);
  611. PlayerColor owner = getSidePlayer(info.side);
  612. auto * ret = new CStack(&base, owner, info.id, info.side, SlotID::SUMMONED_SLOT_PLACEHOLDER);
  613. ret->initialPosition = info.position;
  614. stacks.push_back(ret);
  615. ret->localInit(this);
  616. ret->summoned = info.summoned;
  617. }
  618. void BattleInfo::moveUnit(uint32_t id, BattleHex destination)
  619. {
  620. auto * sta = getStack(id);
  621. if(!sta)
  622. {
  623. logGlobal->error("Cannot find stack %d", id);
  624. return;
  625. }
  626. sta->position = destination;
  627. //Bonuses can be limited by unit placement, so, change tree version
  628. //to force updating a bonus. TODO: update version only when such bonuses are present
  629. CBonusSystemNode::treeHasChanged();
  630. }
  631. void BattleInfo::setUnitState(uint32_t id, const JsonNode & data, int64_t healthDelta)
  632. {
  633. CStack * changedStack = getStack(id, false);
  634. if(!changedStack)
  635. throw std::runtime_error("Invalid unit id in BattleInfo update");
  636. if(!changedStack->alive() && healthDelta > 0)
  637. {
  638. //checking if we resurrect a stack that is under a living stack
  639. auto accessibility = getAccessibility();
  640. if(!accessibility.accessible(changedStack->getPosition(), changedStack))
  641. {
  642. logNetwork->error("Cannot resurrect %s because hex %d is occupied!", changedStack->nodeName(), changedStack->getPosition().hex);
  643. return; //position is already occupied
  644. }
  645. }
  646. bool killed = (-healthDelta) >= changedStack->getAvailableHealth();//todo: check using alive state once rebirth will be handled separately
  647. bool resurrected = !changedStack->alive() && healthDelta > 0;
  648. //applying changes
  649. changedStack->load(data);
  650. if(healthDelta < 0)
  651. {
  652. changedStack->removeBonusesRecursive(Bonus::UntilBeingAttacked);
  653. }
  654. resurrected = resurrected || (killed && changedStack->alive());
  655. if(killed)
  656. {
  657. if(changedStack->cloneID >= 0)
  658. {
  659. //remove clone as well
  660. CStack * clone = getStack(changedStack->cloneID);
  661. if(clone)
  662. clone->makeGhost();
  663. changedStack->cloneID = -1;
  664. }
  665. }
  666. if(resurrected || killed)
  667. {
  668. //removing all spells effects
  669. auto selector = [](const Bonus * b)
  670. {
  671. //Special case: DISRUPTING_RAY is absolutely permanent
  672. return b->source == BonusSource::SPELL_EFFECT && b->sid.as<SpellID>() != SpellID::DISRUPTING_RAY;
  673. };
  674. changedStack->removeBonusesRecursive(selector);
  675. }
  676. if(!changedStack->alive() && changedStack->isClone())
  677. {
  678. for(CStack * s : stacks)
  679. {
  680. if(s->cloneID == changedStack->unitId())
  681. s->cloneID = -1;
  682. }
  683. }
  684. }
  685. void BattleInfo::removeUnit(uint32_t id)
  686. {
  687. std::set<uint32_t> ids;
  688. ids.insert(id);
  689. while(!ids.empty())
  690. {
  691. auto toRemoveId = *ids.begin();
  692. auto * toRemove = getStack(toRemoveId, false);
  693. if(!toRemove)
  694. {
  695. logGlobal->error("Cannot find stack %d", toRemoveId);
  696. return;
  697. }
  698. if(!toRemove->ghost)
  699. {
  700. toRemove->onRemoved();
  701. toRemove->detachFromAll();
  702. //stack may be removed instantly (not being killed first)
  703. //handle clone remove also here
  704. if(toRemove->cloneID >= 0)
  705. {
  706. ids.insert(toRemove->cloneID);
  707. toRemove->cloneID = -1;
  708. }
  709. //cleanup remaining clone links if any
  710. for(auto * s : stacks)
  711. {
  712. if(s->cloneID == toRemoveId)
  713. s->cloneID = -1;
  714. }
  715. }
  716. ids.erase(toRemoveId);
  717. }
  718. }
  719. void BattleInfo::updateUnit(uint32_t id, const JsonNode & data)
  720. {
  721. //TODO
  722. }
  723. void BattleInfo::addUnitBonus(uint32_t id, const std::vector<Bonus> & bonus)
  724. {
  725. CStack * sta = getStack(id, false);
  726. if(!sta)
  727. {
  728. logGlobal->error("Cannot find stack %d", id);
  729. return;
  730. }
  731. for(const Bonus & b : bonus)
  732. addOrUpdateUnitBonus(sta, b, true);
  733. }
  734. void BattleInfo::updateUnitBonus(uint32_t id, const std::vector<Bonus> & bonus)
  735. {
  736. CStack * sta = getStack(id, false);
  737. if(!sta)
  738. {
  739. logGlobal->error("Cannot find stack %d", id);
  740. return;
  741. }
  742. for(const Bonus & b : bonus)
  743. addOrUpdateUnitBonus(sta, b, false);
  744. }
  745. void BattleInfo::removeUnitBonus(uint32_t id, const std::vector<Bonus> & bonus)
  746. {
  747. CStack * sta = getStack(id, false);
  748. if(!sta)
  749. {
  750. logGlobal->error("Cannot find stack %d", id);
  751. return;
  752. }
  753. for(const Bonus & one : bonus)
  754. {
  755. auto selector = [one](const Bonus * b)
  756. {
  757. //compare everything but turnsRemain, limiter and propagator
  758. return one.duration == b->duration
  759. && one.type == b->type
  760. && one.subtype == b->subtype
  761. && one.source == b->source
  762. && one.val == b->val
  763. && one.sid == b->sid
  764. && one.valType == b->valType
  765. && one.additionalInfo == b->additionalInfo
  766. && one.effectRange == b->effectRange;
  767. };
  768. sta->removeBonusesRecursive(selector);
  769. }
  770. }
  771. uint32_t BattleInfo::nextUnitId() const
  772. {
  773. return static_cast<uint32_t>(stacks.size());
  774. }
  775. void BattleInfo::addOrUpdateUnitBonus(CStack * sta, const Bonus & value, bool forceAdd)
  776. {
  777. if(forceAdd || !sta->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, value.sid).And(Selector::typeSubtypeValueType(value.type, value.subtype, value.valType))))
  778. {
  779. //no such effect or cumulative - add new
  780. logBonus->trace("%s receives a new bonus: %s", sta->nodeName(), value.Description());
  781. sta->addNewBonus(std::make_shared<Bonus>(value));
  782. }
  783. else
  784. {
  785. logBonus->trace("%s updated bonus: %s", sta->nodeName(), value.Description());
  786. for(const auto & stackBonus : sta->getExportedBonusList()) //TODO: optimize
  787. {
  788. if(stackBonus->source == value.source && stackBonus->sid == value.sid && stackBonus->type == value.type && stackBonus->subtype == value.subtype && stackBonus->valType == value.valType)
  789. {
  790. stackBonus->turnsRemain = std::max(stackBonus->turnsRemain, value.turnsRemain);
  791. }
  792. }
  793. CBonusSystemNode::treeHasChanged();
  794. }
  795. }
  796. void BattleInfo::setWallState(EWallPart partOfWall, EWallState state)
  797. {
  798. si.wallState[partOfWall] = state;
  799. }
  800. void BattleInfo::addObstacle(const ObstacleChanges & changes)
  801. {
  802. auto obstacle = std::make_shared<SpellCreatedObstacle>();
  803. obstacle->fromInfo(changes);
  804. obstacles.push_back(obstacle);
  805. }
  806. void BattleInfo::updateObstacle(const ObstacleChanges& changes)
  807. {
  808. auto changedObstacle = std::make_shared<SpellCreatedObstacle>();
  809. changedObstacle->fromInfo(changes);
  810. for(auto & obstacle : obstacles)
  811. {
  812. if(obstacle->uniqueID == changes.id) // update this obstacle
  813. {
  814. auto * spellObstacle = dynamic_cast<SpellCreatedObstacle *>(obstacle.get());
  815. assert(spellObstacle);
  816. // Currently we only support to update the "revealed" property
  817. spellObstacle->revealed = changedObstacle->revealed;
  818. break;
  819. }
  820. }
  821. }
  822. void BattleInfo::removeObstacle(uint32_t id)
  823. {
  824. for(int i=0; i < obstacles.size(); ++i)
  825. {
  826. if(obstacles[i]->uniqueID == id) //remove this obstacle
  827. {
  828. obstacles.erase(obstacles.begin() + i);
  829. break;
  830. }
  831. }
  832. }
  833. CArmedInstance * BattleInfo::battleGetArmyObject(BattleSide side) const
  834. {
  835. return const_cast<CArmedInstance*>(CBattleInfoEssentials::battleGetArmyObject(side));
  836. }
  837. CGHeroInstance * BattleInfo::battleGetFightingHero(BattleSide side) const
  838. {
  839. return const_cast<CGHeroInstance*>(CBattleInfoEssentials::battleGetFightingHero(side));
  840. }
  841. #if SCRIPTING_ENABLED
  842. scripting::Pool * BattleInfo::getContextPool() const
  843. {
  844. //this is real battle, use global scripting context pool
  845. //TODO: make this line not ugly
  846. return battleGetFightingHero(BattleSide::ATTACKER)->cb->getGlobalContextPool();
  847. }
  848. #endif
  849. bool CMP_stack::operator()(const battle::Unit * a, const battle::Unit * b) const
  850. {
  851. switch(phase)
  852. {
  853. case 0: //catapult moves after turrets
  854. return a->creatureIndex() > b->creatureIndex(); //catapult is 145 and turrets are 149
  855. case 1:
  856. case 2:
  857. case 3:
  858. {
  859. int as = a->getInitiative(turn);
  860. int bs = b->getInitiative(turn);
  861. if(as != bs)
  862. return as > bs;
  863. if(a->unitSide() == b->unitSide())
  864. return a->unitSlot() < b->unitSlot();
  865. return (a->unitSide() == side || b->unitSide() == side)
  866. ? a->unitSide() != side
  867. : a->unitSide() < b->unitSide();
  868. }
  869. default:
  870. assert(false);
  871. return false;
  872. }
  873. assert(false);
  874. return false;
  875. }
  876. CMP_stack::CMP_stack(int Phase, int Turn, BattleSide Side):
  877. phase(Phase),
  878. turn(Turn),
  879. side(Side)
  880. {
  881. }
  882. VCMI_LIB_NAMESPACE_END