CUnitState.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  1. /*
  2. * CUnitState.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 "CUnitState.h"
  12. #include "../NetPacks.h"
  13. #include "../CCreatureHandler.h"
  14. #include "../serializer/JsonDeserializer.h"
  15. #include "../serializer/JsonSerializer.h"
  16. namespace battle
  17. {
  18. CTotalsProxy::CTotalsProxy(const IBonusBearer * Target, CSelector Selector, int InitialValue)
  19. : target(Target),
  20. selector(Selector),
  21. initialValue(InitialValue),
  22. meleeCachedLast(0),
  23. meleeValue(0),
  24. rangedCachedLast(0),
  25. rangedValue(0)
  26. {
  27. }
  28. CTotalsProxy::CTotalsProxy(const CTotalsProxy & other)
  29. : target(other.target),
  30. selector(other.selector),
  31. initialValue(other.initialValue),
  32. meleeCachedLast(other.meleeCachedLast),
  33. meleeValue(other.meleeValue),
  34. rangedCachedLast(other.rangedCachedLast),
  35. rangedValue(other.rangedValue)
  36. {
  37. }
  38. CTotalsProxy & CTotalsProxy::operator=(const CTotalsProxy & other)
  39. {
  40. initialValue = other.initialValue;
  41. meleeCachedLast = other.meleeCachedLast;
  42. meleeValue = other.meleeValue;
  43. rangedCachedLast = other.rangedCachedLast;
  44. rangedValue = other.rangedValue;
  45. return *this;
  46. }
  47. int CTotalsProxy::getMeleeValue() const
  48. {
  49. static const auto limit = Selector::effectRange(Bonus::NO_LIMIT).Or(Selector::effectRange(Bonus::ONLY_MELEE_FIGHT));
  50. const auto treeVersion = target->getTreeVersion();
  51. if(treeVersion != meleeCachedLast)
  52. {
  53. auto bonuses = target->getBonuses(selector, limit);
  54. meleeValue = initialValue + bonuses->totalValue();
  55. meleeCachedLast = treeVersion;
  56. }
  57. return meleeValue;
  58. }
  59. int CTotalsProxy::getRangedValue() const
  60. {
  61. static const auto limit = Selector::effectRange(Bonus::NO_LIMIT).Or(Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT));
  62. const auto treeVersion = target->getTreeVersion();
  63. if(treeVersion != rangedCachedLast)
  64. {
  65. auto bonuses = target->getBonuses(selector, limit);
  66. rangedValue = initialValue + bonuses->totalValue();
  67. rangedCachedLast = treeVersion;
  68. }
  69. return rangedValue;
  70. }
  71. ///CCheckProxy
  72. CCheckProxy::CCheckProxy(const IBonusBearer * Target, CSelector Selector)
  73. : target(Target),
  74. selector(Selector),
  75. cachedLast(0),
  76. hasBonus(false)
  77. {
  78. }
  79. CCheckProxy::CCheckProxy(const CCheckProxy & other)
  80. : target(other.target),
  81. selector(other.selector),
  82. cachedLast(other.cachedLast),
  83. hasBonus(other.hasBonus)
  84. {
  85. }
  86. bool CCheckProxy::getHasBonus() const
  87. {
  88. const auto treeVersion = target->getTreeVersion();
  89. if(treeVersion != cachedLast)
  90. {
  91. hasBonus = target->hasBonus(selector);
  92. cachedLast = treeVersion;
  93. }
  94. return hasBonus;
  95. }
  96. ///CAmmo
  97. CAmmo::CAmmo(const battle::Unit * Owner, CSelector totalSelector)
  98. : used(0),
  99. owner(Owner),
  100. totalProxy(Owner, totalSelector)
  101. {
  102. reset();
  103. }
  104. CAmmo::CAmmo(const CAmmo & other)
  105. : used(other.used),
  106. owner(other.owner),
  107. totalProxy(other.totalProxy)
  108. {
  109. }
  110. CAmmo & CAmmo::operator= (const CAmmo & other)
  111. {
  112. used = other.used;
  113. totalProxy = other.totalProxy;
  114. return *this;
  115. }
  116. int32_t CAmmo::available() const
  117. {
  118. return total() - used;
  119. }
  120. bool CAmmo::canUse(int32_t amount) const
  121. {
  122. return !isLimited() || (available() - amount >= 0);
  123. }
  124. bool CAmmo::isLimited() const
  125. {
  126. return true;
  127. }
  128. void CAmmo::reset()
  129. {
  130. used = 0;
  131. }
  132. int32_t CAmmo::total() const
  133. {
  134. return totalProxy->totalValue();
  135. }
  136. void CAmmo::use(int32_t amount)
  137. {
  138. if(!isLimited())
  139. return;
  140. if(available() - amount < 0)
  141. {
  142. logGlobal->error("Stack ammo overuse. total: %d, used: %d, requested: %d", total(), used, amount);
  143. used += available();
  144. }
  145. else
  146. used += amount;
  147. }
  148. void CAmmo::serializeJson(JsonSerializeFormat & handler)
  149. {
  150. handler.serializeInt("used", used, 0);
  151. }
  152. ///CShots
  153. CShots::CShots(const battle::Unit * Owner)
  154. : CAmmo(Owner, Selector::type(Bonus::SHOTS)),
  155. shooter(Owner, Selector::type(Bonus::SHOOTER))
  156. {
  157. }
  158. CShots::CShots(const CShots & other)
  159. : CAmmo(other),
  160. env(other.env),
  161. shooter(other.shooter)
  162. {
  163. }
  164. CShots & CShots::operator=(const CShots & other)
  165. {
  166. CAmmo::operator=(other);
  167. shooter = other.shooter;
  168. return *this;
  169. }
  170. bool CShots::isLimited() const
  171. {
  172. return !env->unitHasAmmoCart(owner) || !shooter.getHasBonus();
  173. }
  174. void CShots::setEnv(const IUnitEnvironment * env_)
  175. {
  176. env = env_;
  177. }
  178. int32_t CShots::total() const
  179. {
  180. if(shooter.getHasBonus())
  181. return CAmmo::total();
  182. else
  183. return 0;
  184. }
  185. ///CCasts
  186. CCasts::CCasts(const battle::Unit * Owner):
  187. CAmmo(Owner, Selector::type(Bonus::CASTS))
  188. {
  189. }
  190. CCasts::CCasts(const CCasts & other)
  191. : CAmmo(other)
  192. {
  193. }
  194. CCasts & CCasts::operator=(const CCasts & other)
  195. {
  196. CAmmo::operator=(other);
  197. return *this;
  198. }
  199. ///CRetaliations
  200. CRetaliations::CRetaliations(const battle::Unit * Owner)
  201. : CAmmo(Owner, Selector::type(Bonus::ADDITIONAL_RETALIATION)),
  202. totalCache(0),
  203. noRetaliation(Owner, Selector::type(Bonus::SIEGE_WEAPON).Or(Selector::type(Bonus::HYPNOTIZED)).Or(Selector::type(Bonus::NO_RETALIATION))),
  204. unlimited(Owner, Selector::type(Bonus::UNLIMITED_RETALIATIONS))
  205. {
  206. }
  207. CRetaliations::CRetaliations(const CRetaliations & other)
  208. : CAmmo(other),
  209. totalCache(other.totalCache),
  210. noRetaliation(other.noRetaliation),
  211. unlimited(other.unlimited)
  212. {
  213. }
  214. CRetaliations & CRetaliations::operator=(const CRetaliations & other)
  215. {
  216. CAmmo::operator=(other);
  217. totalCache = other.totalCache;
  218. noRetaliation = other.noRetaliation;
  219. unlimited = other.unlimited;
  220. return *this;
  221. }
  222. bool CRetaliations::isLimited() const
  223. {
  224. return !unlimited.getHasBonus() || noRetaliation.getHasBonus();
  225. }
  226. int32_t CRetaliations::total() const
  227. {
  228. if(noRetaliation.getHasBonus())
  229. return 0;
  230. //after dispell bonus should remain during current round
  231. int32_t val = 1 + totalProxy->totalValue();
  232. vstd::amax(totalCache, val);
  233. return totalCache;
  234. }
  235. void CRetaliations::reset()
  236. {
  237. CAmmo::reset();
  238. totalCache = 0;
  239. }
  240. void CRetaliations::serializeJson(JsonSerializeFormat & handler)
  241. {
  242. CAmmo::serializeJson(handler);
  243. //we may be serialized in the middle of turn
  244. handler.serializeInt("totalCache", totalCache, 0);
  245. }
  246. ///CHealth
  247. CHealth::CHealth(const battle::Unit * Owner):
  248. owner(Owner)
  249. {
  250. reset();
  251. }
  252. CHealth::CHealth(const CHealth & other):
  253. owner(other.owner),
  254. firstHPleft(other.firstHPleft),
  255. fullUnits(other.fullUnits),
  256. resurrected(other.resurrected)
  257. {
  258. }
  259. CHealth & CHealth::operator=(const CHealth & other)
  260. {
  261. //do not change owner
  262. firstHPleft = other.firstHPleft;
  263. fullUnits = other.fullUnits;
  264. resurrected = other.resurrected;
  265. return *this;
  266. }
  267. void CHealth::init()
  268. {
  269. reset();
  270. fullUnits = owner->unitBaseAmount() > 1 ? owner->unitBaseAmount() - 1 : 0;
  271. firstHPleft = owner->unitBaseAmount() > 0 ? owner->MaxHealth() : 0;
  272. }
  273. void CHealth::addResurrected(int32_t amount)
  274. {
  275. resurrected += amount;
  276. vstd::amax(resurrected, 0);
  277. }
  278. int64_t CHealth::available() const
  279. {
  280. return static_cast<int64_t>(firstHPleft) + owner->MaxHealth() * fullUnits;
  281. }
  282. int64_t CHealth::total() const
  283. {
  284. return static_cast<int64_t>(owner->MaxHealth()) * owner->unitBaseAmount();
  285. }
  286. void CHealth::damage(int64_t & amount)
  287. {
  288. const int32_t oldCount = getCount();
  289. const bool withKills = amount >= firstHPleft;
  290. if(withKills)
  291. {
  292. int64_t totalHealth = available();
  293. if(amount > totalHealth)
  294. amount = totalHealth;
  295. totalHealth -= amount;
  296. if(totalHealth <= 0)
  297. {
  298. fullUnits = 0;
  299. firstHPleft = 0;
  300. }
  301. else
  302. {
  303. setFromTotal(totalHealth);
  304. }
  305. }
  306. else
  307. {
  308. firstHPleft -= amount;
  309. }
  310. addResurrected(getCount() - oldCount);
  311. }
  312. void CHealth::heal(int64_t & amount, EHealLevel level, EHealPower power)
  313. {
  314. const int32_t unitHealth = owner->MaxHealth();
  315. const int32_t oldCount = getCount();
  316. int64_t maxHeal = std::numeric_limits<int64_t>::max();
  317. switch(level)
  318. {
  319. case EHealLevel::HEAL:
  320. maxHeal = std::max(0, unitHealth - firstHPleft);
  321. break;
  322. case EHealLevel::RESURRECT:
  323. maxHeal = total() - available();
  324. break;
  325. default:
  326. assert(level == EHealLevel::OVERHEAL);
  327. break;
  328. }
  329. vstd::amax(maxHeal, 0);
  330. vstd::abetween(amount, 0, maxHeal);
  331. if(amount == 0)
  332. return;
  333. int64_t availableHealth = available();
  334. availableHealth += amount;
  335. setFromTotal(availableHealth);
  336. if(power == EHealPower::ONE_BATTLE)
  337. addResurrected(getCount() - oldCount);
  338. else
  339. assert(power == EHealPower::PERMANENT);
  340. }
  341. void CHealth::setFromTotal(const int64_t totalHealth)
  342. {
  343. const int32_t unitHealth = owner->MaxHealth();
  344. firstHPleft = totalHealth % unitHealth;
  345. fullUnits = totalHealth / unitHealth;
  346. if(firstHPleft == 0 && fullUnits >= 1)
  347. {
  348. firstHPleft = unitHealth;
  349. fullUnits -= 1;
  350. }
  351. }
  352. void CHealth::reset()
  353. {
  354. fullUnits = 0;
  355. firstHPleft = 0;
  356. resurrected = 0;
  357. }
  358. int32_t CHealth::getCount() const
  359. {
  360. return fullUnits + (firstHPleft > 0 ? 1 : 0);
  361. }
  362. int32_t CHealth::getFirstHPleft() const
  363. {
  364. return firstHPleft;
  365. }
  366. int32_t CHealth::getResurrected() const
  367. {
  368. return resurrected;
  369. }
  370. void CHealth::takeResurrected()
  371. {
  372. if(resurrected != 0)
  373. {
  374. int64_t totalHealth = available();
  375. totalHealth -= resurrected * owner->MaxHealth();
  376. vstd::amax(totalHealth, 0);
  377. setFromTotal(totalHealth);
  378. resurrected = 0;
  379. }
  380. }
  381. void CHealth::serializeJson(JsonSerializeFormat & handler)
  382. {
  383. handler.serializeInt("firstHPleft", firstHPleft, 0);
  384. handler.serializeInt("fullUnits", fullUnits, 0);
  385. handler.serializeInt("resurrected", resurrected, 0);
  386. }
  387. ///CUnitState
  388. CUnitState::CUnitState()
  389. : env(nullptr),
  390. cloned(false),
  391. defending(false),
  392. defendingAnim(false),
  393. drainedMana(false),
  394. fear(false),
  395. hadMorale(false),
  396. ghost(false),
  397. ghostPending(false),
  398. movedThisRound(false),
  399. summoned(false),
  400. waiting(false),
  401. casts(this),
  402. counterAttacks(this),
  403. health(this),
  404. shots(this),
  405. totalAttacks(this, Selector::type(Bonus::ADDITIONAL_ATTACK), 1),
  406. minDamage(this, Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1)), 0),
  407. maxDamage(this, Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2)), 0),
  408. attack(this, Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), 0),
  409. defence(this, Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE), 0),
  410. inFrenzy(this, Selector::type(Bonus::IN_FRENZY)),
  411. cloneLifetimeMarker(this, Selector::type(Bonus::NONE).And(Selector::source(Bonus::SPELL_EFFECT, SpellID::CLONE))),
  412. cloneID(-1),
  413. position()
  414. {
  415. }
  416. CUnitState & CUnitState::operator=(const CUnitState & other)
  417. {
  418. //do not change unit and bonus info
  419. cloned = other.cloned;
  420. defending = other.defending;
  421. defendingAnim = other.defendingAnim;
  422. drainedMana = other.drainedMana;
  423. fear = other.fear;
  424. hadMorale = other.hadMorale;
  425. ghost = other.ghost;
  426. ghostPending = other.ghostPending;
  427. movedThisRound = other.movedThisRound;
  428. summoned = other.summoned;
  429. waiting = other.waiting;
  430. casts = other.casts;
  431. counterAttacks = other.counterAttacks;
  432. health = other.health;
  433. shots = other.shots;
  434. totalAttacks = other.totalAttacks;
  435. minDamage = other.minDamage;
  436. maxDamage = other.maxDamage;
  437. attack = other.attack;
  438. defence = other.defence;
  439. inFrenzy = other.inFrenzy;
  440. cloneLifetimeMarker = other.cloneLifetimeMarker;
  441. cloneID = other.cloneID;
  442. position = other.position;
  443. return *this;
  444. }
  445. int32_t CUnitState::creatureIndex() const
  446. {
  447. return static_cast<int32_t>(creatureId().toEnum());
  448. }
  449. CreatureID CUnitState::creatureId() const
  450. {
  451. return unitType()->idNumber;
  452. }
  453. int32_t CUnitState::creatureLevel() const
  454. {
  455. return static_cast<int32_t>(unitType()->level);
  456. }
  457. bool CUnitState::doubleWide() const
  458. {
  459. return unitType()->doubleWide;
  460. }
  461. int32_t CUnitState::creatureCost() const
  462. {
  463. return unitType()->cost[Res::GOLD];
  464. }
  465. int32_t CUnitState::creatureIconIndex() const
  466. {
  467. return unitType()->iconIndex;
  468. }
  469. int32_t CUnitState::getCasterUnitId() const
  470. {
  471. return static_cast<int32_t>(unitId());
  472. }
  473. ui8 CUnitState::getSpellSchoolLevel(const spells::Spell * spell, int * outSelectedSchool) const
  474. {
  475. int skill = valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, spell->getIndex()));
  476. vstd::abetween(skill, 0, 3);
  477. return skill;
  478. }
  479. int64_t CUnitState::getSpellBonus(const spells::Spell * spell, int64_t base, const Unit * affectedStack) const
  480. {
  481. //does not have sorcery-like bonuses (yet?)
  482. return base;
  483. }
  484. int64_t CUnitState::getSpecificSpellBonus(const spells::Spell * spell, int64_t base) const
  485. {
  486. return base;
  487. }
  488. int CUnitState::getEffectLevel(const spells::Spell * spell) const
  489. {
  490. return getSpellSchoolLevel(spell);
  491. }
  492. int CUnitState::getEffectPower(const spells::Spell * spell) const
  493. {
  494. return valOfBonuses(Bonus::CREATURE_SPELL_POWER) * getCount() / 100;
  495. }
  496. int CUnitState::getEnchantPower(const spells::Spell * spell) const
  497. {
  498. int res = valOfBonuses(Bonus::CREATURE_ENCHANT_POWER);
  499. if(res <= 0)
  500. res = 3;//default for creatures
  501. return res;
  502. }
  503. int64_t CUnitState::getEffectValue(const spells::Spell * spell) const
  504. {
  505. return static_cast<int64_t>(getCount()) * valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, spell->getIndex());
  506. }
  507. const PlayerColor CUnitState::getOwner() const
  508. {
  509. return env->unitEffectiveOwner(this);
  510. }
  511. void CUnitState::getCasterName(MetaString & text) const
  512. {
  513. //always plural name in case of spell cast.
  514. addNameReplacement(text, true);
  515. }
  516. void CUnitState::getCastDescription(const spells::Spell * spell, const std::vector<const Unit *> & attacked, MetaString & text) const
  517. {
  518. text.addTxt(MetaString::GENERAL_TXT, 565);//The %s casts %s
  519. //todo: use text 566 for single creature
  520. getCasterName(text);
  521. text.addReplacement(MetaString::SPELL_NAME, spell->getIndex());
  522. }
  523. bool CUnitState::ableToRetaliate() const
  524. {
  525. return alive()
  526. && counterAttacks.canUse();
  527. }
  528. bool CUnitState::alive() const
  529. {
  530. return health.getCount() > 0;
  531. }
  532. bool CUnitState::isGhost() const
  533. {
  534. return ghost;
  535. }
  536. bool CUnitState::isValidTarget(bool allowDead) const
  537. {
  538. return (alive() || (allowDead && isDead())) && getPosition().isValid() && !isTurret();
  539. }
  540. bool CUnitState::isClone() const
  541. {
  542. return cloned;
  543. }
  544. bool CUnitState::hasClone() const
  545. {
  546. return cloneID > 0;
  547. }
  548. bool CUnitState::canCast() const
  549. {
  550. return casts.canUse(1);//do not check specific cast abilities here
  551. }
  552. bool CUnitState::isCaster() const
  553. {
  554. return casts.total() > 0;//do not check specific cast abilities here
  555. }
  556. bool CUnitState::canShoot() const
  557. {
  558. return shots.canUse(1);
  559. }
  560. bool CUnitState::isShooter() const
  561. {
  562. return shots.total() > 0;
  563. }
  564. int32_t CUnitState::getKilled() const
  565. {
  566. int32_t res = unitBaseAmount() - health.getCount() + health.getResurrected();
  567. vstd::amax(res, 0);
  568. return res;
  569. }
  570. int32_t CUnitState::getCount() const
  571. {
  572. return health.getCount();
  573. }
  574. int32_t CUnitState::getFirstHPleft() const
  575. {
  576. return health.getFirstHPleft();
  577. }
  578. int64_t CUnitState::getAvailableHealth() const
  579. {
  580. return health.available();
  581. }
  582. int64_t CUnitState::getTotalHealth() const
  583. {
  584. return health.total();
  585. }
  586. BattleHex CUnitState::getPosition() const
  587. {
  588. return position;
  589. }
  590. void CUnitState::setPosition(BattleHex hex)
  591. {
  592. position = hex;
  593. }
  594. int32_t CUnitState::getInitiative(int turn) const
  595. {
  596. return valOfBonuses(Selector::type(Bonus::STACKS_SPEED).And(Selector::turns(turn)));
  597. }
  598. bool CUnitState::canMove(int turn) const
  599. {
  600. return alive() && !hasBonus(Selector::type(Bonus::NOT_ACTIVE).And(Selector::turns(turn))); //eg. Ammo Cart or blinded creature
  601. }
  602. bool CUnitState::defended(int turn) const
  603. {
  604. if(!turn)
  605. return defending;
  606. else
  607. return false;
  608. }
  609. bool CUnitState::moved(int turn) const
  610. {
  611. if(!turn)
  612. return movedThisRound;
  613. else
  614. return false;
  615. }
  616. bool CUnitState::willMove(int turn) const
  617. {
  618. return (turn ? true : !defending)
  619. && !moved(turn)
  620. && canMove(turn);
  621. }
  622. bool CUnitState::waited(int turn) const
  623. {
  624. if(!turn)
  625. return waiting;
  626. else
  627. return false;
  628. }
  629. int CUnitState::battleQueuePhase(int turn) const
  630. {
  631. if(turn <= 0 && waited()) //consider waiting state only for ongoing round
  632. {
  633. if(hadMorale)
  634. return 2;
  635. else
  636. return 3;
  637. }
  638. else if(creatureIndex() == CreatureID::CATAPULT || isTurret()) //catapult and turrets are first
  639. {
  640. return 0;
  641. }
  642. else
  643. {
  644. return 1;
  645. }
  646. }
  647. int CUnitState::getTotalAttacks(bool ranged) const
  648. {
  649. return ranged ? totalAttacks.getRangedValue() : totalAttacks.getMeleeValue();
  650. }
  651. int CUnitState::getMinDamage(bool ranged) const
  652. {
  653. return ranged ? minDamage.getRangedValue() : minDamage.getMeleeValue();
  654. }
  655. int CUnitState::getMaxDamage(bool ranged) const
  656. {
  657. return ranged ? maxDamage.getRangedValue() : maxDamage.getMeleeValue();
  658. }
  659. int CUnitState::getAttack(bool ranged) const
  660. {
  661. int ret = ranged ? attack.getRangedValue() : attack.getMeleeValue();
  662. if(!inFrenzy->empty())
  663. {
  664. double frenzyPower = (double)inFrenzy->totalValue() / 100;
  665. frenzyPower *= (double) (ranged ? defence.getRangedValue() : defence.getMeleeValue());
  666. ret += frenzyPower;
  667. }
  668. vstd::amax(ret, 0);
  669. return ret;
  670. }
  671. int CUnitState::getDefence(bool ranged) const
  672. {
  673. if(!inFrenzy->empty())
  674. {
  675. return 0;
  676. }
  677. else
  678. {
  679. int ret = ranged ? defence.getRangedValue() : defence.getMeleeValue();
  680. vstd::amax(ret, 0);
  681. return ret;
  682. }
  683. }
  684. std::shared_ptr<Unit> CUnitState::acquire() const
  685. {
  686. auto ret = std::make_shared<CUnitStateDetached>(this, this);
  687. ret->localInit(env);
  688. *ret = *this;
  689. return ret;
  690. }
  691. std::shared_ptr<CUnitState> CUnitState::acquireState() const
  692. {
  693. auto ret = std::make_shared<CUnitStateDetached>(this, this);
  694. ret->localInit(env);
  695. *ret = *this;
  696. return ret;
  697. }
  698. void CUnitState::serializeJson(JsonSerializeFormat & handler)
  699. {
  700. if(!handler.saving)
  701. reset();
  702. handler.serializeBool("cloned", cloned);
  703. handler.serializeBool("defending", defending);
  704. handler.serializeBool("defendingAnim", defendingAnim);
  705. handler.serializeBool("drainedMana", drainedMana);
  706. handler.serializeBool("fear", fear);
  707. handler.serializeBool("hadMorale", hadMorale);
  708. handler.serializeBool("ghost", ghost);
  709. handler.serializeBool("ghostPending", ghostPending);
  710. handler.serializeBool("moved", movedThisRound);
  711. handler.serializeBool("summoned", summoned);
  712. handler.serializeBool("waiting", waiting);
  713. handler.serializeStruct("casts", casts);
  714. handler.serializeStruct("counterAttacks", counterAttacks);
  715. handler.serializeStruct("health", health);
  716. handler.serializeStruct("shots", shots);
  717. handler.serializeInt("cloneID", cloneID);
  718. handler.serializeInt("position", position);
  719. }
  720. void CUnitState::localInit(const IUnitEnvironment * env_)
  721. {
  722. env = env_;
  723. shots.setEnv(env);
  724. reset();
  725. health.init();
  726. }
  727. void CUnitState::reset()
  728. {
  729. cloned = false;
  730. defending = false;
  731. defendingAnim = false;
  732. drainedMana = false;
  733. fear = false;
  734. hadMorale = false;
  735. ghost = false;
  736. ghostPending = false;
  737. movedThisRound = false;
  738. summoned = false;
  739. waiting = false;
  740. casts.reset();
  741. counterAttacks.reset();
  742. health.reset();
  743. shots.reset();
  744. cloneID = -1;
  745. position = BattleHex::INVALID;
  746. }
  747. void CUnitState::save(JsonNode & data)
  748. {
  749. //TODO: use instance resolver
  750. data.clear();
  751. JsonSerializer ser(nullptr, data);
  752. ser.serializeStruct("state", *this);
  753. }
  754. void CUnitState::load(const JsonNode & data)
  755. {
  756. //TODO: use instance resolver
  757. reset();
  758. JsonDeserializer deser(nullptr, data);
  759. deser.serializeStruct("state", *this);
  760. }
  761. void CUnitState::damage(int64_t & amount)
  762. {
  763. if(cloned)
  764. {
  765. // block ability should not kill clone (0 damage)
  766. if(amount > 0)
  767. {
  768. amount = 1;//TODO: what should be actual damage against clone?
  769. health.reset();
  770. }
  771. }
  772. else
  773. {
  774. health.damage(amount);
  775. }
  776. if(health.available() <= 0 && (cloned || summoned))
  777. ghostPending = true;
  778. }
  779. void CUnitState::heal(int64_t & amount, EHealLevel level, EHealPower power)
  780. {
  781. if(level == EHealLevel::HEAL && power == EHealPower::ONE_BATTLE)
  782. logGlobal->error("Heal for one battle does not make sense");
  783. else if(cloned)
  784. logGlobal->error("Attempt to heal clone");
  785. else
  786. health.heal(amount, level, power);
  787. }
  788. void CUnitState::afterAttack(bool ranged, bool counter)
  789. {
  790. if(counter)
  791. counterAttacks.use();
  792. if(ranged)
  793. shots.use();
  794. }
  795. void CUnitState::afterNewRound()
  796. {
  797. defending = false;
  798. waiting = false;
  799. movedThisRound = false;
  800. hadMorale = false;
  801. fear = false;
  802. drainedMana = false;
  803. counterAttacks.reset();
  804. if(alive() && isClone())
  805. {
  806. if(!cloneLifetimeMarker.getHasBonus())
  807. makeGhost();
  808. }
  809. }
  810. void CUnitState::afterGetsTurn()
  811. {
  812. //if moving second time this round it must be high morale bonus
  813. if(movedThisRound)
  814. hadMorale = true;
  815. }
  816. void CUnitState::makeGhost()
  817. {
  818. health.reset();
  819. ghostPending = true;
  820. }
  821. void CUnitState::onRemoved()
  822. {
  823. health.reset();
  824. ghostPending = false;
  825. ghost = true;
  826. }
  827. CUnitStateDetached::CUnitStateDetached(const IUnitInfo * unit_, const IBonusBearer * bonus_)
  828. : CUnitState(),
  829. unit(unit_),
  830. bonus(bonus_)
  831. {
  832. }
  833. const TBonusListPtr CUnitStateDetached::getAllBonuses(const CSelector & selector, const CSelector & limit, const CBonusSystemNode * root, const std::string & cachingStr) const
  834. {
  835. return bonus->getAllBonuses(selector, limit, root, cachingStr);
  836. }
  837. int64_t CUnitStateDetached::getTreeVersion() const
  838. {
  839. return bonus->getTreeVersion();
  840. }
  841. CUnitStateDetached & CUnitStateDetached::operator=(const CUnitState & other)
  842. {
  843. CUnitState::operator=(other);
  844. return *this;
  845. }
  846. uint32_t CUnitStateDetached::unitId() const
  847. {
  848. return unit->unitId();
  849. }
  850. ui8 CUnitStateDetached::unitSide() const
  851. {
  852. return unit->unitSide();
  853. }
  854. const CCreature * CUnitStateDetached::unitType() const
  855. {
  856. return unit->unitType();
  857. }
  858. PlayerColor CUnitStateDetached::unitOwner() const
  859. {
  860. return unit->unitOwner();
  861. }
  862. SlotID CUnitStateDetached::unitSlot() const
  863. {
  864. return unit->unitSlot();
  865. }
  866. int32_t CUnitStateDetached::unitBaseAmount() const
  867. {
  868. return unit->unitBaseAmount();
  869. }
  870. void CUnitStateDetached::spendMana(const spells::PacketSender * server, const int spellCost) const
  871. {
  872. if(spellCost != 1)
  873. logGlobal->warn("Unexpected spell cost %d for creature", spellCost);
  874. //this is evil, but
  875. //use of netpacks in detached state is an error
  876. //non const API is more evil for hero
  877. const_cast<CUnitStateDetached *>(this)->casts.use(spellCost);
  878. }
  879. }