BattleInfo.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056
  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 "bonuses/Limiters.h"
  13. #include "bonuses/Updaters.h"
  14. #include "../CStack.h"
  15. #include "../CHeroHandler.h"
  16. #include "../NetPacks.h"
  17. #include "../filesystem/Filesystem.h"
  18. #include "../mapObjects/CGTownInstance.h"
  19. #include "../CGeneralTextHandler.h"
  20. #include "../BattleFieldHandler.h"
  21. #include "../ObstacleHandler.h"
  22. //TODO: remove
  23. #include "../IGameCallback.h"
  24. VCMI_LIB_NAMESPACE_BEGIN
  25. ///BattleInfo
  26. std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const battle::Unit * stack)
  27. {
  28. auto reachability = getReachability(stack);
  29. if(reachability.predecessors[dest] == -1) //cannot reach destination
  30. {
  31. return std::make_pair(std::vector<BattleHex>(), 0);
  32. }
  33. //making the Path
  34. std::vector<BattleHex> path;
  35. BattleHex curElem = dest;
  36. while(curElem != start)
  37. {
  38. path.push_back(curElem);
  39. curElem = reachability.predecessors[curElem];
  40. }
  41. return std::make_pair(path, reachability.distances[dest]);
  42. }
  43. void BattleInfo::calculateCasualties(std::map<ui32,si32> * casualties) const
  44. {
  45. for(const auto & st : stacks) //setting casualties
  46. {
  47. si32 killed = st->getKilled();
  48. if(killed > 0)
  49. casualties[st->unitSide()][st->creatureId()] += killed;
  50. }
  51. }
  52. CStack * BattleInfo::generateNewStack(uint32_t id, const CStackInstance & base, ui8 side, const SlotID & slot, BattleHex position)
  53. {
  54. PlayerColor owner = sides[side].color;
  55. assert((owner >= PlayerColor::PLAYER_LIMIT) ||
  56. (base.armyObj && base.armyObj->tempOwner == owner));
  57. auto * ret = new CStack(&base, owner, id, side, slot);
  58. ret->initialPosition = getAvaliableHex(base.getCreatureID(), side, position); //TODO: what if no free tile on battlefield was found?
  59. stacks.push_back(ret);
  60. return ret;
  61. }
  62. CStack * BattleInfo::generateNewStack(uint32_t id, const CStackBasicDescriptor & base, ui8 side, const SlotID & slot, BattleHex position)
  63. {
  64. PlayerColor owner = sides[side].color;
  65. auto * ret = new CStack(&base, owner, id, side, slot);
  66. ret->initialPosition = position;
  67. stacks.push_back(ret);
  68. return ret;
  69. }
  70. void BattleInfo::localInit()
  71. {
  72. for(int i = 0; i < 2; i++)
  73. {
  74. auto * armyObj = battleGetArmyObject(i);
  75. armyObj->battle = this;
  76. armyObj->attachTo(*this);
  77. }
  78. for(CStack * s : stacks)
  79. s->localInit(this);
  80. exportBonuses();
  81. }
  82. namespace CGH
  83. {
  84. static void readBattlePositions(const JsonNode &node, std::vector< std::vector<int> > & dest)
  85. {
  86. for(const JsonNode &level : node.Vector())
  87. {
  88. std::vector<int> pom;
  89. for(const JsonNode &value : level.Vector())
  90. {
  91. pom.push_back(static_cast<int>(value.Float()));
  92. }
  93. dest.push_back(pom);
  94. }
  95. }
  96. }
  97. //RNG that works like H3 one
  98. struct RandGen
  99. {
  100. ui32 seed;
  101. void srand(ui32 s)
  102. {
  103. seed = s;
  104. }
  105. void srand(const int3 & pos)
  106. {
  107. srand(110291 * static_cast<ui32>(pos.x) + 167801 * static_cast<ui32>(pos.y) + 81569);
  108. }
  109. int rand()
  110. {
  111. seed = 214013 * seed + 2531011;
  112. return (seed >> 16) & 0x7FFF;
  113. }
  114. int rand(int min, int max)
  115. {
  116. if(min == max)
  117. return min;
  118. if(min > max)
  119. return min;
  120. return min + rand() % (max - min + 1);
  121. }
  122. };
  123. struct RangeGenerator
  124. {
  125. class ExhaustedPossibilities : public std::exception
  126. {
  127. };
  128. RangeGenerator(int _min, int _max, std::function<int()> _myRand):
  129. min(_min),
  130. remainingCount(_max - _min + 1),
  131. remaining(remainingCount, true),
  132. myRand(std::move(_myRand))
  133. {
  134. }
  135. int generateNumber() const
  136. {
  137. if(!remainingCount)
  138. throw ExhaustedPossibilities();
  139. if(remainingCount == 1)
  140. return 0;
  141. return myRand() % remainingCount;
  142. }
  143. //get number fulfilling predicate. Never gives the same number twice.
  144. int getSuchNumber(const std::function<bool(int)> & goodNumberPred = nullptr)
  145. {
  146. int ret = -1;
  147. do
  148. {
  149. int n = generateNumber();
  150. int i = 0;
  151. for(;;i++)
  152. {
  153. assert(i < (int)remaining.size());
  154. if(!remaining[i])
  155. continue;
  156. if(!n)
  157. break;
  158. n--;
  159. }
  160. remainingCount--;
  161. remaining[i] = false;
  162. ret = i + min;
  163. } while(goodNumberPred && !goodNumberPred(ret));
  164. return ret;
  165. }
  166. int min, remainingCount;
  167. std::vector<bool> remaining;
  168. std::function<int()> myRand;
  169. };
  170. BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const BattleField & battlefieldType, const CArmedInstance * armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance * town)
  171. {
  172. CMP_stack cmpst;
  173. auto * curB = new BattleInfo();
  174. for(auto i = 0u; i < curB->sides.size(); i++)
  175. curB->sides[i].init(heroes[i], armies[i]);
  176. std::vector<CStack*> & stacks = (curB->stacks);
  177. curB->tile = tile;
  178. curB->battlefieldType = battlefieldType;
  179. curB->round = -2;
  180. curB->activeStack = -1;
  181. curB->creatureBank = creatureBank;
  182. curB->replayAllowed = false;
  183. if(town)
  184. {
  185. curB->town = town;
  186. curB->terrainType = town->getNativeTerrain();
  187. }
  188. else
  189. {
  190. curB->town = nullptr;
  191. curB->terrainType = terrain;
  192. }
  193. //setting up siege obstacles
  194. if (town && town->hasFort())
  195. {
  196. curB->si.gateState = EGateState::CLOSED;
  197. curB->si.wallState[EWallPart::GATE] = EWallState::INTACT;
  198. for(const auto wall : {EWallPart::BOTTOM_WALL, EWallPart::BELOW_GATE, EWallPart::OVER_GATE, EWallPart::UPPER_WALL})
  199. {
  200. if (town->hasBuilt(BuildingID::CASTLE))
  201. curB->si.wallState[wall] = EWallState::REINFORCED;
  202. else
  203. curB->si.wallState[wall] = EWallState::INTACT;
  204. }
  205. if (town->hasBuilt(BuildingID::CITADEL))
  206. curB->si.wallState[EWallPart::KEEP] = EWallState::INTACT;
  207. if (town->hasBuilt(BuildingID::CASTLE))
  208. {
  209. curB->si.wallState[EWallPart::UPPER_TOWER] = EWallState::INTACT;
  210. curB->si.wallState[EWallPart::BOTTOM_TOWER] = EWallState::INTACT;
  211. }
  212. }
  213. //randomize obstacles
  214. if (town == nullptr && !creatureBank) //do it only when it's not siege and not creature bank
  215. {
  216. RandGen r{};
  217. auto ourRand = [&](){ return r.rand(); };
  218. r.srand(tile);
  219. r.rand(1,8); //battle sound ID to play... can't do anything with it here
  220. int tilesToBlock = r.rand(5,12);
  221. std::vector<BattleHex> blockedTiles;
  222. auto appropriateAbsoluteObstacle = [&](int id)
  223. {
  224. const auto * info = Obstacle(id).getInfo();
  225. return info && info->isAbsoluteObstacle && info->isAppropriate(curB->terrainType, battlefieldType);
  226. };
  227. auto appropriateUsualObstacle = [&](int id)
  228. {
  229. const auto * info = Obstacle(id).getInfo();
  230. return info && !info->isAbsoluteObstacle && info->isAppropriate(curB->terrainType, battlefieldType);
  231. };
  232. if(r.rand(1,100) <= 40) //put cliff-like obstacle
  233. {
  234. try
  235. {
  236. RangeGenerator obidgen(0, VLC->obstacleHandler->objects.size() - 1, ourRand);
  237. auto obstPtr = std::make_shared<CObstacleInstance>();
  238. obstPtr->obstacleType = CObstacleInstance::ABSOLUTE_OBSTACLE;
  239. obstPtr->ID = obidgen.getSuchNumber(appropriateAbsoluteObstacle);
  240. obstPtr->uniqueID = static_cast<si32>(curB->obstacles.size());
  241. curB->obstacles.push_back(obstPtr);
  242. for(BattleHex blocked : obstPtr->getBlockedTiles())
  243. blockedTiles.push_back(blocked);
  244. tilesToBlock -= Obstacle(obstPtr->ID).getInfo()->blockedTiles.size() / 2;
  245. }
  246. catch(RangeGenerator::ExhaustedPossibilities &)
  247. {
  248. //silently ignore, if we can't place absolute obstacle, we'll go with the usual ones
  249. logGlobal->debug("RangeGenerator::ExhaustedPossibilities exception occured - cannot place absolute obstacle");
  250. }
  251. }
  252. try
  253. {
  254. while(tilesToBlock > 0)
  255. {
  256. RangeGenerator obidgen(0, VLC->obstacleHandler->objects.size() - 1, ourRand);
  257. auto tileAccessibility = curB->getAccesibility();
  258. const int obid = obidgen.getSuchNumber(appropriateUsualObstacle);
  259. const ObstacleInfo &obi = *Obstacle(obid).getInfo();
  260. auto validPosition = [&](BattleHex pos) -> bool
  261. {
  262. if(obi.height >= pos.getY())
  263. return false;
  264. if(pos.getX() == 0)
  265. return false;
  266. if(pos.getX() + obi.width > 15)
  267. return false;
  268. if(vstd::contains(blockedTiles, pos))
  269. return false;
  270. for(BattleHex blocked : obi.getBlocked(pos))
  271. {
  272. if(tileAccessibility[blocked] == EAccessibility::UNAVAILABLE) //for ship-to-ship battlefield - exclude hardcoded unavailable tiles
  273. return false;
  274. if(vstd::contains(blockedTiles, blocked))
  275. return false;
  276. int x = blocked.getX();
  277. if(x <= 2 || x >= 14)
  278. return false;
  279. }
  280. return true;
  281. };
  282. RangeGenerator posgenerator(18, 168, ourRand);
  283. auto obstPtr = std::make_shared<CObstacleInstance>();
  284. obstPtr->ID = obid;
  285. obstPtr->pos = posgenerator.getSuchNumber(validPosition);
  286. obstPtr->uniqueID = static_cast<si32>(curB->obstacles.size());
  287. curB->obstacles.push_back(obstPtr);
  288. for(BattleHex blocked : obstPtr->getBlockedTiles())
  289. blockedTiles.push_back(blocked);
  290. tilesToBlock -= static_cast<int>(obi.blockedTiles.size());
  291. }
  292. }
  293. catch(RangeGenerator::ExhaustedPossibilities &)
  294. {
  295. logGlobal->debug("RangeGenerator::ExhaustedPossibilities exception occured - cannot place usual obstacle");
  296. }
  297. }
  298. //reading battleStartpos - add creatures AFTER random obstacles are generated
  299. //TODO: parse once to some structure
  300. std::vector<std::vector<int>> looseFormations[2];
  301. std::vector<std::vector<int>> tightFormations[2];
  302. std::vector<std::vector<int>> creBankFormations[2];
  303. std::vector<int> commanderField;
  304. std::vector<int> commanderBank;
  305. const JsonNode config(ResourceID("config/battleStartpos.json"));
  306. const JsonVector &positions = config["battle_positions"].Vector();
  307. CGH::readBattlePositions(positions[0]["levels"], looseFormations[0]);
  308. CGH::readBattlePositions(positions[1]["levels"], looseFormations[1]);
  309. CGH::readBattlePositions(positions[2]["levels"], tightFormations[0]);
  310. CGH::readBattlePositions(positions[3]["levels"], tightFormations[1]);
  311. CGH::readBattlePositions(positions[4]["levels"], creBankFormations[0]);
  312. CGH::readBattlePositions(positions[5]["levels"], creBankFormations[1]);
  313. for (auto position : config["commanderPositions"]["field"].Vector())
  314. {
  315. commanderField.push_back(static_cast<int>(position.Float()));
  316. }
  317. for (auto position : config["commanderPositions"]["creBank"].Vector())
  318. {
  319. commanderBank.push_back(static_cast<int>(position.Float()));
  320. }
  321. //adding war machines
  322. if(!creatureBank)
  323. {
  324. //Checks if hero has artifact and create appropriate stack
  325. auto handleWarMachine = [&](int side, const ArtifactPosition & artslot, BattleHex hex)
  326. {
  327. const CArtifactInstance * warMachineArt = heroes[side]->getArt(artslot);
  328. if(nullptr != warMachineArt)
  329. {
  330. CreatureID cre = warMachineArt->artType->getWarMachine();
  331. if(cre != CreatureID::NONE)
  332. curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(cre, 1), side, SlotID::WAR_MACHINES_SLOT, hex);
  333. }
  334. };
  335. if(heroes[0])
  336. {
  337. handleWarMachine(0, ArtifactPosition::MACH1, 52);
  338. handleWarMachine(0, ArtifactPosition::MACH2, 18);
  339. handleWarMachine(0, ArtifactPosition::MACH3, 154);
  340. if(town && town->hasFort())
  341. handleWarMachine(0, ArtifactPosition::MACH4, 120);
  342. }
  343. if(heroes[1])
  344. {
  345. if(!town) //defending hero shouldn't receive ballista (bug #551)
  346. handleWarMachine(1, ArtifactPosition::MACH1, 66);
  347. handleWarMachine(1, ArtifactPosition::MACH2, 32);
  348. handleWarMachine(1, ArtifactPosition::MACH3, 168);
  349. }
  350. }
  351. //war machines added
  352. //battleStartpos read
  353. for(int side = 0; side < 2; side++)
  354. {
  355. int formationNo = armies[side]->stacksCount() - 1;
  356. vstd::abetween(formationNo, 0, GameConstants::ARMY_SIZE - 1);
  357. int k = 0; //stack serial
  358. for(auto i = armies[side]->Slots().begin(); i != armies[side]->Slots().end(); i++, k++)
  359. {
  360. std::vector<int> *formationVector = nullptr;
  361. if(armies[side]->formation == EArmyFormation::TIGHT )
  362. formationVector = &tightFormations[side][formationNo];
  363. else
  364. formationVector = &looseFormations[side][formationNo];
  365. if(creatureBank)
  366. formationVector = &creBankFormations[side][formationNo];
  367. BattleHex pos = (k < formationVector->size() ? formationVector->at(k) : 0);
  368. if(creatureBank && i->second->type->isDoubleWide())
  369. pos += side ? BattleHex::LEFT : BattleHex::RIGHT;
  370. curB->generateNewStack(curB->nextUnitId(), *i->second, side, i->first, pos);
  371. }
  372. }
  373. //adding commanders
  374. for (int i = 0; i < 2; ++i)
  375. {
  376. if (heroes[i] && heroes[i]->commander && heroes[i]->commander->alive)
  377. {
  378. curB->generateNewStack(curB->nextUnitId(), *heroes[i]->commander, i, SlotID::COMMANDER_SLOT_PLACEHOLDER, creatureBank ? commanderBank[i] : commanderField[i]);
  379. }
  380. }
  381. if (curB->town && curB->town->fortLevel() >= CGTownInstance::CITADEL)
  382. {
  383. // keep tower
  384. curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_CENTRAL_TOWER);
  385. if (curB->town->fortLevel() >= CGTownInstance::CASTLE)
  386. {
  387. // lower tower + upper tower
  388. curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_UPPER_TOWER);
  389. curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_BOTTOM_TOWER);
  390. }
  391. //Moat generating is done on server
  392. }
  393. std::stable_sort(stacks.begin(),stacks.end(),cmpst);
  394. auto neutral = std::make_shared<CreatureAlignmentLimiter>(EAlignment::NEUTRAL);
  395. auto good = std::make_shared<CreatureAlignmentLimiter>(EAlignment::GOOD);
  396. auto evil = std::make_shared<CreatureAlignmentLimiter>(EAlignment::EVIL);
  397. const auto * bgInfo = VLC->battlefields()->getById(battlefieldType);
  398. for(const std::shared_ptr<Bonus> & bonus : bgInfo->bonuses)
  399. {
  400. curB->addNewBonus(bonus);
  401. }
  402. //native terrain bonuses
  403. static auto nativeTerrain = std::make_shared<CreatureTerrainLimiter>();
  404. curB->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::STACKS_SPEED, BonusSource::TERRAIN_NATIVE, 1, 0, 0)->addLimiter(nativeTerrain));
  405. curB->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, 0, PrimarySkill::ATTACK)->addLimiter(nativeTerrain));
  406. curB->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, 0, PrimarySkill::DEFENSE)->addLimiter(nativeTerrain));
  407. //////////////////////////////////////////////////////////////////////////
  408. //tactics
  409. bool isTacticsAllowed = !creatureBank; //no tactics in creature banks
  410. constexpr int sideSize = 2;
  411. std::array<int, sideSize> battleRepositionHex = {};
  412. std::array<int, sideSize> battleRepositionHexBlock = {};
  413. for(int i = 0; i < sideSize; i++)
  414. {
  415. if(heroes[i])
  416. {
  417. battleRepositionHex[i] += heroes[i]->valOfBonuses(Selector::type()(BonusType::BEFORE_BATTLE_REPOSITION));
  418. battleRepositionHexBlock[i] += heroes[i]->valOfBonuses(Selector::type()(BonusType::BEFORE_BATTLE_REPOSITION_BLOCK));
  419. }
  420. }
  421. int tacticsSkillDiffAttacker = battleRepositionHex[BattleSide::ATTACKER] - battleRepositionHexBlock[BattleSide::DEFENDER];
  422. int tacticsSkillDiffDefender = battleRepositionHex[BattleSide::DEFENDER] - battleRepositionHexBlock[BattleSide::ATTACKER];
  423. /* for current tactics, we need to choose one side, so, we will choose side when first - second > 0, and ignore sides
  424. when first - second <= 0. If there will be situations when both > 0, attacker will be chosen. Anyway, in OH3 this
  425. will not happen because tactics block opposite tactics on same value.
  426. TODO: For now, it is an error to use BEFORE_BATTLE_REPOSITION bonus without counterpart, but it can be changed if
  427. double tactics will be implemented.
  428. */
  429. if(isTacticsAllowed)
  430. {
  431. if(tacticsSkillDiffAttacker > 0 && tacticsSkillDiffDefender > 0)
  432. logGlobal->warn("Double tactics is not implemented, only attacker will have tactics!");
  433. if(tacticsSkillDiffAttacker > 0)
  434. {
  435. curB->tacticsSide = BattleSide::ATTACKER;
  436. //bonus specifies distance you can move beyond base row; this allows 100% compatibility with HMM3 mechanics
  437. curB->tacticDistance = 1 + tacticsSkillDiffAttacker;
  438. }
  439. else if(tacticsSkillDiffDefender > 0)
  440. {
  441. curB->tacticsSide = BattleSide::DEFENDER;
  442. //bonus specifies distance you can move beyond base row; this allows 100% compatibility with HMM3 mechanics
  443. curB->tacticDistance = 1 + tacticsSkillDiffDefender;
  444. }
  445. else
  446. curB->tacticDistance = 0;
  447. }
  448. return curB;
  449. }
  450. const CGHeroInstance * BattleInfo::getHero(const PlayerColor & player) const
  451. {
  452. for(const auto & side : sides)
  453. if(side.color == player)
  454. return side.hero;
  455. logGlobal->error("Player %s is not in battle!", player.getStr());
  456. return nullptr;
  457. }
  458. ui8 BattleInfo::whatSide(const PlayerColor & player) const
  459. {
  460. for(int i = 0; i < sides.size(); i++)
  461. if(sides[i].color == player)
  462. return i;
  463. logGlobal->warn("BattleInfo::whatSide: Player %s is not in battle!", player.getStr());
  464. return -1;
  465. }
  466. CStack * BattleInfo::getStack(int stackID, bool onlyAlive)
  467. {
  468. return const_cast<CStack *>(battleGetStackByID(stackID, onlyAlive));
  469. }
  470. BattleInfo::BattleInfo():
  471. round(-1),
  472. activeStack(-1),
  473. town(nullptr),
  474. tile(-1,-1,-1),
  475. battlefieldType(BattleField::NONE),
  476. tacticsSide(0),
  477. tacticDistance(0)
  478. {
  479. setBattle(this);
  480. setNodeType(BATTLE);
  481. }
  482. BattleInfo::~BattleInfo()
  483. {
  484. for (auto & elem : stacks)
  485. delete elem;
  486. for(int i = 0; i < 2; i++)
  487. if(auto * _armyObj = battleGetArmyObject(i))
  488. _armyObj->battle = nullptr;
  489. }
  490. int32_t BattleInfo::getActiveStackID() const
  491. {
  492. return activeStack;
  493. }
  494. TStacks BattleInfo::getStacksIf(TStackFilter predicate) const
  495. {
  496. TStacks ret;
  497. vstd::copy_if(stacks, std::back_inserter(ret), predicate);
  498. return ret;
  499. }
  500. battle::Units BattleInfo::getUnitsIf(battle::UnitFilter predicate) const
  501. {
  502. battle::Units ret;
  503. vstd::copy_if(stacks, std::back_inserter(ret), predicate);
  504. return ret;
  505. }
  506. BattleField BattleInfo::getBattlefieldType() const
  507. {
  508. return battlefieldType;
  509. }
  510. TerrainId BattleInfo::getTerrainType() const
  511. {
  512. return terrainType;
  513. }
  514. IBattleInfo::ObstacleCList BattleInfo::getAllObstacles() const
  515. {
  516. ObstacleCList ret;
  517. for(const auto & obstacle : obstacles)
  518. ret.push_back(obstacle);
  519. return ret;
  520. }
  521. PlayerColor BattleInfo::getSidePlayer(ui8 side) const
  522. {
  523. return sides.at(side).color;
  524. }
  525. const CArmedInstance * BattleInfo::getSideArmy(ui8 side) const
  526. {
  527. return sides.at(side).armyObject;
  528. }
  529. const CGHeroInstance * BattleInfo::getSideHero(ui8 side) const
  530. {
  531. return sides.at(side).hero;
  532. }
  533. ui8 BattleInfo::getTacticDist() const
  534. {
  535. return tacticDistance;
  536. }
  537. ui8 BattleInfo::getTacticsSide() const
  538. {
  539. return tacticsSide;
  540. }
  541. const CGTownInstance * BattleInfo::getDefendedTown() const
  542. {
  543. return town;
  544. }
  545. EWallState BattleInfo::getWallState(EWallPart partOfWall) const
  546. {
  547. return si.wallState.at(partOfWall);
  548. }
  549. EGateState BattleInfo::getGateState() const
  550. {
  551. return si.gateState;
  552. }
  553. uint32_t BattleInfo::getCastSpells(ui8 side) const
  554. {
  555. return sides.at(side).castSpellsCount;
  556. }
  557. int32_t BattleInfo::getEnchanterCounter(ui8 side) const
  558. {
  559. return sides.at(side).enchanterCounter;
  560. }
  561. const IBonusBearer * BattleInfo::getBonusBearer() const
  562. {
  563. return this;
  564. }
  565. int64_t BattleInfo::getActualDamage(const DamageRange & damage, int32_t attackerCount, vstd::RNG & rng) const
  566. {
  567. if(damage.min != damage.max)
  568. {
  569. int64_t sum = 0;
  570. auto howManyToAv = std::min<int32_t>(10, attackerCount);
  571. auto rangeGen = rng.getInt64Range(damage.min, damage.max);
  572. for(int32_t g = 0; g < howManyToAv; ++g)
  573. sum += rangeGen();
  574. return sum / howManyToAv;
  575. }
  576. else
  577. {
  578. return damage.min;
  579. }
  580. }
  581. void BattleInfo::nextRound(int32_t roundNr)
  582. {
  583. for(int i = 0; i < 2; ++i)
  584. {
  585. sides.at(i).castSpellsCount = 0;
  586. vstd::amax(--sides.at(i).enchanterCounter, 0);
  587. }
  588. round = roundNr;
  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 = getAccesibility();
  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 != 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. && one.description == b->description;
  768. };
  769. sta->removeBonusesRecursive(selector);
  770. }
  771. }
  772. uint32_t BattleInfo::nextUnitId() const
  773. {
  774. return static_cast<uint32_t>(stacks.size());
  775. }
  776. void BattleInfo::addOrUpdateUnitBonus(CStack * sta, const Bonus & value, bool forceAdd)
  777. {
  778. if(forceAdd || !sta->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, value.sid).And(Selector::typeSubtype(value.type, value.subtype))))
  779. {
  780. //no such effect or cumulative - add new
  781. logBonus->trace("%s receives a new bonus: %s", sta->nodeName(), value.Description());
  782. sta->addNewBonus(std::make_shared<Bonus>(value));
  783. }
  784. else
  785. {
  786. logBonus->trace("%s updated bonus: %s", sta->nodeName(), value.Description());
  787. for(const auto & stackBonus : sta->getExportedBonusList()) //TODO: optimize
  788. {
  789. if(stackBonus->source == value.source && stackBonus->sid == value.sid && stackBonus->type == value.type && stackBonus->subtype == value.subtype)
  790. {
  791. stackBonus->turnsRemain = std::max(stackBonus->turnsRemain, value.turnsRemain);
  792. }
  793. }
  794. CBonusSystemNode::treeHasChanged();
  795. }
  796. }
  797. void BattleInfo::setWallState(EWallPart partOfWall, EWallState state)
  798. {
  799. si.wallState[partOfWall] = state;
  800. }
  801. void BattleInfo::addObstacle(const ObstacleChanges & changes)
  802. {
  803. std::shared_ptr<SpellCreatedObstacle> obstacle = std::make_shared<SpellCreatedObstacle>();
  804. obstacle->fromInfo(changes);
  805. obstacles.push_back(obstacle);
  806. }
  807. void BattleInfo::updateObstacle(const ObstacleChanges& changes)
  808. {
  809. std::shared_ptr<SpellCreatedObstacle> changedObstacle = std::make_shared<SpellCreatedObstacle>();
  810. changedObstacle->fromInfo(changes);
  811. for(auto & obstacle : obstacles)
  812. {
  813. if(obstacle->uniqueID == changes.id) // update this obstacle
  814. {
  815. auto * spellObstacle = dynamic_cast<SpellCreatedObstacle *>(obstacle.get());
  816. assert(spellObstacle);
  817. // Currently we only support to update the "revealed" property
  818. spellObstacle->revealed = changedObstacle->revealed;
  819. break;
  820. }
  821. }
  822. }
  823. void BattleInfo::removeObstacle(uint32_t id)
  824. {
  825. for(int i=0; i < obstacles.size(); ++i)
  826. {
  827. if(obstacles[i]->uniqueID == id) //remove this obstacle
  828. {
  829. obstacles.erase(obstacles.begin() + i);
  830. break;
  831. }
  832. }
  833. }
  834. CArmedInstance * BattleInfo::battleGetArmyObject(ui8 side) const
  835. {
  836. return const_cast<CArmedInstance*>(CBattleInfoEssentials::battleGetArmyObject(side));
  837. }
  838. CGHeroInstance * BattleInfo::battleGetFightingHero(ui8 side) const
  839. {
  840. return const_cast<CGHeroInstance*>(CBattleInfoEssentials::battleGetFightingHero(side));
  841. }
  842. #if SCRIPTING_ENABLED
  843. scripting::Pool * BattleInfo::getContextPool() const
  844. {
  845. //this is real battle, use global scripting context pool
  846. //TODO: make this line not ugly
  847. return IObjectInterface::cb->getGlobalContextPool();
  848. }
  849. #endif
  850. bool CMP_stack::operator()(const battle::Unit * a, const battle::Unit * b) const
  851. {
  852. switch(phase)
  853. {
  854. case 0: //catapult moves after turrets
  855. return a->creatureIndex() > b->creatureIndex(); //catapult is 145 and turrets are 149
  856. case 1:
  857. case 2:
  858. case 3:
  859. {
  860. int as = a->getInitiative(turn);
  861. int bs = b->getInitiative(turn);
  862. if(as != bs)
  863. return as > bs;
  864. if(a->unitSide() == b->unitSide())
  865. return a->unitSlot() < b->unitSlot();
  866. return (a->unitSide() == side || b->unitSide() == side)
  867. ? a->unitSide() != side
  868. : a->unitSide() < b->unitSide();
  869. }
  870. default:
  871. assert(false);
  872. return false;
  873. }
  874. assert(false);
  875. return false;
  876. }
  877. CMP_stack::CMP_stack(int Phase, int Turn, uint8_t Side):
  878. phase(Phase),
  879. turn(Turn),
  880. side(Side)
  881. {
  882. }
  883. VCMI_LIB_NAMESPACE_END