BattleInfo.cpp 28 KB

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