mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-15 00:05:02 +02:00
Initial version of new bonus caching system
This commit is contained in:
@ -44,10 +44,6 @@ public:
|
|||||||
Returns defence of creature or hero.
|
Returns defence of creature or hero.
|
||||||
*/
|
*/
|
||||||
virtual int getDefense(bool ranged) const;
|
virtual int getDefense(bool ranged) const;
|
||||||
/**
|
|
||||||
Returns primskill of creature or hero.
|
|
||||||
*/
|
|
||||||
int getPrimSkillLevel(PrimarySkill id) const;
|
|
||||||
/**
|
/**
|
||||||
Returns morale of creature or hero. Taking absolute bonuses into account.
|
Returns morale of creature or hero. Taking absolute bonuses into account.
|
||||||
For now, uses range from EGameSettings
|
For now, uses range from EGameSettings
|
||||||
|
@ -69,14 +69,6 @@ int AFactionMember::getMaxDamage(bool ranged) const
|
|||||||
return getBonusBearer()->valOfBonuses(selector, cachingStr);
|
return getBonusBearer()->valOfBonuses(selector, cachingStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int AFactionMember::getPrimSkillLevel(PrimarySkill id) const
|
|
||||||
{
|
|
||||||
auto allSkills = getBonusBearer()->getBonusesOfType(BonusType::PRIMARY_SKILL);
|
|
||||||
int ret = allSkills->valOfBonuses(Selector::subtype()(BonusSubtypeID(id)));
|
|
||||||
int minSkillValue = VLC->engineSettings()->getVectorValue(EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, id.getNum());
|
|
||||||
return std::max(ret, minSkillValue); //otherwise, some artifacts may cause negative skill value effect, sp=0 works in old saves
|
|
||||||
}
|
|
||||||
|
|
||||||
int AFactionMember::moraleValAndBonusList(TConstBonusListPtr & bonusList) const
|
int AFactionMember::moraleValAndBonusList(TConstBonusListPtr & bonusList) const
|
||||||
{
|
{
|
||||||
int32_t maxGoodMorale = VLC->engineSettings()->getVector(EGameSettings::COMBAT_GOOD_MORALE_DICE).size();
|
int32_t maxGoodMorale = VLC->engineSettings()->getVector(EGameSettings::COMBAT_GOOD_MORALE_DICE).size();
|
||||||
|
@ -65,6 +65,7 @@ set(lib_MAIN_SRCS
|
|||||||
battle/Unit.cpp
|
battle/Unit.cpp
|
||||||
|
|
||||||
bonuses/Bonus.cpp
|
bonuses/Bonus.cpp
|
||||||
|
bonuses/BonusCache.cpp
|
||||||
bonuses/BonusEnum.cpp
|
bonuses/BonusEnum.cpp
|
||||||
bonuses/BonusList.cpp
|
bonuses/BonusList.cpp
|
||||||
bonuses/BonusParams.cpp
|
bonuses/BonusParams.cpp
|
||||||
@ -435,6 +436,7 @@ set(lib_MAIN_HEADERS
|
|||||||
battle/Unit.h
|
battle/Unit.h
|
||||||
|
|
||||||
bonuses/Bonus.h
|
bonuses/Bonus.h
|
||||||
|
bonuses/BonusCache.h
|
||||||
bonuses/BonusEnum.h
|
bonuses/BonusEnum.h
|
||||||
bonuses/BonusList.h
|
bonuses/BonusList.h
|
||||||
bonuses/BonusParams.h
|
bonuses/BonusParams.h
|
||||||
|
@ -26,7 +26,7 @@ namespace battle
|
|||||||
CAmmo::CAmmo(const battle::Unit * Owner, CSelector totalSelector):
|
CAmmo::CAmmo(const battle::Unit * Owner, CSelector totalSelector):
|
||||||
used(0),
|
used(0),
|
||||||
owner(Owner),
|
owner(Owner),
|
||||||
totalProxy(Owner, std::move(totalSelector))
|
totalProxy(Owner, totalSelector)
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
@ -34,7 +34,6 @@ CAmmo::CAmmo(const battle::Unit * Owner, CSelector totalSelector):
|
|||||||
CAmmo & CAmmo::operator= (const CAmmo & other)
|
CAmmo & CAmmo::operator= (const CAmmo & other)
|
||||||
{
|
{
|
||||||
used = other.used;
|
used = other.used;
|
||||||
totalProxy = other.totalProxy;
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +59,7 @@ void CAmmo::reset()
|
|||||||
|
|
||||||
int32_t CAmmo::total() const
|
int32_t CAmmo::total() const
|
||||||
{
|
{
|
||||||
return totalProxy->totalValue();
|
return totalProxy.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAmmo::use(int32_t amount)
|
void CAmmo::use(int32_t amount)
|
||||||
@ -89,13 +88,6 @@ CShots::CShots(const battle::Unit * Owner)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CShots & CShots::operator=(const CShots & other)
|
|
||||||
{
|
|
||||||
CAmmo::operator=(other);
|
|
||||||
shooter = other.shooter;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CShots::isLimited() const
|
bool CShots::isLimited() const
|
||||||
{
|
{
|
||||||
return !shooter.getHasBonus() || !env->unitHasAmmoCart(owner);
|
return !shooter.getHasBonus() || !env->unitHasAmmoCart(owner);
|
||||||
@ -140,7 +132,7 @@ int32_t CRetaliations::total() const
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
//after dispel bonus should remain during current round
|
//after dispel bonus should remain during current round
|
||||||
int32_t val = 1 + totalProxy->totalValue();
|
int32_t val = 1 + totalProxy.getValue();
|
||||||
vstd::amax(totalCache, val);
|
vstd::amax(totalCache, val);
|
||||||
return totalCache;
|
return totalCache;
|
||||||
}
|
}
|
||||||
@ -341,12 +333,7 @@ CUnitState::CUnitState():
|
|||||||
counterAttacks(this),
|
counterAttacks(this),
|
||||||
health(this),
|
health(this),
|
||||||
shots(this),
|
shots(this),
|
||||||
totalAttacks(this, Selector::type()(BonusType::ADDITIONAL_ATTACK), 1),
|
bonusCache(this, generateBonusSelectors()),
|
||||||
minDamage(this, Selector::typeSubtype(BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageBoth).Or(Selector::typeSubtype(BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageMin)), 0),
|
|
||||||
maxDamage(this, Selector::typeSubtype(BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageBoth).Or(Selector::typeSubtype(BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageMax)), 0),
|
|
||||||
attack(this, Selector::typeSubtype(BonusType::PRIMARY_SKILL, BonusSubtypeID(PrimarySkill::ATTACK)), 0),
|
|
||||||
defence(this, Selector::typeSubtype(BonusType::PRIMARY_SKILL, BonusSubtypeID(PrimarySkill::DEFENSE)), 0),
|
|
||||||
inFrenzy(this, Selector::type()(BonusType::IN_FRENZY)),
|
|
||||||
cloneLifetimeMarker(this, Selector::type()(BonusType::NONE).And(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(SpellID(SpellID::CLONE)))), "CUnitState::cloneLifetimeMarker"),
|
cloneLifetimeMarker(this, Selector::type()(BonusType::NONE).And(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(SpellID(SpellID::CLONE)))), "CUnitState::cloneLifetimeMarker"),
|
||||||
cloneID(-1)
|
cloneID(-1)
|
||||||
{
|
{
|
||||||
@ -374,12 +361,7 @@ CUnitState & CUnitState::operator=(const CUnitState & other)
|
|||||||
counterAttacks = other.counterAttacks;
|
counterAttacks = other.counterAttacks;
|
||||||
health = other.health;
|
health = other.health;
|
||||||
shots = other.shots;
|
shots = other.shots;
|
||||||
totalAttacks = other.totalAttacks;
|
// bonusCache = other.bonusCache;
|
||||||
minDamage = other.minDamage;
|
|
||||||
maxDamage = other.maxDamage;
|
|
||||||
attack = other.attack;
|
|
||||||
defence = other.defence;
|
|
||||||
inFrenzy = other.inFrenzy;
|
|
||||||
cloneLifetimeMarker = other.cloneLifetimeMarker;
|
cloneLifetimeMarker = other.cloneLifetimeMarker;
|
||||||
cloneID = other.cloneID;
|
cloneID = other.cloneID;
|
||||||
position = other.position;
|
position = other.position;
|
||||||
@ -695,45 +677,62 @@ BattlePhases::Type CUnitState::battleQueuePhase(int turn) const
|
|||||||
|
|
||||||
int CUnitState::getTotalAttacks(bool ranged) const
|
int CUnitState::getTotalAttacks(bool ranged) const
|
||||||
{
|
{
|
||||||
return ranged ? totalAttacks.getRangedValue() : totalAttacks.getMeleeValue();
|
return 1 + (ranged ?
|
||||||
|
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::TOTAL_ATTACKS_RANGED):
|
||||||
|
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::TOTAL_ATTACKS_MELEE));
|
||||||
}
|
}
|
||||||
|
|
||||||
int CUnitState::getMinDamage(bool ranged) const
|
int CUnitState::getMinDamage(bool ranged) const
|
||||||
{
|
{
|
||||||
return ranged ? minDamage.getRangedValue() : minDamage.getMeleeValue();
|
return ranged ?
|
||||||
|
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::MIN_DAMAGE_RANGED):
|
||||||
|
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::MIN_DAMAGE_MELEE);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int CUnitState::getMaxDamage(bool ranged) const
|
int CUnitState::getMaxDamage(bool ranged) const
|
||||||
{
|
{
|
||||||
return ranged ? maxDamage.getRangedValue() : maxDamage.getMeleeValue();
|
return ranged ?
|
||||||
|
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::MAX_DAMAGE_RANGED):
|
||||||
|
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::MAX_DAMAGE_MELEE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CUnitState::getAttack(bool ranged) const
|
int CUnitState::getAttack(bool ranged) const
|
||||||
{
|
{
|
||||||
int ret = ranged ? attack.getRangedValue() : attack.getMeleeValue();
|
int attack = ranged ?
|
||||||
|
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::ATTACK_RANGED):
|
||||||
|
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::ATTACK_MELEE);
|
||||||
|
|
||||||
if(!inFrenzy->empty())
|
int frenzy = bonusCache.cache.getBonusValue(UnitBonusValuesProxy::IN_FRENZY);
|
||||||
|
if(frenzy != 0)
|
||||||
{
|
{
|
||||||
double frenzyPower = static_cast<double>(inFrenzy->totalValue()) / 100;
|
int defence = ranged ?
|
||||||
frenzyPower *= static_cast<double>(ranged ? defence.getRangedValue() : defence.getMeleeValue());
|
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::DEFENCE_RANGED):
|
||||||
ret += static_cast<int>(frenzyPower);
|
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::DEFENCE_MELEE);
|
||||||
|
|
||||||
|
int frenzyBonus = frenzy * defence / 100;
|
||||||
|
attack += frenzyBonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
vstd::amax(ret, 0);
|
vstd::amax(attack, 0);
|
||||||
return ret;
|
return attack;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CUnitState::getDefense(bool ranged) const
|
int CUnitState::getDefense(bool ranged) const
|
||||||
{
|
{
|
||||||
if(!inFrenzy->empty())
|
int frenzy = bonusCache.cache.getBonusValue(UnitBonusValuesProxy::IN_FRENZY);
|
||||||
|
|
||||||
|
if(frenzy != 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int ret = ranged ? defence.getRangedValue() : defence.getMeleeValue();
|
int defence = ranged ?
|
||||||
vstd::amax(ret, 0);
|
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::DEFENCE_RANGED):
|
||||||
return ret;
|
bonusCache.cache.getBonusValue(UnitBonusValuesProxy::DEFENCE_MELEE);
|
||||||
|
vstd::amax(defence, 0);
|
||||||
|
return defence;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -911,6 +910,33 @@ void CUnitState::onRemoved()
|
|||||||
ghost = true;
|
ghost = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UnitBonusValuesProxy::SelectorsArray * CUnitState::generateBonusSelectors()
|
||||||
|
{
|
||||||
|
static const CSelector additionalAttack = Selector::type()(BonusType::ADDITIONAL_ATTACK);
|
||||||
|
static const CSelector selectorMelee = Selector::effectRange()(BonusLimitEffect::NO_LIMIT).Or(Selector::effectRange()(BonusLimitEffect::ONLY_MELEE_FIGHT));
|
||||||
|
static const CSelector selectorRanged = Selector::effectRange()(BonusLimitEffect::NO_LIMIT).Or(Selector::effectRange()(BonusLimitEffect::ONLY_DISTANCE_FIGHT));
|
||||||
|
static const CSelector minDamage = Selector::typeSubtype(BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageBoth).Or(Selector::typeSubtype(BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageMin));
|
||||||
|
static const CSelector maxDamage = Selector::typeSubtype(BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageBoth).Or(Selector::typeSubtype(BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageMin));
|
||||||
|
static const CSelector attack = Selector::typeSubtype(BonusType::PRIMARY_SKILL, BonusSubtypeID(PrimarySkill::ATTACK));
|
||||||
|
static const CSelector defence = Selector::typeSubtype(BonusType::PRIMARY_SKILL, BonusSubtypeID(PrimarySkill::ATTACK));
|
||||||
|
|
||||||
|
static const UnitBonusValuesProxy::SelectorsArray selectors = {
|
||||||
|
additionalAttack.And(selectorMelee), //TOTAL_ATTACKS_MELEE,
|
||||||
|
additionalAttack.And(selectorRanged), //TOTAL_ATTACKS_RANGED,
|
||||||
|
minDamage.And(selectorMelee), //MIN_DAMAGE_MELEE,
|
||||||
|
minDamage.And(selectorRanged), //MIN_DAMAGE_RANGED,
|
||||||
|
minDamage.And(selectorMelee), //MAX_DAMAGE_MELEE,
|
||||||
|
maxDamage.And(selectorRanged), //MAX_DAMAGE_RANGED,
|
||||||
|
attack.And(selectorRanged),//ATTACK_MELEE,
|
||||||
|
attack.And(selectorRanged),//ATTACK_RANGED,
|
||||||
|
defence.And(selectorRanged),//DEFENCE_MELEE,
|
||||||
|
defence.And(selectorRanged),//DEFENCE_RANGED,
|
||||||
|
Selector::type()(BonusType::IN_FRENZY),//IN_FRENZY,
|
||||||
|
};
|
||||||
|
|
||||||
|
return &selectors;
|
||||||
|
}
|
||||||
|
|
||||||
CUnitStateDetached::CUnitStateDetached(const IUnitInfo * unit_, const IBonusBearer * bonus_):
|
CUnitStateDetached::CUnitStateDetached(const IUnitInfo * unit_, const IBonusBearer * bonus_):
|
||||||
unit(unit_),
|
unit(unit_),
|
||||||
bonus(bonus_)
|
bonus(bonus_)
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
#include "../bonuses/CBonusProxy.h"
|
#include "../bonuses/CBonusProxy.h"
|
||||||
|
#include "../bonuses/BonusCache.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -32,10 +33,6 @@ class DLL_LINKAGE CAmmo
|
|||||||
public:
|
public:
|
||||||
explicit CAmmo(const battle::Unit * Owner, CSelector totalSelector);
|
explicit CAmmo(const battle::Unit * Owner, CSelector totalSelector);
|
||||||
|
|
||||||
//only copy construction is allowed for acquire(), serializeJson should be used for any other "assignment"
|
|
||||||
CAmmo(const CAmmo & other) = default;
|
|
||||||
CAmmo(CAmmo && other) = delete;
|
|
||||||
|
|
||||||
CAmmo & operator=(const CAmmo & other);
|
CAmmo & operator=(const CAmmo & other);
|
||||||
CAmmo & operator=(CAmmo && other) = delete;
|
CAmmo & operator=(CAmmo && other) = delete;
|
||||||
|
|
||||||
@ -50,15 +47,14 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
int32_t used;
|
int32_t used;
|
||||||
const battle::Unit * owner;
|
const battle::Unit * owner;
|
||||||
CBonusProxy totalProxy;
|
BonusValueCache totalProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CShots : public CAmmo
|
class DLL_LINKAGE CShots : public CAmmo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit CShots(const battle::Unit * Owner);
|
explicit CShots(const battle::Unit * Owner);
|
||||||
CShots(const CShots & other) = default;
|
|
||||||
CShots & operator=(const CShots & other);
|
|
||||||
bool isLimited() const override;
|
bool isLimited() const override;
|
||||||
int32_t total() const override;
|
int32_t total() const override;
|
||||||
|
|
||||||
@ -73,16 +69,13 @@ class DLL_LINKAGE CCasts : public CAmmo
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit CCasts(const battle::Unit * Owner);
|
explicit CCasts(const battle::Unit * Owner);
|
||||||
CCasts(const CCasts & other) = default;
|
|
||||||
CCasts & operator=(const CCasts & other) = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CRetaliations : public CAmmo
|
class DLL_LINKAGE CRetaliations : public CAmmo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit CRetaliations(const battle::Unit * Owner);
|
explicit CRetaliations(const battle::Unit * Owner);
|
||||||
CRetaliations(const CRetaliations & other) = default;
|
|
||||||
CRetaliations & operator=(const CRetaliations & other) = default;
|
|
||||||
bool isLimited() const override;
|
bool isLimited() const override;
|
||||||
int32_t total() const override;
|
int32_t total() const override;
|
||||||
void reset() override;
|
void reset() override;
|
||||||
@ -132,6 +125,41 @@ private:
|
|||||||
int32_t resurrected;
|
int32_t resurrected;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class UnitBonusValuesProxy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ECacheKeys : uint8_t
|
||||||
|
{
|
||||||
|
TOTAL_ATTACKS_MELEE,
|
||||||
|
TOTAL_ATTACKS_RANGED,
|
||||||
|
|
||||||
|
MIN_DAMAGE_MELEE,
|
||||||
|
MIN_DAMAGE_RANGED,
|
||||||
|
MAX_DAMAGE_MELEE,
|
||||||
|
MAX_DAMAGE_RANGED,
|
||||||
|
|
||||||
|
ATTACK_MELEE,
|
||||||
|
ATTACK_RANGED,
|
||||||
|
|
||||||
|
DEFENCE_MELEE,
|
||||||
|
DEFENCE_RANGED,
|
||||||
|
|
||||||
|
IN_FRENZY,
|
||||||
|
|
||||||
|
TOTAL_KEYS,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr size_t KEYS_COUNT = static_cast<size_t>(ECacheKeys::TOTAL_KEYS);
|
||||||
|
|
||||||
|
BonusValuesArrayCache<ECacheKeys, KEYS_COUNT> cache;
|
||||||
|
|
||||||
|
using SelectorsArray = BonusValuesArrayCache<ECacheKeys, KEYS_COUNT>::SelectorsArray;
|
||||||
|
|
||||||
|
UnitBonusValuesProxy(const IBonusBearer * Target, const SelectorsArray * selectors):
|
||||||
|
cache(Target, selectors)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CUnitState : public Unit
|
class DLL_LINKAGE CUnitState : public Unit
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -154,11 +182,6 @@ public:
|
|||||||
CHealth health;
|
CHealth health;
|
||||||
CShots shots;
|
CShots shots;
|
||||||
|
|
||||||
CTotalsProxy totalAttacks;
|
|
||||||
|
|
||||||
CTotalsProxy minDamage;
|
|
||||||
CTotalsProxy maxDamage;
|
|
||||||
|
|
||||||
///id of alive clone of this stack clone if any
|
///id of alive clone of this stack clone if any
|
||||||
si32 cloneID;
|
si32 cloneID;
|
||||||
|
|
||||||
@ -266,12 +289,11 @@ public:
|
|||||||
void onRemoved();
|
void onRemoved();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static const UnitBonusValuesProxy::SelectorsArray * generateBonusSelectors();
|
||||||
|
|
||||||
const IUnitEnvironment * env;
|
const IUnitEnvironment * env;
|
||||||
|
|
||||||
CTotalsProxy attack;
|
UnitBonusValuesProxy bonusCache;
|
||||||
CTotalsProxy defence;
|
|
||||||
CBonusProxy inFrenzy;
|
|
||||||
|
|
||||||
CCheckProxy cloneLifetimeMarker;
|
CCheckProxy cloneLifetimeMarker;
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
@ -282,13 +304,12 @@ class DLL_LINKAGE CUnitStateDetached : public CUnitState
|
|||||||
public:
|
public:
|
||||||
explicit CUnitStateDetached(const IUnitInfo * unit_, const IBonusBearer * bonus_);
|
explicit CUnitStateDetached(const IUnitInfo * unit_, const IBonusBearer * bonus_);
|
||||||
|
|
||||||
TConstBonusListPtr getAllBonuses(const CSelector & selector, const CSelector & limit,
|
CUnitStateDetached & operator= (const CUnitState & other);
|
||||||
const std::string & cachingStr = "") const override;
|
|
||||||
|
TConstBonusListPtr getAllBonuses(const CSelector & selector, const CSelector & limit, const std::string & cachingStr = "") const override;
|
||||||
|
|
||||||
int64_t getTreeVersion() const override;
|
int64_t getTreeVersion() const override;
|
||||||
|
|
||||||
CUnitStateDetached & operator= (const CUnitState & other);
|
|
||||||
|
|
||||||
uint32_t unitId() const override;
|
uint32_t unitId() const override;
|
||||||
BattleSide unitSide() const override;
|
BattleSide unitSide() const override;
|
||||||
|
|
||||||
@ -297,7 +318,6 @@ public:
|
|||||||
|
|
||||||
SlotID unitSlot() const override;
|
SlotID unitSlot() const override;
|
||||||
|
|
||||||
|
|
||||||
int32_t unitBaseAmount() const override;
|
int32_t unitBaseAmount() const override;
|
||||||
|
|
||||||
void spendMana(ServerCallback * server, const int spellCost) const override;
|
void spendMana(ServerCallback * server, const int spellCost) const override;
|
||||||
|
81
lib/bonuses/BonusCache.cpp
Normal file
81
lib/bonuses/BonusCache.cpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* BonusCache.cpp, part of VCMI engine
|
||||||
|
*
|
||||||
|
* Authors: listed in file AUTHORS in main folder
|
||||||
|
*
|
||||||
|
* License: GNU General Public License v2.0 or later
|
||||||
|
* Full text of license available in license.txt file, in main folder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "StdInc.h"
|
||||||
|
#include "BonusCache.h"
|
||||||
|
#include "IBonusBearer.h"
|
||||||
|
|
||||||
|
#include "BonusSelector.h"
|
||||||
|
#include "BonusList.h"
|
||||||
|
|
||||||
|
#include "../VCMI_Lib.h"
|
||||||
|
#include "../IGameSettings.h"
|
||||||
|
|
||||||
|
int BonusCacheBase::getBonusValueImpl(BonusCacheEntry & currentValue, const CSelector & selector) const
|
||||||
|
{
|
||||||
|
if (target->getTreeVersion() == currentValue.version)
|
||||||
|
{
|
||||||
|
return currentValue.value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// NOTE: following code theoretically can fail if bonus tree was changed by another thread between two following lines
|
||||||
|
// However, this situation should not be possible - gamestate modification should only happen in single-treaded mode with locked gamestate mutex
|
||||||
|
int newValue = target->valOfBonuses(selector);
|
||||||
|
currentValue.value = newValue;
|
||||||
|
currentValue.version = target->getTreeVersion();
|
||||||
|
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BonusValueCache::BonusValueCache(const IBonusBearer * target, const CSelector selector)
|
||||||
|
:BonusCacheBase(target),selector(selector)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int BonusValueCache::getValue() const
|
||||||
|
{
|
||||||
|
return getBonusValueImpl(value, selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrimarySkillsCache::PrimarySkillsCache(const IBonusBearer * target)
|
||||||
|
:target(target)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void PrimarySkillsCache::update() const
|
||||||
|
{
|
||||||
|
static const CSelector primarySkillsSelector = Selector::type()(BonusType::PRIMARY_SKILL);
|
||||||
|
static const CSelector attackSelector = Selector::subtype()(PrimarySkill::ATTACK);
|
||||||
|
static const CSelector defenceSelector = Selector::subtype()(PrimarySkill::DEFENSE);
|
||||||
|
static const CSelector spellPowerSelector = Selector::subtype()(PrimarySkill::SPELL_POWER);
|
||||||
|
static const CSelector knowledgeSelector = Selector::subtype()(PrimarySkill::KNOWLEDGE);
|
||||||
|
|
||||||
|
std::array<int, 4> minValues = {
|
||||||
|
VLC->engineSettings()->getVectorValue(EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, PrimarySkill::ATTACK),
|
||||||
|
VLC->engineSettings()->getVectorValue(EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, PrimarySkill::DEFENSE),
|
||||||
|
VLC->engineSettings()->getVectorValue(EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, PrimarySkill::SPELL_POWER),
|
||||||
|
VLC->engineSettings()->getVectorValue(EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, PrimarySkill::KNOWLEDGE)
|
||||||
|
};
|
||||||
|
|
||||||
|
auto list = target->getBonuses(primarySkillsSelector);
|
||||||
|
skills[PrimarySkill::ATTACK] = std::max(minValues[PrimarySkill::ATTACK], list->valOfBonuses(attackSelector));
|
||||||
|
skills[PrimarySkill::DEFENSE] = std::max(minValues[PrimarySkill::DEFENSE], list->valOfBonuses(defenceSelector));
|
||||||
|
skills[PrimarySkill::SPELL_POWER] = std::max(minValues[PrimarySkill::SPELL_POWER], list->valOfBonuses(spellPowerSelector));
|
||||||
|
skills[PrimarySkill::KNOWLEDGE] = std::max(minValues[PrimarySkill::KNOWLEDGE], list->valOfBonuses(knowledgeSelector));
|
||||||
|
|
||||||
|
version = target->getTreeVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::array<std::atomic<int32_t>, 4> & PrimarySkillsCache::getSkills() const
|
||||||
|
{
|
||||||
|
if (target->getTreeVersion() != version)
|
||||||
|
update();
|
||||||
|
return skills;
|
||||||
|
}
|
81
lib/bonuses/BonusCache.h
Normal file
81
lib/bonuses/BonusCache.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* BonusCache.h, part of VCMI engine
|
||||||
|
*
|
||||||
|
* Authors: listed in file AUTHORS in main folder
|
||||||
|
*
|
||||||
|
* License: GNU General Public License v2.0 or later
|
||||||
|
* Full text of license available in license.txt file, in main folder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BonusSelector.h"
|
||||||
|
|
||||||
|
/// Internal base class with no own cache
|
||||||
|
class BonusCacheBase
|
||||||
|
{
|
||||||
|
const IBonusBearer * target;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit BonusCacheBase(const IBonusBearer * target):
|
||||||
|
target(target)
|
||||||
|
{}
|
||||||
|
|
||||||
|
struct BonusCacheEntry
|
||||||
|
{
|
||||||
|
std::atomic<int64_t> version = 0;
|
||||||
|
std::atomic<int64_t> value = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
int getBonusValueImpl(BonusCacheEntry & currentValue, const CSelector & selector) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Cache that tracks a single query to bonus system
|
||||||
|
class BonusValueCache : public BonusCacheBase
|
||||||
|
{
|
||||||
|
CSelector selector;
|
||||||
|
mutable BonusCacheEntry value;
|
||||||
|
public:
|
||||||
|
BonusValueCache(const IBonusBearer * target, const CSelector selector);
|
||||||
|
int getValue() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Cache that can track a list of queries to bonus system
|
||||||
|
template<typename EnumType, size_t SIZE>
|
||||||
|
class BonusValuesArrayCache : public BonusCacheBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using SelectorsArray = std::array<const CSelector, SIZE>;
|
||||||
|
|
||||||
|
BonusValuesArrayCache(const IBonusBearer * target, const SelectorsArray * selectors)
|
||||||
|
: BonusCacheBase(target)
|
||||||
|
, selectors(selectors)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int getBonusValue(EnumType which) const
|
||||||
|
{
|
||||||
|
auto index = static_cast<size_t>(which);
|
||||||
|
return getBonusValueImpl(cache[index], (*selectors)[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using CacheArray = std::array<BonusCacheEntry, SIZE>;
|
||||||
|
|
||||||
|
const SelectorsArray * selectors;
|
||||||
|
mutable CacheArray cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Cache that tracks values of primary skill values in bonus system
|
||||||
|
class PrimarySkillsCache
|
||||||
|
{
|
||||||
|
const IBonusBearer * target;
|
||||||
|
mutable std::atomic<int64_t> version = 0;
|
||||||
|
mutable std::array<std::atomic<int32_t>, 4> skills;
|
||||||
|
|
||||||
|
void update() const;
|
||||||
|
public:
|
||||||
|
PrimarySkillsCache(const IBonusBearer * target);
|
||||||
|
|
||||||
|
const std::array<std::atomic<int32_t>, 4> & getSkills() const;
|
||||||
|
};
|
@ -9,179 +9,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "BonusList.h"
|
|
||||||
#include "CBonusProxy.h"
|
#include "CBonusProxy.h"
|
||||||
#include "IBonusBearer.h"
|
#include "IBonusBearer.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
///CBonusProxy
|
|
||||||
CBonusProxy::CBonusProxy(const IBonusBearer * Target, CSelector Selector):
|
|
||||||
bonusListCachedLast(0),
|
|
||||||
target(Target),
|
|
||||||
selector(std::move(Selector)),
|
|
||||||
currentBonusListIndex(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusProxy::CBonusProxy(const CBonusProxy & other):
|
|
||||||
bonusListCachedLast(other.bonusListCachedLast),
|
|
||||||
target(other.target),
|
|
||||||
selector(other.selector),
|
|
||||||
currentBonusListIndex(other.currentBonusListIndex)
|
|
||||||
{
|
|
||||||
bonusList[currentBonusListIndex] = other.bonusList[currentBonusListIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusProxy::CBonusProxy(CBonusProxy && other) noexcept:
|
|
||||||
bonusListCachedLast(0),
|
|
||||||
target(other.target),
|
|
||||||
currentBonusListIndex(0)
|
|
||||||
{
|
|
||||||
std::swap(bonusListCachedLast, other.bonusListCachedLast);
|
|
||||||
std::swap(selector, other.selector);
|
|
||||||
std::swap(bonusList, other.bonusList);
|
|
||||||
std::swap(currentBonusListIndex, other.currentBonusListIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusProxy & CBonusProxy::operator=(const CBonusProxy & other)
|
|
||||||
{
|
|
||||||
boost::lock_guard<boost::mutex> lock(swapGuard);
|
|
||||||
|
|
||||||
selector = other.selector;
|
|
||||||
swapBonusList(other.bonusList[other.currentBonusListIndex]);
|
|
||||||
bonusListCachedLast = other.bonusListCachedLast;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusProxy & CBonusProxy::operator=(CBonusProxy && other) noexcept
|
|
||||||
{
|
|
||||||
std::swap(bonusListCachedLast, other.bonusListCachedLast);
|
|
||||||
std::swap(selector, other.selector);
|
|
||||||
std::swap(bonusList, other.bonusList);
|
|
||||||
std::swap(currentBonusListIndex, other.currentBonusListIndex);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusProxy::swapBonusList(TConstBonusListPtr other) const
|
|
||||||
{
|
|
||||||
// The idea here is to avoid changing active bonusList while it can be read by a different thread.
|
|
||||||
// Because such use of shared ptr is not thread safe
|
|
||||||
// So to avoid this we change the second offline instance and swap active index
|
|
||||||
auto newCurrent = 1 - currentBonusListIndex;
|
|
||||||
bonusList[newCurrent] = std::move(other);
|
|
||||||
currentBonusListIndex = newCurrent;
|
|
||||||
}
|
|
||||||
|
|
||||||
TConstBonusListPtr CBonusProxy::getBonusList() const
|
|
||||||
{
|
|
||||||
auto needUpdateBonusList = [&]() -> bool
|
|
||||||
{
|
|
||||||
return target->getTreeVersion() != bonusListCachedLast || !bonusList[currentBonusListIndex];
|
|
||||||
};
|
|
||||||
|
|
||||||
// avoid locking if everything is up-to-date
|
|
||||||
if(needUpdateBonusList())
|
|
||||||
{
|
|
||||||
boost::lock_guard<boost::mutex>lock(swapGuard);
|
|
||||||
|
|
||||||
if(needUpdateBonusList())
|
|
||||||
{
|
|
||||||
//TODO: support limiters
|
|
||||||
swapBonusList(target->getAllBonuses(selector, Selector::all));
|
|
||||||
bonusListCachedLast = target->getTreeVersion();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bonusList[currentBonusListIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
const BonusList * CBonusProxy::operator->() const
|
|
||||||
{
|
|
||||||
return getBonusList().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
CTotalsProxy::CTotalsProxy(const IBonusBearer * Target, CSelector Selector, int InitialValue):
|
|
||||||
CBonusProxy(Target, std::move(Selector)),
|
|
||||||
initialValue(InitialValue),
|
|
||||||
meleeCachedLast(0),
|
|
||||||
meleeValue(0),
|
|
||||||
rangedCachedLast(0),
|
|
||||||
rangedValue(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CTotalsProxy::CTotalsProxy(const CTotalsProxy & other)
|
|
||||||
: CBonusProxy(other),
|
|
||||||
initialValue(other.initialValue),
|
|
||||||
meleeCachedLast(other.meleeCachedLast),
|
|
||||||
meleeValue(other.meleeValue),
|
|
||||||
rangedCachedLast(other.rangedCachedLast),
|
|
||||||
rangedValue(other.rangedValue)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int CTotalsProxy::getValue() const
|
|
||||||
{
|
|
||||||
const auto treeVersion = target->getTreeVersion();
|
|
||||||
|
|
||||||
if(treeVersion != valueCachedLast)
|
|
||||||
{
|
|
||||||
auto bonuses = getBonusList();
|
|
||||||
|
|
||||||
value = initialValue + bonuses->totalValue();
|
|
||||||
valueCachedLast = treeVersion;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CTotalsProxy::getValueAndList(TConstBonusListPtr & outBonusList) const
|
|
||||||
{
|
|
||||||
const auto treeVersion = target->getTreeVersion();
|
|
||||||
outBonusList = getBonusList();
|
|
||||||
|
|
||||||
if(treeVersion != valueCachedLast)
|
|
||||||
{
|
|
||||||
value = initialValue + outBonusList->totalValue();
|
|
||||||
valueCachedLast = treeVersion;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CTotalsProxy::getMeleeValue() const
|
|
||||||
{
|
|
||||||
static const auto limit = Selector::effectRange()(BonusLimitEffect::NO_LIMIT).Or(Selector::effectRange()(BonusLimitEffect::ONLY_MELEE_FIGHT));
|
|
||||||
|
|
||||||
const auto treeVersion = target->getTreeVersion();
|
|
||||||
|
|
||||||
if(treeVersion != meleeCachedLast)
|
|
||||||
{
|
|
||||||
auto bonuses = target->getBonuses(selector, limit);
|
|
||||||
meleeValue = initialValue + bonuses->totalValue();
|
|
||||||
meleeCachedLast = treeVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
return meleeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CTotalsProxy::getRangedValue() const
|
|
||||||
{
|
|
||||||
static const auto limit = Selector::effectRange()(BonusLimitEffect::NO_LIMIT).Or(Selector::effectRange()(BonusLimitEffect::ONLY_DISTANCE_FIGHT));
|
|
||||||
|
|
||||||
const auto treeVersion = target->getTreeVersion();
|
|
||||||
|
|
||||||
if(treeVersion != rangedCachedLast)
|
|
||||||
{
|
|
||||||
auto bonuses = target->getBonuses(selector, limit);
|
|
||||||
rangedValue = initialValue + bonuses->totalValue();
|
|
||||||
rangedCachedLast = treeVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rangedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
///CCheckProxy
|
///CCheckProxy
|
||||||
CCheckProxy::CCheckProxy(const IBonusBearer * Target, BonusType bonusType):
|
CCheckProxy::CCheckProxy(const IBonusBearer * Target, BonusType bonusType):
|
||||||
target(Target),
|
target(Target),
|
||||||
|
@ -10,66 +10,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Bonus.h"
|
|
||||||
#include "BonusSelector.h"
|
#include "BonusSelector.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
class DLL_LINKAGE CBonusProxy
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CBonusProxy(const IBonusBearer * Target, CSelector Selector);
|
|
||||||
CBonusProxy(const CBonusProxy & other);
|
|
||||||
CBonusProxy(CBonusProxy && other) noexcept;
|
|
||||||
|
|
||||||
CBonusProxy & operator=(CBonusProxy && other) noexcept;
|
|
||||||
CBonusProxy & operator=(const CBonusProxy & other);
|
|
||||||
const BonusList * operator->() const;
|
|
||||||
TConstBonusListPtr getBonusList() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
CSelector selector;
|
|
||||||
const IBonusBearer * target;
|
|
||||||
mutable int64_t bonusListCachedLast;
|
|
||||||
mutable TConstBonusListPtr bonusList[2];
|
|
||||||
mutable int currentBonusListIndex;
|
|
||||||
mutable boost::mutex swapGuard;
|
|
||||||
void swapBonusList(TConstBonusListPtr other) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE CTotalsProxy : public CBonusProxy
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CTotalsProxy(const IBonusBearer * Target, CSelector Selector, int InitialValue);
|
|
||||||
CTotalsProxy(const CTotalsProxy & other);
|
|
||||||
CTotalsProxy(CTotalsProxy && other) = delete;
|
|
||||||
|
|
||||||
CTotalsProxy & operator=(const CTotalsProxy & other) = default;
|
|
||||||
CTotalsProxy & operator=(CTotalsProxy && other) = delete;
|
|
||||||
|
|
||||||
int getMeleeValue() const;
|
|
||||||
int getRangedValue() const;
|
|
||||||
int getValue() const;
|
|
||||||
/**
|
|
||||||
Returns total value of all selected bonuses and sets bonusList as a pointer to the list of selected bonuses
|
|
||||||
@param bonusList is the out list of all selected bonuses
|
|
||||||
@return total value of all selected bonuses and 0 otherwise
|
|
||||||
*/
|
|
||||||
int getValueAndList(TConstBonusListPtr & bonusList) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int initialValue;
|
|
||||||
|
|
||||||
mutable int64_t valueCachedLast = 0;
|
|
||||||
mutable int value = 0;
|
|
||||||
|
|
||||||
mutable int64_t meleeCachedLast;
|
|
||||||
mutable int meleeValue;
|
|
||||||
|
|
||||||
mutable int64_t rangedCachedLast;
|
|
||||||
mutable int rangedValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE CCheckProxy
|
class DLL_LINKAGE CCheckProxy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -293,6 +293,7 @@ CGHeroInstance::CGHeroInstance(IGameCallback * cb)
|
|||||||
level(1),
|
level(1),
|
||||||
exp(UNINITIALIZED_EXPERIENCE),
|
exp(UNINITIALIZED_EXPERIENCE),
|
||||||
gender(EHeroGender::DEFAULT),
|
gender(EHeroGender::DEFAULT),
|
||||||
|
primarySkills(this),
|
||||||
lowestCreatureSpeed(0)
|
lowestCreatureSpeed(0)
|
||||||
{
|
{
|
||||||
setNodeType(HERO);
|
setNodeType(HERO);
|
||||||
@ -704,40 +705,20 @@ void CGHeroInstance::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
|
|||||||
setStackCount(SlotID(0), identifier.getNum());
|
setStackCount(SlotID(0), identifier.getNum());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<int, 4> CGHeroInstance::getPrimarySkills() const
|
int CGHeroInstance::getPrimSkillLevel(PrimarySkill id) const
|
||||||
{
|
{
|
||||||
std::array<int, 4> result;
|
return primarySkills.getSkills()[id];
|
||||||
|
|
||||||
auto allSkills = getBonusBearer()->getBonusesOfType(BonusType::PRIMARY_SKILL);
|
|
||||||
for (auto skill : PrimarySkill::ALL_SKILLS())
|
|
||||||
{
|
|
||||||
int ret = allSkills->valOfBonuses(Selector::subtype()(BonusSubtypeID(skill)));
|
|
||||||
int minSkillValue = VLC->engineSettings()->getVectorValue(EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, skill.getNum());
|
|
||||||
result[skill] = std::max(ret, minSkillValue); //otherwise, some artifacts may cause negative skill value effect
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double CGHeroInstance::getFightingStrength() const
|
double CGHeroInstance::getFightingStrength() const
|
||||||
{
|
{
|
||||||
const auto & primarySkills = getPrimarySkills();
|
const auto & skillValues = primarySkills.getSkills();
|
||||||
return getFightingStrengthImpl(primarySkills);
|
return sqrt((1.0 + 0.05*skillValues[PrimarySkill::ATTACK]) * (1.0 + 0.05*skillValues[PrimarySkill::DEFENSE]));
|
||||||
}
|
|
||||||
|
|
||||||
double CGHeroInstance::getFightingStrengthImpl(const std::array<int, 4> & primarySkills) const
|
|
||||||
{
|
|
||||||
return sqrt((1.0 + 0.05*primarySkills[PrimarySkill::ATTACK]) * (1.0 + 0.05*primarySkills[PrimarySkill::DEFENSE]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double CGHeroInstance::getMagicStrength() const
|
double CGHeroInstance::getMagicStrength() const
|
||||||
{
|
{
|
||||||
const auto & primarySkills = getPrimarySkills();
|
const auto & skillValues = primarySkills.getSkills();
|
||||||
return getMagicStrengthImpl(primarySkills);
|
|
||||||
}
|
|
||||||
|
|
||||||
double CGHeroInstance::getMagicStrengthImpl(const std::array<int, 4> & primarySkills) const
|
|
||||||
{
|
|
||||||
if (!hasSpellbook())
|
if (!hasSpellbook())
|
||||||
return 1;
|
return 1;
|
||||||
bool atLeastOneCombatSpell = false;
|
bool atLeastOneCombatSpell = false;
|
||||||
@ -751,13 +732,12 @@ double CGHeroInstance::getMagicStrengthImpl(const std::array<int, 4> & primarySk
|
|||||||
}
|
}
|
||||||
if (!atLeastOneCombatSpell)
|
if (!atLeastOneCombatSpell)
|
||||||
return 1;
|
return 1;
|
||||||
return sqrt((1.0 + 0.05*primarySkills[PrimarySkill::KNOWLEDGE] * mana / manaLimit()) * (1.0 + 0.05*primarySkills[PrimarySkill::SPELL_POWER] * mana / manaLimit()));
|
return sqrt((1.0 + 0.05*skillValues[PrimarySkill::KNOWLEDGE] * mana / manaLimit()) * (1.0 + 0.05*skillValues[PrimarySkill::SPELL_POWER] * mana / manaLimit()));
|
||||||
}
|
}
|
||||||
|
|
||||||
double CGHeroInstance::getHeroStrength() const
|
double CGHeroInstance::getHeroStrength() const
|
||||||
{
|
{
|
||||||
const auto & primarySkills = getPrimarySkills();
|
return getFightingStrength() * getMagicStrength();
|
||||||
return getFightingStrengthImpl(primarySkills) * getMagicStrengthImpl(primarySkills);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t CGHeroInstance::getValueForDiplomacy() const
|
uint64_t CGHeroInstance::getValueForDiplomacy() const
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "CArmedInstance.h"
|
#include "CArmedInstance.h"
|
||||||
#include "IOwnableObject.h"
|
#include "IOwnableObject.h"
|
||||||
|
|
||||||
|
#include "../bonuses/BonusCache.h"
|
||||||
#include "../entities/hero/EHeroGender.h"
|
#include "../entities/hero/EHeroGender.h"
|
||||||
#include "../CArtHandler.h" // For CArtifactSet
|
#include "../CArtHandler.h" // For CArtifactSet
|
||||||
|
|
||||||
@ -58,13 +59,12 @@ class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator,
|
|||||||
friend class CMapFormatJson;
|
friend class CMapFormatJson;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
PrimarySkillsCache primarySkills;
|
||||||
|
|
||||||
std::set<SpellID> spells; //known spells (spell IDs)
|
std::set<SpellID> spells; //known spells (spell IDs)
|
||||||
mutable int lowestCreatureSpeed;
|
mutable int lowestCreatureSpeed;
|
||||||
ui32 movement; //remaining movement points
|
ui32 movement; //remaining movement points
|
||||||
|
|
||||||
double getFightingStrengthImpl(const std::array<int, 4> & primarySkills) const;
|
|
||||||
double getMagicStrengthImpl(const std::array<int, 4> & primarySkills) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
@ -204,7 +204,7 @@ public:
|
|||||||
std::vector<SecondarySkill> getLevelUpProposedSecondarySkills(vstd::RNG & rand) const;
|
std::vector<SecondarySkill> getLevelUpProposedSecondarySkills(vstd::RNG & rand) const;
|
||||||
|
|
||||||
ui8 getSecSkillLevel(const SecondarySkill & skill) const; //0 - no skill
|
ui8 getSecSkillLevel(const SecondarySkill & skill) const; //0 - no skill
|
||||||
std::array<int, 4> getPrimarySkills() const;
|
int getPrimSkillLevel(PrimarySkill id) const;
|
||||||
|
|
||||||
/// Returns true if hero has free secondary skill slot.
|
/// Returns true if hero has free secondary skill slot.
|
||||||
bool canLearnSkill() const;
|
bool canLearnSkill() const;
|
||||||
|
@ -257,7 +257,7 @@ bool BattleActionProcessor::doAttackAction(const CBattleInfoCallback & battle, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
//attack
|
//attack
|
||||||
int totalAttacks = stack->totalAttacks.getMeleeValue();
|
int totalAttacks = stack->getTotalAttacks(false);
|
||||||
|
|
||||||
//TODO: move to CUnitState
|
//TODO: move to CUnitState
|
||||||
const auto * attackingHero = battle.battleGetFightingHero(ba.side);
|
const auto * attackingHero = battle.battleGetFightingHero(ba.side);
|
||||||
@ -378,7 +378,7 @@ bool BattleActionProcessor::doShootAction(const CBattleInfoCallback & battle, co
|
|||||||
}
|
}
|
||||||
//allow more than one additional attack
|
//allow more than one additional attack
|
||||||
|
|
||||||
int totalRangedAttacks = stack->totalAttacks.getRangedValue();
|
int totalRangedAttacks = stack->getTotalAttacks(true);
|
||||||
|
|
||||||
//TODO: move to CUnitState
|
//TODO: move to CUnitState
|
||||||
const auto * attackingHero = battle.battleGetFightingHero(ba.side);
|
const auto * attackingHero = battle.battleGetFightingHero(ba.side);
|
||||||
|
Reference in New Issue
Block a user