mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
Merge remote-tracking branch 'upstream/develop' into town-buildings
# Conflicts: # lib/CTownHandler.cpp # lib/rewardable/Interface.cpp
This commit is contained in:
commit
ce80c83ca2
@ -51,11 +51,11 @@ int64_t AttackPossibility::calculateDamageReduce(
|
||||
|
||||
// FIXME: provide distance info for Jousting bonus
|
||||
auto enemyDamageBeforeAttack = cb.battleEstimateDamage(defender, attacker, 0);
|
||||
auto enemiesKilled = damageDealt / defender->MaxHealth() + (damageDealt % defender->MaxHealth() >= defender->getFirstHPleft() ? 1 : 0);
|
||||
auto enemiesKilled = damageDealt / defender->getMaxHealth() + (damageDealt % defender->getMaxHealth() >= defender->getFirstHPleft() ? 1 : 0);
|
||||
auto enemyDamage = averageDmg(enemyDamageBeforeAttack.damage);
|
||||
auto damagePerEnemy = enemyDamage / (double)defender->getCount();
|
||||
|
||||
return (int64_t)(damagePerEnemy * (enemiesKilled * KILL_BOUNTY + damageDealt * HEALTH_BOUNTY / (double)defender->MaxHealth()));
|
||||
return (int64_t)(damagePerEnemy * (enemiesKilled * KILL_BOUNTY + damageDealt * HEALTH_BOUNTY / (double)defender->getMaxHealth()));
|
||||
}
|
||||
|
||||
int64_t AttackPossibility::evaluateBlockedShootersDmg(const BattleAttackInfo & attackInfo, BattleHex hex, const HypotheticBattle & state)
|
||||
@ -97,7 +97,7 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo & attackInf
|
||||
auto attacker = attackInfo.attacker;
|
||||
auto defender = attackInfo.defender;
|
||||
const std::string cachingStringBlocksRetaliation = "type_BLOCKS_RETALIATION";
|
||||
static const auto selectorBlocksRetaliation = Selector::type()(Bonus::BLOCKS_RETALIATION);
|
||||
static const auto selectorBlocksRetaliation = Selector::type()(BonusType::BLOCKS_RETALIATION);
|
||||
const auto attackerSide = state.playerToSide(state.battleGetOwner(attacker));
|
||||
const bool counterAttacksBlocked = attacker->hasBonus(selectorBlocksRetaliation, cachingStringBlocksRetaliation);
|
||||
|
||||
|
@ -104,12 +104,12 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
||||
{
|
||||
if(stack->creatureId() == CreatureID::CATAPULT)
|
||||
return useCatapult(stack);
|
||||
if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON) && stack->hasBonusOfType(Bonus::HEALER))
|
||||
if(stack->hasBonusOfType(BonusType::SIEGE_WEAPON) && stack->hasBonusOfType(BonusType::HEALER))
|
||||
{
|
||||
auto healingTargets = cb->battleGetStacks(CBattleInfoEssentials::ONLY_MINE);
|
||||
std::map<int, const CStack*> woundHpToStack;
|
||||
for(auto stack : healingTargets)
|
||||
if(auto woundHp = stack->MaxHealth() - stack->getFirstHPleft())
|
||||
if(auto woundHp = stack->getMaxHealth() - stack->getFirstHPleft())
|
||||
woundHpToStack[woundHp] = stack;
|
||||
if(woundHpToStack.empty())
|
||||
return BattleAction::makeDefend(stack);
|
||||
@ -137,7 +137,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
||||
std::optional<PossibleSpellcast> bestSpellcast(std::nullopt);
|
||||
//TODO: faerie dragon type spell should be selected by server
|
||||
SpellID creatureSpellToCast = cb->battleGetRandomStackSpell(CRandomGenerator::getDefault(), stack, CBattleInfoCallback::RANDOM_AIMED);
|
||||
if(stack->hasBonusOfType(Bonus::SPELLCASTER) && stack->canCast() && creatureSpellToCast != SpellID::NONE)
|
||||
if(stack->hasBonusOfType(BonusType::SPELLCASTER) && stack->canCast() && creatureSpellToCast != SpellID::NONE)
|
||||
{
|
||||
const CSpell * spell = creatureSpellToCast.toSpell();
|
||||
|
||||
@ -214,7 +214,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
||||
bestAttack.attackerState->unitType()->getJsonKey(),
|
||||
bestAttack.affectedUnits[0]->unitType()->getJsonKey(),
|
||||
(int)bestAttack.affectedUnits[0]->getCount(), action, (int)bestAttack.from, (int)bestAttack.attack.attacker->getPosition().hex,
|
||||
bestAttack.attack.chargeDistance, bestAttack.attack.attacker->Speed(0, true),
|
||||
bestAttack.attack.chargeDistance, bestAttack.attack.attacker->speed(0, true),
|
||||
bestAttack.defenderDamageReduce, bestAttack.attackerDamageReduce, bestAttack.attackValue()
|
||||
);
|
||||
}
|
||||
@ -241,7 +241,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
||||
}
|
||||
|
||||
if(score <= EvaluationResult::INEFFECTIVE_SCORE
|
||||
&& !stack->hasBonusOfType(Bonus::FLYING)
|
||||
&& !stack->hasBonusOfType(BonusType::FLYING)
|
||||
&& stack->unitSide() == BattleSide::ATTACKER
|
||||
&& cb->battleGetSiegeLevel() >= CGTownInstance::CITADEL)
|
||||
{
|
||||
@ -321,7 +321,7 @@ BattleAction CBattleAI::goTowardsNearest(const CStack * stack, std::vector<Battl
|
||||
|
||||
scoreEvaluator.updateReachabilityMap(hb);
|
||||
|
||||
if(stack->hasBonusOfType(Bonus::FLYING))
|
||||
if(stack->hasBonusOfType(BonusType::FLYING))
|
||||
{
|
||||
std::set<BattleHex> obstacleHexes;
|
||||
|
||||
|
@ -65,7 +65,7 @@ int64_t BattleExchangeVariant::trackAttack(
|
||||
bool evaluateOnly)
|
||||
{
|
||||
const std::string cachingStringBlocksRetaliation = "type_BLOCKS_RETALIATION";
|
||||
static const auto selectorBlocksRetaliation = Selector::type()(Bonus::BLOCKS_RETALIATION);
|
||||
static const auto selectorBlocksRetaliation = Selector::type()(BonusType::BLOCKS_RETALIATION);
|
||||
const bool counterAttacksBlocked = attacker->hasBonus(selectorBlocksRetaliation, cachingStringBlocksRetaliation);
|
||||
|
||||
DamageEstimation retaliation;
|
||||
@ -205,7 +205,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(const battle::Uni
|
||||
if(targets.unreachableEnemies.empty())
|
||||
return result;
|
||||
|
||||
auto speed = activeStack->Speed();
|
||||
auto speed = activeStack->speed();
|
||||
|
||||
if(speed == 0)
|
||||
return result;
|
||||
@ -607,7 +607,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb)
|
||||
|
||||
for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1)
|
||||
{
|
||||
bool reachable = unitReachability.distances[hex] <= unit->Speed(turn);
|
||||
bool reachable = unitReachability.distances[hex] <= unit->speed(turn);
|
||||
|
||||
if(!reachable && unitReachability.accessibility[hex] == EAccessibility::ALIVE_STACK)
|
||||
{
|
||||
@ -617,7 +617,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb)
|
||||
{
|
||||
for(BattleHex neighbor : hex.neighbouringTiles())
|
||||
{
|
||||
reachable = unitReachability.distances[neighbor] <= unit->Speed(turn);
|
||||
reachable = unitReachability.distances[neighbor] <= unit->speed(turn);
|
||||
|
||||
if(reachable) break;
|
||||
}
|
||||
@ -665,7 +665,7 @@ bool BattleExchangeEvaluator::checkPositionBlocksOurStacks(HypotheticBattle & hb
|
||||
for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1)
|
||||
{
|
||||
bool enemyUnit = false;
|
||||
bool reachable = unitReachability.distances[hex] <= unit->Speed(turn);
|
||||
bool reachable = unitReachability.distances[hex] <= unit->speed(turn);
|
||||
|
||||
if(!reachable && unitReachability.accessibility[hex] == EAccessibility::ALIVE_STACK)
|
||||
{
|
||||
@ -677,7 +677,7 @@ bool BattleExchangeEvaluator::checkPositionBlocksOurStacks(HypotheticBattle & hb
|
||||
|
||||
for(BattleHex neighbor : hex.neighbouringTiles())
|
||||
{
|
||||
reachable = unitReachability.distances[neighbor] <= unit->Speed(turn);
|
||||
reachable = unitReachability.distances[neighbor] <= unit->speed(turn);
|
||||
|
||||
if(reachable) break;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ PotentialTargets::PotentialTargets(const battle::Unit * attacker, const Hypothet
|
||||
const battle::Unit * forcedTarget = nullptr;
|
||||
BattleHex forcedHex;
|
||||
|
||||
if(attackerInfo->hasBonusOfType(Bonus::ATTACKS_NEAREST_CREATURE))
|
||||
if(attackerInfo->hasBonusOfType(BonusType::ATTACKS_NEAREST_CREATURE))
|
||||
{
|
||||
forceTarget = true;
|
||||
auto nearest = state.getNearestStack(attackerInfo);
|
||||
|
@ -24,7 +24,7 @@ void actualizeEffect(TBonusListPtr target, const Bonus & ef)
|
||||
{
|
||||
for(auto & bonus : *target) //TODO: optimize
|
||||
{
|
||||
if(bonus->source == Bonus::SPELL_EFFECT && bonus->type == ef.type && bonus->subtype == ef.subtype)
|
||||
if(bonus->source == BonusSource::SPELL_EFFECT && bonus->type == ef.type && bonus->subtype == ef.subtype)
|
||||
{
|
||||
if(bonus->turnsRemain < ef.turnsRemain)
|
||||
{
|
||||
@ -36,9 +36,9 @@ void actualizeEffect(TBonusListPtr target, const Bonus & ef)
|
||||
}
|
||||
}
|
||||
|
||||
StackWithBonuses::StackWithBonuses(const HypotheticBattle * Owner, const CStack * Stack)
|
||||
StackWithBonuses::StackWithBonuses(const HypotheticBattle * Owner, const battle::CUnitState * Stack)
|
||||
: battle::CUnitState(),
|
||||
origBearer(Stack),
|
||||
origBearer(Stack->getBonusBearer()),
|
||||
owner(Owner),
|
||||
type(Stack->unitType()),
|
||||
baseAmount(Stack->unitBaseAmount()),
|
||||
@ -126,7 +126,7 @@ TConstBonusListPtr StackWithBonuses::getAllBonuses(const CSelector & selector, c
|
||||
{
|
||||
if(selector(&bonus) && (!limit || !limit(&bonus)))
|
||||
{
|
||||
if(ret->getFirst(Selector::source(Bonus::SPELL_EFFECT, bonus.sid).And(Selector::typeSubtype(bonus.type, bonus.subtype))))
|
||||
if(ret->getFirst(Selector::source(BonusSource::SPELL_EFFECT, bonus.sid).And(Selector::typeSubtype(bonus.type, bonus.subtype))))
|
||||
{
|
||||
actualizeEffect(ret, bonus);
|
||||
}
|
||||
|
@ -14,16 +14,10 @@
|
||||
#include <vcmi/Environment.h>
|
||||
#include <vcmi/ServerCallback.h>
|
||||
|
||||
#include "../../lib/HeroBonus.h"
|
||||
#include "../../lib/bonuses/Bonus.h"
|
||||
#include "../../lib/battle/BattleProxy.h"
|
||||
#include "../../lib/battle/CUnitState.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CStack;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class HypotheticBattle;
|
||||
|
||||
///Fake random generator, used by AI to evaluate random server behavior
|
||||
@ -54,7 +48,7 @@ public:
|
||||
std::vector<Bonus> bonusesToUpdate;
|
||||
std::set<std::shared_ptr<Bonus>> bonusesToRemove;
|
||||
|
||||
StackWithBonuses(const HypotheticBattle * Owner, const CStack * Stack);
|
||||
StackWithBonuses(const HypotheticBattle * Owner, const battle::CUnitState * Stack);
|
||||
|
||||
StackWithBonuses(const HypotheticBattle * Owner, const battle::UnitInfo & info);
|
||||
|
||||
|
@ -307,7 +307,7 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
|
||||
auto art2 = a2->artType;
|
||||
|
||||
if(art1->price == art2->price)
|
||||
return art1->valOfBonuses(Bonus::PRIMARY_SKILL) > art2->valOfBonuses(Bonus::PRIMARY_SKILL);
|
||||
return art1->valOfBonuses(BonusType::PRIMARY_SKILL) > art2->valOfBonuses(BonusType::PRIMARY_SKILL);
|
||||
else
|
||||
return art1->price > art2->price;
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ std::vector<SlotInfo>::iterator ArmyManager::getWeakestCreature(std::vector<Slot
|
||||
if(left.creature->getLevel() != right.creature->getLevel())
|
||||
return left.creature->getLevel() < right.creature->getLevel();
|
||||
|
||||
return left.creature->Speed() > right.creature->Speed();
|
||||
return left.creature->speed() > right.creature->speed();
|
||||
});
|
||||
|
||||
return weakest;
|
||||
@ -108,12 +108,12 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
||||
uint64_t armyValue = 0;
|
||||
|
||||
TemporaryArmy newArmyInstance;
|
||||
auto bonusModifiers = armyCarrier->getBonuses(Selector::type()(Bonus::MORALE));
|
||||
auto bonusModifiers = armyCarrier->getBonuses(Selector::type()(BonusType::MORALE));
|
||||
|
||||
for(auto bonus : *bonusModifiers)
|
||||
{
|
||||
// army bonuses will change and object bonuses are temporary
|
||||
if(bonus->source != Bonus::ARMY && bonus->source != Bonus::OBJECT)
|
||||
if(bonus->source != BonusSource::ARMY && bonus->source != BonusSource::OBJECT)
|
||||
{
|
||||
newArmyInstance.addNewBonus(std::make_shared<Bonus>(*bonus));
|
||||
}
|
||||
@ -150,7 +150,7 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
||||
|
||||
for(auto & slot : newArmyInstance.Slots())
|
||||
{
|
||||
auto morale = slot.second->MoraleVal();
|
||||
auto morale = slot.second->moraleVal();
|
||||
auto multiplier = 1.0f;
|
||||
|
||||
const float BadMoraleChance = 0.083f;
|
||||
|
@ -72,10 +72,10 @@ float HeroManager::evaluateSecSkill(SecondarySkill skill, const CGHeroInstance *
|
||||
|
||||
float HeroManager::evaluateSpeciality(const CGHeroInstance * hero) const
|
||||
{
|
||||
auto heroSpecial = Selector::source(Bonus::HERO_SPECIAL, hero->type->getIndex());
|
||||
auto secondarySkillBonus = Selector::targetSourceType()(Bonus::SECONDARY_SKILL);
|
||||
auto heroSpecial = Selector::source(BonusSource::HERO_SPECIAL, hero->type->getIndex());
|
||||
auto secondarySkillBonus = Selector::targetSourceType()(BonusSource::SECONDARY_SKILL);
|
||||
auto specialSecondarySkillBonuses = hero->getBonuses(heroSpecial.And(secondarySkillBonus));
|
||||
auto secondarySkillBonuses = hero->getBonuses(Selector::sourceTypeSel(Bonus::SECONDARY_SKILL));
|
||||
auto secondarySkillBonuses = hero->getBonuses(Selector::sourceTypeSel(BonusSource::SECONDARY_SKILL));
|
||||
float specialityScore = 0.0f;
|
||||
|
||||
for(auto bonus : *secondarySkillBonuses)
|
||||
|
@ -53,14 +53,14 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army)
|
||||
double shootersStrength = 0;
|
||||
ui32 maxSpeed = 0;
|
||||
|
||||
static const CSelector selectorSHOOTER = Selector::type()(Bonus::SHOOTER);
|
||||
static const std::string keySHOOTER = "type_"+std::to_string((int32_t)Bonus::SHOOTER);
|
||||
static const CSelector selectorSHOOTER = Selector::type()(BonusType::SHOOTER);
|
||||
static const std::string keySHOOTER = "type_"+std::to_string((int32_t)BonusType::SHOOTER);
|
||||
|
||||
static const CSelector selectorFLYING = Selector::type()(Bonus::FLYING);
|
||||
static const std::string keyFLYING = "type_"+std::to_string((int32_t)Bonus::FLYING);
|
||||
static const CSelector selectorFLYING = Selector::type()(BonusType::FLYING);
|
||||
static const std::string keyFLYING = "type_"+std::to_string((int32_t)BonusType::FLYING);
|
||||
|
||||
static const CSelector selectorSTACKS_SPEED = Selector::type()(Bonus::STACKS_SPEED);
|
||||
static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)Bonus::STACKS_SPEED);
|
||||
static const CSelector selectorSTACKS_SPEED = Selector::type()(BonusType::STACKS_SPEED);
|
||||
static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)BonusType::STACKS_SPEED);
|
||||
|
||||
for(auto s : army->Slots())
|
||||
{
|
||||
|
@ -210,14 +210,14 @@ uint64_t evaluateArtifactArmyValue(CArtifactInstance * art)
|
||||
return 1500;
|
||||
|
||||
auto statsValue =
|
||||
10 * art->valOfBonuses(Bonus::MOVEMENT, 1)
|
||||
+ 1200 * art->valOfBonuses(Bonus::STACKS_SPEED)
|
||||
+ 700 * art->valOfBonuses(Bonus::MORALE)
|
||||
+ 700 * art->getAttack(false)
|
||||
+ 700 * art->getDefense(false)
|
||||
+ 700 * art->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::KNOWLEDGE)
|
||||
+ 700 * art->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::SPELL_POWER)
|
||||
+ 500 * art->valOfBonuses(Bonus::LUCK);
|
||||
10 * art->valOfBonuses(BonusType::MOVEMENT, 1)
|
||||
+ 1200 * art->valOfBonuses(BonusType::STACKS_SPEED)
|
||||
+ 700 * art->valOfBonuses(BonusType::MORALE)
|
||||
+ 700 * art->valOfBonuses(BonusType::PRIMARY_SKILL, PrimarySkill::ATTACK)
|
||||
+ 700 * art->valOfBonuses(BonusType::PRIMARY_SKILL, PrimarySkill::DEFENSE)
|
||||
+ 700 * art->valOfBonuses(BonusType::PRIMARY_SKILL, PrimarySkill::KNOWLEDGE)
|
||||
+ 700 * art->valOfBonuses(BonusType::PRIMARY_SKILL, PrimarySkill::SPELL_POWER)
|
||||
+ 500 * art->valOfBonuses(BonusType::LUCK);
|
||||
|
||||
auto classValue = 0;
|
||||
|
||||
|
@ -107,7 +107,7 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
|
||||
|
||||
return attack;
|
||||
}
|
||||
else if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON))
|
||||
else if(stack->hasBonusOfType(BonusType::SIEGE_WEAPON))
|
||||
{
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
@ -270,7 +270,7 @@ BattleAction CStupidAI::goTowards(const CStack * stack, std::vector<BattleHex> h
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
|
||||
if(stack->hasBonusOfType(Bonus::FLYING))
|
||||
if(stack->hasBonusOfType(BonusType::FLYING))
|
||||
{
|
||||
// Flying stack doesn't go hex by hex, so we can't backtrack using predecessors.
|
||||
// We just check all available hexes and pick the one closest to the target.
|
||||
|
@ -257,7 +257,7 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
|
||||
auto art2 = a2->artType;
|
||||
|
||||
if(art1->price == art2->price)
|
||||
return art1->valOfBonuses(Bonus::PRIMARY_SKILL) > art2->valOfBonuses(Bonus::PRIMARY_SKILL);
|
||||
return art1->valOfBonuses(BonusType::PRIMARY_SKILL) > art2->valOfBonuses(BonusType::PRIMARY_SKILL);
|
||||
else
|
||||
return art1->price > art2->price;
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ std::vector<SlotInfo>::iterator ArmyManager::getWeakestCreature(std::vector<Slot
|
||||
if(left.creature->getLevel() != right.creature->getLevel())
|
||||
return left.creature->getLevel() < right.creature->getLevel();
|
||||
|
||||
return left.creature->Speed() > right.creature->Speed();
|
||||
return left.creature->speed() > right.creature->speed();
|
||||
});
|
||||
|
||||
return weakest;
|
||||
|
@ -52,14 +52,14 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army)
|
||||
double shootersStrength = 0;
|
||||
ui32 maxSpeed = 0;
|
||||
|
||||
static const CSelector selectorSHOOTER = Selector::type()(Bonus::SHOOTER);
|
||||
static const std::string keySHOOTER = "type_"+std::to_string((int32_t)Bonus::SHOOTER);
|
||||
static const CSelector selectorSHOOTER = Selector::type()(BonusType::SHOOTER);
|
||||
static const std::string keySHOOTER = "type_"+std::to_string((int32_t)BonusType::SHOOTER);
|
||||
|
||||
static const CSelector selectorFLYING = Selector::type()(Bonus::FLYING);
|
||||
static const std::string keyFLYING = "type_"+std::to_string((int32_t)Bonus::FLYING);
|
||||
static const CSelector selectorFLYING = Selector::type()(BonusType::FLYING);
|
||||
static const std::string keyFLYING = "type_"+std::to_string((int32_t)BonusType::FLYING);
|
||||
|
||||
static const CSelector selectorSTACKS_SPEED = Selector::type()(Bonus::STACKS_SPEED);
|
||||
static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)Bonus::STACKS_SPEED);
|
||||
static const CSelector selectorSTACKS_SPEED = Selector::type()(BonusType::STACKS_SPEED);
|
||||
static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)BonusType::STACKS_SPEED);
|
||||
|
||||
for(auto s : army->Slots())
|
||||
{
|
||||
|
@ -22,6 +22,10 @@
|
||||
#include "../../lib/CGameState.h"
|
||||
#include "../../lib/NetPacksBase.h"
|
||||
#include "../../lib/NetPacks.h"
|
||||
#include "../../lib/bonuses/CBonusSystemNode.h"
|
||||
#include "../../lib/bonuses/Limiters.h"
|
||||
#include "../../lib/bonuses/Updaters.h"
|
||||
#include "../../lib/bonuses/Propagators.h"
|
||||
#include "../../lib/serializer/CTypeList.h"
|
||||
#include "../../lib/serializer/BinarySerializer.h"
|
||||
#include "../../lib/serializer/BinaryDeserializer.h"
|
||||
|
@ -234,7 +234,7 @@
|
||||
"core.bonus.HEALER.name": "Healer",
|
||||
"core.bonus.HEALER.description": "Heals allied units",
|
||||
"core.bonus.HP_REGENERATION.name": "Regeneration",
|
||||
"core.bonus.HP_REGENERATION.description": "Heals ${SHval} hit points every round",
|
||||
"core.bonus.HP_REGENERATION.description": "Heals ${val} hit points every round",
|
||||
"core.bonus.JOUSTING.name": "Champion charge",
|
||||
"core.bonus.JOUSTING.description": "+${val}% damage for each hex travelled",
|
||||
"core.bonus.KING.name": "King",
|
||||
|
@ -229,7 +229,7 @@
|
||||
"core.bonus.HEALER.name": "Heiler",
|
||||
"core.bonus.HEALER.description": "Heilt verbündete Einheiten",
|
||||
"core.bonus.HP_REGENERATION.name": "Regeneration",
|
||||
"core.bonus.HP_REGENERATION.description": "Heilt ${SHval} Trefferpunkte jede Runde",
|
||||
"core.bonus.HP_REGENERATION.description": "Heilt ${val} Trefferpunkte jede Runde",
|
||||
"core.bonus.JOUSTING.name": "Champion Charge",
|
||||
"core.bonus.JOUSTING.description": "+${val}% Schaden pro zurückgelegtem Feld",
|
||||
"core.bonus.KING.name": "König",
|
||||
|
@ -208,7 +208,7 @@
|
||||
"core.bonus.HEALER.name": "Uzdrowiciel",
|
||||
"core.bonus.HEALER.description": "Leczy sprzymierzone jednostki",
|
||||
"core.bonus.HP_REGENERATION.name": "Regeneracja",
|
||||
"core.bonus.HP_REGENERATION.description": "Leczy ${SHval} punktów zdrowia każdej rundy",
|
||||
"core.bonus.HP_REGENERATION.description": "Leczy ${val} punktów zdrowia każdej rundy",
|
||||
"core.bonus.JOUSTING.name": "Szarża Czempiona",
|
||||
"core.bonus.JOUSTING.description": "+${val}% obrażeń na przebytego heksa",
|
||||
"core.bonus.KING.name": "Król",
|
||||
|
@ -232,7 +232,7 @@
|
||||
"core.bonus.HEALER.name": "Целитель",
|
||||
"core.bonus.HEALER.description": "Исцеляет дружественные юниты",
|
||||
"core.bonus.HP_REGENERATION.name": "Регенерация",
|
||||
"core.bonus.HP_REGENERATION.description": "Исцеляет ${SHval} очков здоровья каждый ход",
|
||||
"core.bonus.HP_REGENERATION.description": "Исцеляет ${val} очков здоровья каждый ход",
|
||||
"core.bonus.JOUSTING.name": "Разгон",
|
||||
"core.bonus.JOUSTING.description": "+${val}% урона за каждую пройденную клетку",
|
||||
"core.bonus.KING.name": "Король",
|
||||
|
@ -208,7 +208,7 @@
|
||||
"core.bonus.HEALER.name" : "Цілитель",
|
||||
"core.bonus.HEALER.description" : "Лікує союзників",
|
||||
"core.bonus.HP_REGENERATION.name" : "Регенерація",
|
||||
"core.bonus.HP_REGENERATION.description" : "Відновлює ${SHval} очок здоров'я кожного раунду",
|
||||
"core.bonus.HP_REGENERATION.description" : "Відновлює ${val} очок здоров'я кожного раунду",
|
||||
"core.bonus.JOUSTING.name" : "Турнірна перевага",
|
||||
"core.bonus.JOUSTING.description" : "+${val}% шкоди за кожен пройдений гекс",
|
||||
"core.bonus.KING.name" : "Король",
|
||||
|
@ -43,6 +43,10 @@
|
||||
#include "../lib/CArtHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/bonuses/CBonusSystemNode.h"
|
||||
#include "../lib/bonuses/Limiters.h"
|
||||
#include "../lib/bonuses/Updaters.h"
|
||||
#include "../lib/bonuses/Propagators.h"
|
||||
#include "../lib/serializer/CTypeList.h"
|
||||
#include "../lib/serializer/BinaryDeserializer.h"
|
||||
#include "../lib/serializer/BinarySerializer.h"
|
||||
@ -1221,11 +1225,11 @@ void CPlayerInterface::availableCreaturesChanged( const CGDwelling *town )
|
||||
void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus &bonus, bool gain )
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
if (bonus.type == Bonus::NONE)
|
||||
if (bonus.type == BonusType::NONE)
|
||||
return;
|
||||
|
||||
adventureInt->onHeroChanged(hero);
|
||||
if ((bonus.type == Bonus::FLYING_MOVEMENT || bonus.type == Bonus::WATER_WALKING) && !gain)
|
||||
if ((bonus.type == BonusType::FLYING_MOVEMENT || bonus.type == BonusType::WATER_WALKING) && !gain)
|
||||
{
|
||||
//recalculate paths because hero has lost bonus influencing pathfinding
|
||||
localState->erasePath(hero);
|
||||
|
@ -706,7 +706,7 @@ void ApplyClientNetPackVisitor::visitBattleSetActiveStack(BattleSetActiveStack &
|
||||
|
||||
const CStack *activated = gs.curB->battleGetStackByID(pack.stack);
|
||||
PlayerColor playerToCall; //pack.player that will move activated stack
|
||||
if (activated->hasBonusOfType(Bonus::HYPNOTIZED))
|
||||
if (activated->hasBonusOfType(BonusType::HYPNOTIZED))
|
||||
{
|
||||
playerToCall = (gs.curB->sides[0].color == activated->unitOwner()
|
||||
? gs.curB->sides[1].color
|
||||
|
@ -140,7 +140,7 @@ bool BattleActionsController::isActiveStackSpellcaster() const
|
||||
if (!casterStack)
|
||||
return false;
|
||||
|
||||
bool spellcaster = casterStack->hasBonusOfType(Bonus::SPELLCASTER);
|
||||
bool spellcaster = casterStack->hasBonusOfType(BonusType::SPELLCASTER);
|
||||
return (spellcaster && casterStack->canCast());
|
||||
}
|
||||
|
||||
@ -228,7 +228,7 @@ void BattleActionsController::reorderPossibleActionsPriority(const CStack * stac
|
||||
case PossiblePlayerBattleAction::NO_LOCATION:
|
||||
case PossiblePlayerBattleAction::FREE_LOCATION:
|
||||
case PossiblePlayerBattleAction::OBSTACLE:
|
||||
if(!stack->hasBonusOfType(Bonus::NO_SPELLCAST_BY_DEFAULT) && context == MouseHoveredHexContext::OCCUPIED_HEX)
|
||||
if(!stack->hasBonusOfType(BonusType::NO_SPELLCAST_BY_DEFAULT) && context == MouseHoveredHexContext::OCCUPIED_HEX)
|
||||
return 1;
|
||||
else
|
||||
return 100;//bottom priority
|
||||
@ -349,7 +349,7 @@ void BattleActionsController::actionSetCursor(PossiblePlayerBattleAction action,
|
||||
|
||||
case PossiblePlayerBattleAction::MOVE_TACTICS:
|
||||
case PossiblePlayerBattleAction::MOVE_STACK:
|
||||
if (owner.stacksController->getActiveStack()->hasBonusOfType(Bonus::FLYING))
|
||||
if (owner.stacksController->getActiveStack()->hasBonusOfType(BonusType::FLYING))
|
||||
CCS->curh->set(Cursor::Combat::FLY);
|
||||
else
|
||||
CCS->curh->set(Cursor::Combat::MOVE);
|
||||
@ -434,7 +434,7 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
|
||||
|
||||
case PossiblePlayerBattleAction::MOVE_TACTICS:
|
||||
case PossiblePlayerBattleAction::MOVE_STACK:
|
||||
if (owner.stacksController->getActiveStack()->hasBonusOfType(Bonus::FLYING))
|
||||
if (owner.stacksController->getActiveStack()->hasBonusOfType(BonusType::FLYING))
|
||||
return (boost::format(CGI->generaltexth->allTexts[295]) % owner.stacksController->getActiveStack()->getName()).str(); //Fly %s here
|
||||
else
|
||||
return (boost::format(CGI->generaltexth->allTexts[294]) % owner.stacksController->getActiveStack()->getName()).str(); //Move %s here
|
||||
@ -529,7 +529,7 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
|
||||
switch (action.get())
|
||||
{
|
||||
case PossiblePlayerBattleAction::CHOOSE_TACTICS_STACK:
|
||||
return (targetStack && targetStackOwned && targetStack->Speed() > 0);
|
||||
return (targetStack && targetStackOwned && targetStack->speed() > 0);
|
||||
|
||||
case PossiblePlayerBattleAction::CREATURE_INFO:
|
||||
return (targetStack && targetStackOwned && targetStack->alive());
|
||||
@ -863,7 +863,7 @@ void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterS
|
||||
{
|
||||
creatureSpells.clear();
|
||||
|
||||
bool spellcaster = casterStack->hasBonusOfType(Bonus::SPELLCASTER);
|
||||
bool spellcaster = casterStack->hasBonusOfType(BonusType::SPELLCASTER);
|
||||
if(casterStack->canCast() && spellcaster)
|
||||
{
|
||||
// faerie dragon can cast only one, randomly selected spell until their next move
|
||||
@ -874,7 +874,7 @@ void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterS
|
||||
creatureSpells.push_back(spellToCast);
|
||||
}
|
||||
|
||||
TConstBonusListPtr bl = casterStack->getBonuses(Selector::type()(Bonus::SPELLCASTER));
|
||||
TConstBonusListPtr bl = casterStack->getBonuses(Selector::type()(BonusType::SPELLCASTER));
|
||||
|
||||
for (auto const & bonus : *bl)
|
||||
{
|
||||
|
@ -370,7 +370,7 @@ bool MovementAnimation::init()
|
||||
distanceX = endPosition.x - begPosition.x;
|
||||
distanceY = endPosition.y - begPosition.y;
|
||||
|
||||
if (stack->hasBonus(Selector::type()(Bonus::FLYING)))
|
||||
if (stack->hasBonus(Selector::type()(BonusType::FLYING)))
|
||||
{
|
||||
float distance = static_cast<float>(sqrt(distanceX * distanceX + distanceY * distanceY));
|
||||
progressPerSecond = AnimationControls::getFlightDistance(stack->unitType()) / distance;
|
||||
|
@ -65,21 +65,21 @@ void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bt
|
||||
return;
|
||||
}
|
||||
//don't show animation when no HP is regenerated
|
||||
switch(bte.effect)
|
||||
switch(static_cast<BonusType>(bte.effect))
|
||||
{
|
||||
case Bonus::HP_REGENERATION:
|
||||
case BonusType::HP_REGENERATION:
|
||||
displayEffect(EBattleEffect::REGENERATION, "REGENER", stack->getPosition());
|
||||
break;
|
||||
case Bonus::MANA_DRAIN:
|
||||
case BonusType::MANA_DRAIN:
|
||||
displayEffect(EBattleEffect::MANA_DRAIN, "MANADRAI", stack->getPosition());
|
||||
break;
|
||||
case Bonus::POISON:
|
||||
case BonusType::POISON:
|
||||
displayEffect(EBattleEffect::POISON, "POISON", stack->getPosition());
|
||||
break;
|
||||
case Bonus::FEAR:
|
||||
case BonusType::FEAR:
|
||||
displayEffect(EBattleEffect::FEAR, "FEAR", stack->getPosition());
|
||||
break;
|
||||
case Bonus::MORALE:
|
||||
case BonusType::MORALE:
|
||||
{
|
||||
std::string hlp = CGI->generaltexth->allTexts[33];
|
||||
boost::algorithm::replace_first(hlp,"%s",(stack->getName()));
|
||||
|
@ -636,7 +636,7 @@ void BattleInterface::tacticPhaseEnd()
|
||||
|
||||
static bool immobile(const CStack *s)
|
||||
{
|
||||
return !s->Speed(0, true); //should bound stacks be immobile?
|
||||
return !s->speed(0, true); //should bound stacks be immobile?
|
||||
}
|
||||
|
||||
void BattleInterface::tacticNextStack(const CStack * current)
|
||||
|
@ -107,7 +107,7 @@ BattleHex BattleStacksController::getStackCurrentPosition(const CStack * stack)
|
||||
if ( !stackAnimation.at(stack->unitId())->isMoving())
|
||||
return stack->getPosition();
|
||||
|
||||
if (stack->hasBonusOfType(Bonus::FLYING) && stackAnimation.at(stack->unitId())->getType() == ECreatureAnimType::MOVING )
|
||||
if (stack->hasBonusOfType(BonusType::FLYING) && stackAnimation.at(stack->unitId())->getType() == ECreatureAnimType::MOVING )
|
||||
return BattleHex::HEX_AFTER_ALL;
|
||||
|
||||
for (auto & anim : currentAnimations)
|
||||
@ -247,7 +247,7 @@ void BattleStacksController::setActiveStack(const CStack *stack)
|
||||
bool BattleStacksController::stackNeedsAmountBox(const CStack * stack) const
|
||||
{
|
||||
//do not show box for singular war machines, stacked war machines with box shown are supported as extension feature
|
||||
if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON) && stack->getCount() == 1)
|
||||
if(stack->hasBonusOfType(BonusType::SIEGE_WEAPON) && stack->getCount() == 1)
|
||||
return false;
|
||||
|
||||
if(!stack->alive())
|
||||
@ -528,7 +528,7 @@ void BattleStacksController::stackMoved(const CStack *stack, std::vector<BattleH
|
||||
addNewAnim(new MovementStartAnimation(owner, stack));
|
||||
});
|
||||
|
||||
if (!stack->hasBonus(Selector::typeSubtype(Bonus::FLYING, 1)))
|
||||
if (!stack->hasBonus(Selector::typeSubtype(BonusType::FLYING, 1)))
|
||||
{
|
||||
owner.addToAnimationStage(EAnimationEvents::MOVEMENT, [&]()
|
||||
{
|
||||
@ -793,7 +793,7 @@ void BattleStacksController::removeExpiredColorFilters()
|
||||
{
|
||||
if (!filter.persistent)
|
||||
{
|
||||
if (filter.source && !filter.target->hasBonus(Selector::source(Bonus::SPELL_EFFECT, filter.source->id), Selector::all))
|
||||
if (filter.source && !filter.target->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, filter.source->id), Selector::all))
|
||||
return true;
|
||||
if (filter.effect == ColorFilter::genEmptyShifter())
|
||||
return true;
|
||||
|
@ -426,11 +426,11 @@ void BattleWindow::bSpellf()
|
||||
{
|
||||
//TODO: move to spell mechanics, add more information to spell cast problem
|
||||
//Handle Orb of Inhibition-like effects -> we want to display dialog with info, why casting is impossible
|
||||
auto blockingBonus = owner.currentHero()->getBonusLocalFirst(Selector::type()(Bonus::BLOCK_ALL_MAGIC));
|
||||
auto blockingBonus = owner.currentHero()->getBonusLocalFirst(Selector::type()(BonusType::BLOCK_ALL_MAGIC));
|
||||
if (!blockingBonus)
|
||||
return;
|
||||
|
||||
if (blockingBonus->source == Bonus::ARTIFACT)
|
||||
if (blockingBonus->source == BonusSource::ARTIFACT)
|
||||
{
|
||||
const auto artID = ArtifactID(blockingBonus->sid);
|
||||
//If we have artifact, put name of our hero. Otherwise assume it's the enemy.
|
||||
|
@ -372,7 +372,7 @@ CTownTooltip::CTownTooltip(Point pos, const CGTownInstance * town)
|
||||
init(InfoAboutTown(town, true));
|
||||
}
|
||||
|
||||
void MoraleLuckBox::set(const IBonusBearer * node)
|
||||
void MoraleLuckBox::set(const AFactionMember * node)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
|
||||
|
||||
@ -385,7 +385,7 @@ void MoraleLuckBox::set(const IBonusBearer * node)
|
||||
bonusValue = 0;
|
||||
|
||||
if(node)
|
||||
bonusValue = morale ? node->MoraleValAndBonusList(modifierList) : node->LuckValAndBonusList(modifierList);
|
||||
bonusValue = morale ? node->moraleValAndBonusList(modifierList) : node->luckValAndBonusList(modifierList);
|
||||
|
||||
int mrlt = (bonusValue>0)-(bonusValue<0); //signum: -1 - bad luck / morale, 0 - neutral, 1 - good
|
||||
hoverText = CGI->generaltexth->heroscrn[hoverTextBase[morale] - mrlt];
|
||||
@ -393,21 +393,21 @@ void MoraleLuckBox::set(const IBonusBearer * node)
|
||||
text = CGI->generaltexth->arraytxt[textId[morale]];
|
||||
boost::algorithm::replace_first(text,"%s",CGI->generaltexth->arraytxt[neutralDescr[morale]-mrlt]);
|
||||
|
||||
if (morale && node && (node->hasBonusOfType(Bonus::UNDEAD)
|
||||
|| node->hasBonusOfType(Bonus::NON_LIVING)))
|
||||
if (morale && node && (node->getBonusBearer()->hasBonusOfType(BonusType::UNDEAD)
|
||||
|| node->getBonusBearer()->hasBonusOfType(BonusType::NON_LIVING)))
|
||||
{
|
||||
text += CGI->generaltexth->arraytxt[113]; //unaffected by morale
|
||||
bonusValue = 0;
|
||||
}
|
||||
else if(morale && node && node->hasBonusOfType(Bonus::NO_MORALE))
|
||||
else if(morale && node && node->getBonusBearer()->hasBonusOfType(BonusType::NO_MORALE))
|
||||
{
|
||||
auto noMorale = node->getBonus(Selector::type()(Bonus::NO_MORALE));
|
||||
auto noMorale = node->getBonusBearer()->getBonus(Selector::type()(BonusType::NO_MORALE));
|
||||
text += "\n" + noMorale->Description();
|
||||
bonusValue = 0;
|
||||
}
|
||||
else if (!morale && node && node->hasBonusOfType(Bonus::NO_LUCK))
|
||||
else if (!morale && node && node->getBonusBearer()->hasBonusOfType(BonusType::NO_LUCK))
|
||||
{
|
||||
auto noLuck = node->getBonus(Selector::type()(Bonus::NO_LUCK));
|
||||
auto noLuck = node->getBonusBearer()->getBonus(Selector::type()(BonusType::NO_LUCK));
|
||||
text += "\n" + noLuck->Description();
|
||||
bonusValue = 0;
|
||||
}
|
||||
@ -458,7 +458,7 @@ CCreaturePic::CCreaturePic(int x, int y, const CCreature * cre, bool Big, bool A
|
||||
bg = std::make_shared<CPicture>((*CGI->townh)[faction]->creatureBg120);
|
||||
anim = std::make_shared<CCreatureAnim>(0, 0, cre->animDefName);
|
||||
anim->clipRect(cre->isDoubleWide()?170:150, 155, bg->pos.w, bg->pos.h);
|
||||
anim->startPreview(cre->hasBonusOfType(Bonus::SIEGE_WEAPON));
|
||||
anim->startPreview(cre->hasBonusOfType(BonusType::SIEGE_WEAPON));
|
||||
|
||||
amount = std::make_shared<CLabel>(bg->pos.w, bg->pos.h, FONT_MEDIUM, ETextAlignment::BOTTOMRIGHT, Colors::WHITE);
|
||||
|
||||
|
@ -16,7 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
class CGGarrison;
|
||||
struct InfoAboutArmy;
|
||||
class CArmedInstance;
|
||||
class IBonusBearer;
|
||||
class AFactionMember;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@ -170,7 +170,7 @@ public:
|
||||
bool morale; //true if morale, false if luck
|
||||
bool small;
|
||||
|
||||
void set(const IBonusBearer *node);
|
||||
void set(const AFactionMember *node);
|
||||
|
||||
MoraleLuckBox(bool Morale, const Rect &r, bool Small=false);
|
||||
};
|
||||
|
@ -1703,9 +1703,9 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *
|
||||
sizes.y+=21;
|
||||
values.push_back(std::make_shared<LabeledValue>(sizes, CGI->generaltexth->allTexts[199], CGI->generaltexth->fcommands[2], getMyCreature()->getMinDamage(false), getMyCreature()->getMaxDamage(false)));
|
||||
sizes.y+=20;
|
||||
values.push_back(std::make_shared<LabeledValue>(sizes, CGI->generaltexth->allTexts[388], CGI->generaltexth->fcommands[3], getMyCreature()->MaxHealth()));
|
||||
values.push_back(std::make_shared<LabeledValue>(sizes, CGI->generaltexth->allTexts[388], CGI->generaltexth->fcommands[3], getMyCreature()->getMaxHealth()));
|
||||
sizes.y+=21;
|
||||
values.push_back(std::make_shared<LabeledValue>(sizes, CGI->generaltexth->allTexts[193], CGI->generaltexth->fcommands[4], getMyCreature()->valOfBonuses(Bonus::STACKS_SPEED)));
|
||||
values.push_back(std::make_shared<LabeledValue>(sizes, CGI->generaltexth->allTexts[193], CGI->generaltexth->fcommands[4], getMyCreature()->valOfBonuses(BonusType::STACKS_SPEED)));
|
||||
sizes.y+=20;
|
||||
values.push_back(std::make_shared<LabeledValue>(sizes, CGI->generaltexth->allTexts[194], CGI->generaltexth->fcommands[5], town->creatureGrowth(level)));
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ CStackWindow::ActiveSpellsSection::ActiveSpellsSection(CStackWindow * owner, int
|
||||
spellText = CGI->generaltexth->allTexts[610]; //"%s, duration: %d rounds."
|
||||
boost::replace_first(spellText, "%s", spell->getNameTranslated());
|
||||
//FIXME: support permanent duration
|
||||
int duration = battleStack->getBonusLocalFirst(Selector::source(Bonus::SPELL_EFFECT,effect))->turnsRemain;
|
||||
int duration = battleStack->getBonusLocalFirst(Selector::source(BonusSource::SPELL_EFFECT,effect))->turnsRemain;
|
||||
boost::replace_first(spellText, "%d", std::to_string(duration));
|
||||
|
||||
spellIcons.push_back(std::make_shared<CAnimImage>("SpellInt", effect + 1, 0, firstPos.x + offset.x * printed, firstPos.y + offset.y * printed));
|
||||
@ -510,7 +510,7 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
|
||||
name = std::make_shared<CLabel>(215, 12, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, parent->info->getName());
|
||||
|
||||
int dmgMultiply = 1;
|
||||
if(parent->info->owner && parent->info->stackNode->hasBonusOfType(Bonus::SIEGE_WEAPON))
|
||||
if(parent->info->owner && parent->info->stackNode->hasBonusOfType(BonusType::SIEGE_WEAPON))
|
||||
dmgMultiply += parent->info->owner->getPrimSkillLevel(PrimarySkill::ATTACK);
|
||||
|
||||
icons = std::make_shared<CPicture>("stackWindow/icons", 117, 32);
|
||||
@ -525,8 +525,8 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
|
||||
addStatLabel(EStat::ATTACK, parent->info->creature->getAttack(battleStack->isShooter()), battleStack->getAttack(battleStack->isShooter()));
|
||||
addStatLabel(EStat::DEFENCE, parent->info->creature->getDefense(battleStack->isShooter()), battleStack->getDefense(battleStack->isShooter()));
|
||||
addStatLabel(EStat::DAMAGE, parent->info->stackNode->getMinDamage(battleStack->isShooter()) * dmgMultiply, battleStack->getMaxDamage(battleStack->isShooter()) * dmgMultiply);
|
||||
addStatLabel(EStat::HEALTH, parent->info->creature->MaxHealth(), battleStack->MaxHealth());
|
||||
addStatLabel(EStat::SPEED, parent->info->creature->Speed(), battleStack->Speed());
|
||||
addStatLabel(EStat::HEALTH, parent->info->creature->getMaxHealth(), battleStack->getMaxHealth());
|
||||
addStatLabel(EStat::SPEED, parent->info->creature->speed(), battleStack->speed());
|
||||
|
||||
if(battleStack->isShooter())
|
||||
addStatLabel(EStat::SHOTS, battleStack->shots.total(), battleStack->shots.available());
|
||||
@ -539,19 +539,19 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool shooter = parent->info->stackNode->hasBonusOfType(Bonus::SHOOTER) && parent->info->stackNode->valOfBonuses(Bonus::SHOTS);
|
||||
const bool caster = parent->info->stackNode->valOfBonuses(Bonus::CASTS);
|
||||
const bool shooter = parent->info->stackNode->hasBonusOfType(BonusType::SHOOTER) && parent->info->stackNode->valOfBonuses(BonusType::SHOTS);
|
||||
const bool caster = parent->info->stackNode->valOfBonuses(BonusType::CASTS);
|
||||
|
||||
addStatLabel(EStat::ATTACK, parent->info->creature->getAttack(shooter), parent->info->stackNode->getAttack(shooter));
|
||||
addStatLabel(EStat::DEFENCE, parent->info->creature->getDefense(shooter), parent->info->stackNode->getDefense(shooter));
|
||||
addStatLabel(EStat::DAMAGE, parent->info->stackNode->getMinDamage(shooter) * dmgMultiply, parent->info->stackNode->getMaxDamage(shooter) * dmgMultiply);
|
||||
addStatLabel(EStat::HEALTH, parent->info->creature->MaxHealth(), parent->info->stackNode->MaxHealth());
|
||||
addStatLabel(EStat::SPEED, parent->info->creature->Speed(), parent->info->stackNode->Speed());
|
||||
addStatLabel(EStat::HEALTH, parent->info->creature->getMaxHealth(), parent->info->stackNode->getMaxHealth());
|
||||
addStatLabel(EStat::SPEED, parent->info->creature->speed(), parent->info->stackNode->speed());
|
||||
|
||||
if(shooter)
|
||||
addStatLabel(EStat::SHOTS, parent->info->stackNode->valOfBonuses(Bonus::SHOTS));
|
||||
addStatLabel(EStat::SHOTS, parent->info->stackNode->valOfBonuses(BonusType::SHOTS));
|
||||
if(caster)
|
||||
addStatLabel(EStat::MANA, parent->info->stackNode->valOfBonuses(Bonus::CASTS));
|
||||
addStatLabel(EStat::MANA, parent->info->stackNode->valOfBonuses(BonusType::CASTS));
|
||||
|
||||
morale->set(parent->info->stackNode);
|
||||
luck->set(parent->info->stackNode);
|
||||
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../../lib/HeroBonus.h"
|
||||
#include "../../lib/bonuses/Bonus.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "CWindowObject.h"
|
||||
|
||||
|
@ -66,9 +66,19 @@ int64_t CHeroWithMaybePickedArtifact::getTreeVersion() const
|
||||
si32 CHeroWithMaybePickedArtifact::manaLimit() const
|
||||
{
|
||||
//TODO: reduplicate code with CGHeroInstance
|
||||
return si32(getPrimSkillLevel(PrimarySkill::KNOWLEDGE) * (valOfBonuses(Bonus::MANA_PER_KNOWLEDGE)));
|
||||
return si32(getPrimSkillLevel(PrimarySkill::KNOWLEDGE) * (valOfBonuses(BonusType::MANA_PER_KNOWLEDGE)));
|
||||
}
|
||||
|
||||
const IBonusBearer * CHeroWithMaybePickedArtifact::getBonusBearer() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
FactionID CHeroWithMaybePickedArtifact::getFaction() const
|
||||
{
|
||||
return hero->getFaction();
|
||||
}
|
||||
|
||||
CHeroWithMaybePickedArtifact::CHeroWithMaybePickedArtifact(CWindowWithArtifacts * Cww, const CGHeroInstance * Hero)
|
||||
: hero(Hero), cww(Cww)
|
||||
{
|
||||
@ -324,7 +334,7 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
|
||||
|
||||
dismissButton->block(!!curHero->visitedTown || noDismiss);
|
||||
|
||||
if(curHero->valOfBonuses(Selector::type()(Bonus::BEFORE_BATTLE_REPOSITION)) == 0)
|
||||
if(curHero->valOfBonuses(Selector::type()(BonusType::BEFORE_BATTLE_REPOSITION)) == 0)
|
||||
{
|
||||
tacticsButton->block(true);
|
||||
}
|
||||
|
@ -9,7 +9,10 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../../lib/HeroBonus.h"
|
||||
#include <vcmi/FactionMember.h>
|
||||
|
||||
#include "../../lib/bonuses/Bonus.h"
|
||||
#include "../../lib/bonuses/IBonusBearer.h"
|
||||
#include "../widgets/CWindowWithArtifacts.h"
|
||||
#include "../widgets/CGarrisonInt.h"
|
||||
|
||||
@ -45,7 +48,7 @@ public:
|
||||
};
|
||||
|
||||
//helper class for calculating values of hero bonuses without bonuses from picked up artifact
|
||||
class CHeroWithMaybePickedArtifact : public virtual IBonusBearer
|
||||
class CHeroWithMaybePickedArtifact : public IBonusBearer, public AFactionMember
|
||||
{
|
||||
public:
|
||||
const CGHeroInstance * hero;
|
||||
@ -54,6 +57,9 @@ public:
|
||||
CHeroWithMaybePickedArtifact(CWindowWithArtifacts * Cww, const CGHeroInstance * Hero);
|
||||
TConstBonusListPtr getAllBonuses(const CSelector & selector, const CSelector & limit, const CBonusSystemNode * root = nullptr, const std::string & cachingStr = "") const override;
|
||||
|
||||
const IBonusBearer * getBonusBearer() const override;
|
||||
FactionID getFaction() const override;
|
||||
|
||||
int64_t getTreeVersion() const override;
|
||||
|
||||
si32 manaLimit() const;
|
||||
|
@ -586,7 +586,7 @@ void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstan
|
||||
std::vector<const CGHeroInstance*> heroes = LOCPLINT->cb->getHeroesInfo(true);
|
||||
for(auto & heroe : heroes)
|
||||
{
|
||||
totalIncome += heroe->valOfBonuses(Selector::typeSubtype(Bonus::GENERATE_RESOURCE, GameResID(EGameResID::GOLD)));
|
||||
totalIncome += heroe->valOfBonuses(Selector::typeSubtype(BonusType::GENERATE_RESOURCE, GameResID(EGameResID::GOLD)));
|
||||
}
|
||||
|
||||
//Add town income of all towns
|
||||
|
@ -61,7 +61,7 @@
|
||||
#include "../lib/CStopWatch.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "../lib/HeroBonus.h"
|
||||
#include "../lib/bonuses/Bonus.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/NetPacksBase.h"
|
||||
#include "../lib/StartInfo.h"
|
||||
|
@ -27,6 +27,18 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/battle/SiegeInfo.cpp
|
||||
${MAIN_LIB_DIR}/battle/Unit.cpp
|
||||
|
||||
${MAIN_LIB_DIR}/bonuses/Bonus.cpp
|
||||
${MAIN_LIB_DIR}/bonuses/BonusEnum.cpp
|
||||
${MAIN_LIB_DIR}/bonuses/BonusList.cpp
|
||||
${MAIN_LIB_DIR}/bonuses/BonusParams.cpp
|
||||
${MAIN_LIB_DIR}/bonuses/BonusSelector.cpp
|
||||
${MAIN_LIB_DIR}/bonuses/CBonusProxy.cpp
|
||||
${MAIN_LIB_DIR}/bonuses/CBonusSystemNode.cpp
|
||||
${MAIN_LIB_DIR}/bonuses/IBonusBearer.cpp
|
||||
${MAIN_LIB_DIR}/bonuses/Limiters.cpp
|
||||
${MAIN_LIB_DIR}/bonuses/Propagators.cpp
|
||||
${MAIN_LIB_DIR}/bonuses/Updaters.cpp
|
||||
|
||||
${MAIN_LIB_DIR}/events/ApplyDamage.cpp
|
||||
${MAIN_LIB_DIR}/events/GameResumed.cpp
|
||||
${MAIN_LIB_DIR}/events/ObjectVisitEnded.cpp
|
||||
@ -198,7 +210,6 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/CTownHandler.cpp
|
||||
${MAIN_LIB_DIR}/GameConstants.cpp
|
||||
${MAIN_LIB_DIR}/GameSettings.cpp
|
||||
${MAIN_LIB_DIR}/HeroBonus.cpp
|
||||
${MAIN_LIB_DIR}/IGameCallback.cpp
|
||||
${MAIN_LIB_DIR}/IHandlerBase.cpp
|
||||
${MAIN_LIB_DIR}/JsonDetail.cpp
|
||||
@ -300,6 +311,18 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/battle/SiegeInfo.h
|
||||
${MAIN_LIB_DIR}/battle/Unit.h
|
||||
|
||||
${MAIN_LIB_DIR}/bonuses/Bonus.h
|
||||
${MAIN_LIB_DIR}/bonuses/BonusEnum.h
|
||||
${MAIN_LIB_DIR}/bonuses/BonusList.h
|
||||
${MAIN_LIB_DIR}/bonuses/BonusParams.h
|
||||
${MAIN_LIB_DIR}/bonuses/BonusSelector.h
|
||||
${MAIN_LIB_DIR}/bonuses/CBonusProxy.h
|
||||
${MAIN_LIB_DIR}/bonuses/CBonusSystemNode.h
|
||||
${MAIN_LIB_DIR}/bonuses/IBonusBearer.h
|
||||
${MAIN_LIB_DIR}/bonuses/Limiters.h
|
||||
${MAIN_LIB_DIR}/bonuses/Propagators.h
|
||||
${MAIN_LIB_DIR}/bonuses/Updaters.h
|
||||
|
||||
${MAIN_LIB_DIR}/events/ApplyDamage.h
|
||||
${MAIN_LIB_DIR}/events/GameResumed.h
|
||||
${MAIN_LIB_DIR}/events/ObjectVisitEnded.h
|
||||
@ -478,7 +501,6 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/FunctionList.h
|
||||
${MAIN_LIB_DIR}/GameConstants.h
|
||||
${MAIN_LIB_DIR}/GameSettings.h
|
||||
${MAIN_LIB_DIR}/HeroBonus.h
|
||||
${MAIN_LIB_DIR}/IBonusTypeHandler.h
|
||||
${MAIN_LIB_DIR}/IGameCallback.h
|
||||
${MAIN_LIB_DIR}/IGameEventsReceiver.h
|
||||
|
@ -232,7 +232,7 @@
|
||||
},
|
||||
"special":{
|
||||
"type": "boolean",
|
||||
"description": "Special spell. Can be given only by Bonus::SPELL"
|
||||
"description": "Special spell. Can be given only by BonusType::SPELL"
|
||||
},
|
||||
"nonMagical":{
|
||||
"type": "boolean",
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Entity.h"
|
||||
#include "FactionMember.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -18,7 +18,21 @@ class CreatureID;
|
||||
class ResourceSet;
|
||||
enum class EGameResID : int8_t;
|
||||
|
||||
class DLL_LINKAGE Creature : public EntityWithNativeTerrain<CreatureID>
|
||||
/// Base class for creatures and battle stacks
|
||||
class DLL_LINKAGE ACreature: public AFactionMember
|
||||
{
|
||||
public:
|
||||
bool isLiving() const; //non-undead, non-non living or alive
|
||||
ui32 speed(int turn = 0, bool useBind = false) const; //get speed (in moving tiles) of creature with all modificators
|
||||
virtual ui32 getMaxHealth() const; //get max HP of stack with all modifiers
|
||||
};
|
||||
|
||||
template <typename IdType>
|
||||
class DLL_LINKAGE CreatureEntity : public EntityT<IdType>, public ACreature
|
||||
{
|
||||
};
|
||||
|
||||
class DLL_LINKAGE Creature : public CreatureEntity<CreatureID>
|
||||
{
|
||||
protected:
|
||||
// use getNamePlural/Singular instead
|
||||
@ -32,8 +46,6 @@ public:
|
||||
virtual std::string getNamePluralTextID() const = 0;
|
||||
virtual std::string getNameSingularTextID() const = 0;
|
||||
|
||||
virtual uint32_t getMaxHealth() const = 0;
|
||||
|
||||
virtual int32_t getAdvMapAmountMin() const = 0;
|
||||
virtual int32_t getAdvMapAmountMax() const = 0;
|
||||
virtual int32_t getAIValue() const = 0;
|
||||
|
@ -31,15 +31,6 @@ public:
|
||||
virtual bool isNativeTerrain(Identifier<ETerrainId> terrain) const;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE IConstBonusNativeTerrainProvider: public IConstBonusProvider, public INativeTerrainProvider
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Returns native terrain considering some terrain bonuses.
|
||||
*/
|
||||
virtual Identifier<ETerrainId> getNativeTerrain() const;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE Entity
|
||||
{
|
||||
public:
|
||||
@ -68,9 +59,4 @@ class DLL_LINKAGE EntityWithBonuses : public EntityT<IdType>, public IConstBonus
|
||||
{
|
||||
};
|
||||
|
||||
template <typename IdType>
|
||||
class DLL_LINKAGE EntityWithNativeTerrain : public EntityT<IdType>, public IConstBonusNativeTerrainProvider
|
||||
{
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
74
include/vcmi/FactionMember.h
Normal file
74
include/vcmi/FactionMember.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* FactionMember.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 "Entity.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class BonusList;
|
||||
|
||||
namespace PrimarySkill
|
||||
{
|
||||
enum PrimarySkill : int8_t;
|
||||
}
|
||||
|
||||
class DLL_LINKAGE AFactionMember: public IConstBonusProvider, public INativeTerrainProvider
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Returns native terrain considering some terrain bonuses.
|
||||
*/
|
||||
virtual Identifier<ETerrainId> getNativeTerrain() const;
|
||||
/**
|
||||
Returns magic resistance considering some bonuses.
|
||||
*/
|
||||
virtual int32_t magicResistance() const;
|
||||
/**
|
||||
Returns minimal damage of creature or (when implemented) hero.
|
||||
*/
|
||||
virtual int getMinDamage(bool ranged) const;
|
||||
/**
|
||||
Returns maximal damage of creature or (when implemented) hero.
|
||||
*/
|
||||
virtual int getMaxDamage(bool ranged) const;
|
||||
/**
|
||||
Returns attack of creature or hero.
|
||||
*/
|
||||
virtual int getAttack(bool ranged) const;
|
||||
/**
|
||||
Returns defence of creature or hero.
|
||||
*/
|
||||
virtual int getDefense(bool ranged) const;
|
||||
/**
|
||||
Returns primskill of creature or hero.
|
||||
*/
|
||||
int getPrimSkillLevel(PrimarySkill::PrimarySkill id) const;
|
||||
/**
|
||||
Returns morale of creature or hero. Taking absolute bonuses into account.
|
||||
For now, uses range from EGameSettings
|
||||
*/
|
||||
int moraleVal() const;
|
||||
/**
|
||||
Returns luck of creature or hero. Taking absolute bonuses into account.
|
||||
For now, uses range from EGameSettings
|
||||
*/
|
||||
int luckVal() const;
|
||||
/**
|
||||
Returns total value of all morale bonuses and sets bonusList as a pointer to the list of selected bonuses.
|
||||
@param bonusList is the out param it's list of all selected bonuses
|
||||
@return total value of all morale in the range from EGameSettings and 0 otherwise
|
||||
*/
|
||||
int moraleValAndBonusList(std::shared_ptr<const BonusList> & bonusList) const;
|
||||
int luckValAndBonusList(std::shared_ptr<const BonusList> & bonusList) const;
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
@ -12,10 +12,14 @@
|
||||
|
||||
#include "VCMI_Lib.h"
|
||||
#include "GameConstants.h"
|
||||
#include "HeroBonus.h"
|
||||
#include "GameSettings.h"
|
||||
#include "bonuses/BonusList.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "bonuses/IBonusBearer.h"
|
||||
|
||||
#include <vcmi/Entity.h>
|
||||
#include <vcmi/Creature.h>
|
||||
#include <vcmi/Faction.h>
|
||||
#include <vcmi/FactionMember.h>
|
||||
#include <vcmi/FactionService.h>
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
@ -26,11 +30,11 @@ bool INativeTerrainProvider::isNativeTerrain(TerrainId terrain) const
|
||||
return native == terrain || native == ETerrainId::ANY_TERRAIN;
|
||||
}
|
||||
|
||||
TerrainId IConstBonusNativeTerrainProvider::getNativeTerrain() const
|
||||
TerrainId AFactionMember::getNativeTerrain() const
|
||||
{
|
||||
constexpr auto any = TerrainId(ETerrainId::ANY_TERRAIN);
|
||||
const std::string cachingStringNoTerrainPenalty = "type_NO_TERRAIN_PENALTY_sANY";
|
||||
static const auto selectorNoTerrainPenalty = Selector::typeSubtype(Bonus::NO_TERRAIN_PENALTY, any);
|
||||
static const auto selectorNoTerrainPenalty = Selector::typeSubtype(BonusType::NO_TERRAIN_PENALTY, any);
|
||||
|
||||
//this code is used in the CreatureTerrainLimiter::limit to setup battle bonuses
|
||||
//and in the CGHeroInstance::getNativeTerrain() to setup movement bonuses or/and penalties.
|
||||
@ -38,4 +42,144 @@ TerrainId IConstBonusNativeTerrainProvider::getNativeTerrain() const
|
||||
? any : VLC->factions()->getById(getFaction())->getNativeTerrain();
|
||||
}
|
||||
|
||||
int32_t AFactionMember::magicResistance() const
|
||||
{
|
||||
si32 val = getBonusBearer()->valOfBonuses(Selector::type()(BonusType::MAGIC_RESISTANCE));
|
||||
vstd::amin (val, 100);
|
||||
return val;
|
||||
}
|
||||
|
||||
int AFactionMember::getAttack(bool ranged) const
|
||||
{
|
||||
const std::string cachingStr = "type_PRIMARY_SKILLs_ATTACK";
|
||||
|
||||
static const auto selector = Selector::typeSubtype(BonusType::PRIMARY_SKILL, PrimarySkill::ATTACK);
|
||||
|
||||
return getBonusBearer()->valOfBonuses(selector, cachingStr);
|
||||
}
|
||||
|
||||
int AFactionMember::getDefense(bool ranged) const
|
||||
{
|
||||
const std::string cachingStr = "type_PRIMARY_SKILLs_DEFENSE";
|
||||
|
||||
static const auto selector = Selector::typeSubtype(BonusType::PRIMARY_SKILL, PrimarySkill::DEFENSE);
|
||||
|
||||
return getBonusBearer()->valOfBonuses(selector, cachingStr);
|
||||
}
|
||||
|
||||
int AFactionMember::getMinDamage(bool ranged) const
|
||||
{
|
||||
const std::string cachingStr = "type_CREATURE_DAMAGEs_0Otype_CREATURE_DAMAGEs_1";
|
||||
static const auto selector = Selector::typeSubtype(BonusType::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(BonusType::CREATURE_DAMAGE, 1));
|
||||
return getBonusBearer()->valOfBonuses(selector, cachingStr);
|
||||
}
|
||||
|
||||
int AFactionMember::getMaxDamage(bool ranged) const
|
||||
{
|
||||
const std::string cachingStr = "type_CREATURE_DAMAGEs_0Otype_CREATURE_DAMAGEs_2";
|
||||
static const auto selector = Selector::typeSubtype(BonusType::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(BonusType::CREATURE_DAMAGE, 2));
|
||||
return getBonusBearer()->valOfBonuses(selector, cachingStr);
|
||||
}
|
||||
|
||||
int AFactionMember::getPrimSkillLevel(PrimarySkill::PrimarySkill id) const
|
||||
{
|
||||
static const CSelector selectorAllSkills = Selector::type()(BonusType::PRIMARY_SKILL);
|
||||
static const std::string keyAllSkills = "type_PRIMARY_SKILL";
|
||||
auto allSkills = getBonusBearer()->getBonuses(selectorAllSkills, keyAllSkills);
|
||||
auto ret = allSkills->valOfBonuses(Selector::subtype()(id));
|
||||
auto minSkillValue = (id == PrimarySkill::SPELL_POWER || id == PrimarySkill::KNOWLEDGE) ? 1 : 0;
|
||||
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
|
||||
{
|
||||
static const auto unaffectedByMoraleSelector = Selector::type()(BonusType::NON_LIVING).Or(Selector::type()(BonusType::UNDEAD))
|
||||
.Or(Selector::type()(BonusType::SIEGE_WEAPON)).Or(Selector::type()(BonusType::NO_MORALE));
|
||||
|
||||
static const std::string cachingStrUn = "AFactionMember::unaffectedByMoraleSelector";
|
||||
auto unaffected = getBonusBearer()->hasBonus(unaffectedByMoraleSelector, cachingStrUn);
|
||||
if(unaffected)
|
||||
{
|
||||
if(bonusList && !bonusList->empty())
|
||||
bonusList = std::make_shared<const BonusList>();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const auto moraleSelector = Selector::type()(BonusType::MORALE);
|
||||
static const std::string cachingStrMor = "type_MORALE";
|
||||
bonusList = getBonusBearer()->getBonuses(moraleSelector, cachingStrMor);
|
||||
|
||||
int32_t maxGoodMorale = VLC->settings()->getVector(EGameSettings::COMBAT_GOOD_MORALE_DICE).size();
|
||||
int32_t maxBadMorale = -VLC->settings()->getVector(EGameSettings::COMBAT_BAD_MORALE_DICE).size();
|
||||
|
||||
return std::clamp(bonusList->totalValue(), maxBadMorale, maxGoodMorale);
|
||||
}
|
||||
|
||||
int AFactionMember::luckValAndBonusList(TConstBonusListPtr & bonusList) const
|
||||
{
|
||||
if(getBonusBearer()->hasBonusOfType(BonusType::NO_LUCK))
|
||||
{
|
||||
if(bonusList && !bonusList->empty())
|
||||
bonusList = std::make_shared<const BonusList>();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const auto luckSelector = Selector::type()(BonusType::LUCK);
|
||||
static const std::string cachingStrLuck = "type_LUCK";
|
||||
bonusList = getBonusBearer()->getBonuses(luckSelector, cachingStrLuck);
|
||||
|
||||
int32_t maxGoodLuck = VLC->settings()->getVector(EGameSettings::COMBAT_GOOD_LUCK_DICE).size();
|
||||
int32_t maxBadLuck = -VLC->settings()->getVector(EGameSettings::COMBAT_BAD_LUCK_DICE).size();
|
||||
|
||||
return std::clamp(bonusList->totalValue(), maxBadLuck, maxGoodLuck);
|
||||
}
|
||||
|
||||
int AFactionMember::moraleVal() const
|
||||
{
|
||||
TConstBonusListPtr tmp = nullptr;
|
||||
return moraleValAndBonusList(tmp);
|
||||
}
|
||||
|
||||
int AFactionMember::luckVal() const
|
||||
{
|
||||
TConstBonusListPtr tmp = nullptr;
|
||||
return luckValAndBonusList(tmp);
|
||||
}
|
||||
|
||||
ui32 ACreature::getMaxHealth() const
|
||||
{
|
||||
const std::string cachingStr = "type_STACK_HEALTH";
|
||||
static const auto selector = Selector::type()(BonusType::STACK_HEALTH);
|
||||
auto value = getBonusBearer()->valOfBonuses(selector, cachingStr);
|
||||
return std::max(1, value); //never 0
|
||||
}
|
||||
|
||||
ui32 ACreature::speed(int turn, bool useBind) const
|
||||
{
|
||||
//war machines cannot move
|
||||
if(getBonusBearer()->hasBonus(Selector::type()(BonusType::SIEGE_WEAPON).And(Selector::turns(turn))))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
//bind effect check - doesn't influence stack initiative
|
||||
if(useBind && getBonusBearer()->hasBonus(Selector::type()(BonusType::BIND_EFFECT).And(Selector::turns(turn))))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return getBonusBearer()->valOfBonuses(Selector::type()(BonusType::STACKS_SPEED).And(Selector::turns(turn)));
|
||||
}
|
||||
|
||||
bool ACreature::isLiving() const //TODO: theoreticaly there exists "LIVING" bonus in stack experience documentation
|
||||
{
|
||||
static const std::string cachingStr = "ACreature::isLiving";
|
||||
static const CSelector selector = Selector::type()(BonusType::UNDEAD)
|
||||
.Or(Selector::type()(BonusType::NON_LIVING))
|
||||
.Or(Selector::type()(BonusType::GARGOYLE))
|
||||
.Or(Selector::type()(BonusType::SIEGE_WEAPON));
|
||||
|
||||
return !getBonusBearer()->hasBonus(selector, cachingStr);
|
||||
}
|
||||
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
@ -27,9 +27,9 @@ BattleFieldInfo * BattleFieldHandler::loadFromJson(const std::string & scope, co
|
||||
{
|
||||
auto bonus = JsonUtils::parseBonus(b);
|
||||
|
||||
bonus->source = Bonus::TERRAIN_OVERLAY;
|
||||
bonus->source = BonusSource::TERRAIN_OVERLAY;
|
||||
bonus->sid = info->getIndex();
|
||||
bonus->duration = Bonus::ONE_BATTLE;
|
||||
bonus->duration = BonusDuration::ONE_BATTLE;
|
||||
|
||||
info->bonuses.push_back(bonus);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
#include <vcmi/EntityService.h>
|
||||
#include <vcmi/Entity.h>
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "GameConstants.h"
|
||||
#include "IHandlerBase.h"
|
||||
#include "battle/BattleHex.h"
|
||||
|
@ -256,8 +256,8 @@ std::string CArtifact::nodeName() const
|
||||
|
||||
void CArtifact::addNewBonus(const std::shared_ptr<Bonus>& b)
|
||||
{
|
||||
b->source = Bonus::ARTIFACT;
|
||||
b->duration = Bonus::PERMANENT;
|
||||
b->source = BonusSource::ARTIFACT;
|
||||
b->duration = BonusDuration::PERMANENT;
|
||||
b->description = getNameTranslated();
|
||||
CBonusSystemNode::addNewBonus(b);
|
||||
}
|
||||
@ -275,21 +275,21 @@ void CArtifact::serializeJson(JsonSerializeFormat & handler)
|
||||
void CGrowingArtifact::levelUpArtifact (CArtifactInstance * art)
|
||||
{
|
||||
auto b = std::make_shared<Bonus>();
|
||||
b->type = Bonus::LEVEL_COUNTER;
|
||||
b->type = BonusType::LEVEL_COUNTER;
|
||||
b->val = 1;
|
||||
b->duration = Bonus::COMMANDER_KILLED;
|
||||
b->duration = BonusDuration::COMMANDER_KILLED;
|
||||
art->accumulateBonus(b);
|
||||
|
||||
for(const auto & bonus : bonusesPerLevel)
|
||||
{
|
||||
if (art->valOfBonuses(Bonus::LEVEL_COUNTER) % bonus.first == 0) //every n levels
|
||||
if (art->valOfBonuses(BonusType::LEVEL_COUNTER) % bonus.first == 0) //every n levels
|
||||
{
|
||||
art->accumulateBonus(std::make_shared<Bonus>(bonus.second));
|
||||
}
|
||||
}
|
||||
for(const auto & bonus : thresholdBonuses)
|
||||
{
|
||||
if (art->valOfBonuses(Bonus::LEVEL_COUNTER) == bonus.first) //every n levels
|
||||
if (art->valOfBonuses(BonusType::LEVEL_COUNTER) == bonus.first) //every n levels
|
||||
{
|
||||
art->addNewBonus(std::make_shared<Bonus>(bonus.second));
|
||||
}
|
||||
@ -790,7 +790,7 @@ void CArtHandler::afterLoadFinalization()
|
||||
for(auto &bonus : art->getExportedBonusList())
|
||||
{
|
||||
assert(art == objects[art->id]);
|
||||
assert(bonus->source == Bonus::ARTIFACT);
|
||||
assert(bonus->source == BonusSource::ARTIFACT);
|
||||
bonus->sid = art->id;
|
||||
}
|
||||
}
|
||||
@ -822,7 +822,7 @@ std::string CArtifactInstance::nodeName() const
|
||||
CArtifactInstance * CArtifactInstance::createScroll(const SpellID & sid)
|
||||
{
|
||||
auto * ret = new CArtifactInstance(VLC->arth->objects[ArtifactID::SPELL_SCROLL]);
|
||||
auto b = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, sid);
|
||||
auto b = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::SPELL, BonusSource::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, sid);
|
||||
ret->addNewBonus(b);
|
||||
return ret;
|
||||
}
|
||||
@ -903,7 +903,7 @@ CArtifactInstance * CArtifactInstance::createNewArtifactInstance(CArtifact *Art)
|
||||
if (dynamic_cast<CGrowingArtifact *>(Art))
|
||||
{
|
||||
auto bonus = std::make_shared<Bonus>();
|
||||
bonus->type = Bonus::LEVEL_COUNTER;
|
||||
bonus->type = BonusType::LEVEL_COUNTER;
|
||||
bonus->val = 0;
|
||||
ret->addNewBonus (bonus);
|
||||
}
|
||||
@ -963,7 +963,7 @@ void CArtifactInstance::deserializationFix()
|
||||
|
||||
SpellID CArtifactInstance::getScrollSpellID() const
|
||||
{
|
||||
const auto b = getBonusLocalFirst(Selector::type()(Bonus::SPELL));
|
||||
const auto b = getBonusLocalFirst(Selector::type()(BonusType::SPELL));
|
||||
if(!b)
|
||||
{
|
||||
logMod->warn("Warning: %s doesn't bear any spell!", nodeName());
|
||||
|
@ -12,7 +12,8 @@
|
||||
#include <vcmi/Artifact.h>
|
||||
#include <vcmi/ArtifactService.h>
|
||||
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "bonuses/CBonusSystemNode.h"
|
||||
#include "GameConstants.h"
|
||||
#include "IHandlerBase.h"
|
||||
|
||||
|
@ -66,7 +66,7 @@ CBonusTypeHandler::~CBonusTypeHandler()
|
||||
|
||||
std::string CBonusTypeHandler::bonusToString(const std::shared_ptr<Bonus> & bonus, const IBonusBearer * bearer, bool description) const
|
||||
{
|
||||
const CBonusType & bt = bonusTypes[bonus->type];
|
||||
const CBonusType & bt = bonusTypes[vstd::to_underlying(bonus->type)];
|
||||
if(bt.hidden)
|
||||
return "";
|
||||
|
||||
@ -82,9 +82,6 @@ std::string CBonusTypeHandler::bonusToString(const std::shared_ptr<Bonus> & bonu
|
||||
if (text.find("${subtype.spell}") != std::string::npos)
|
||||
boost::algorithm::replace_all(text, "${subtype.spell}", SpellID(bonus->subtype).toSpell()->getNameTranslated());
|
||||
|
||||
if (text.find("${SHval}") != std::string::npos) //regeneration case
|
||||
boost::algorithm::replace_all(text, "${SHval}", std::to_string(std::min(static_cast<si32>(bearer->MaxHealth()),bearer->valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype)))));
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
@ -95,14 +92,14 @@ std::string CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bo
|
||||
|
||||
switch(bonus->type)
|
||||
{
|
||||
case Bonus::SPELL_IMMUNITY:
|
||||
case BonusType::SPELL_IMMUNITY:
|
||||
{
|
||||
fullPath = true;
|
||||
const CSpell * sp = SpellID(bonus->subtype).toSpell();
|
||||
fileName = sp->getIconImmune();
|
||||
break;
|
||||
}
|
||||
case Bonus::FIRE_IMMUNITY:
|
||||
case BonusType::FIRE_IMMUNITY:
|
||||
switch(bonus->subtype)
|
||||
{
|
||||
case 0:
|
||||
@ -116,7 +113,7 @@ std::string CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bo
|
||||
break;//direct damage
|
||||
}
|
||||
break;
|
||||
case Bonus::WATER_IMMUNITY:
|
||||
case BonusType::WATER_IMMUNITY:
|
||||
switch(bonus->subtype)
|
||||
{
|
||||
case 0:
|
||||
@ -130,7 +127,7 @@ std::string CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bo
|
||||
break;//direct damage
|
||||
}
|
||||
break;
|
||||
case Bonus::AIR_IMMUNITY:
|
||||
case BonusType::AIR_IMMUNITY:
|
||||
switch(bonus->subtype)
|
||||
{
|
||||
case 0:
|
||||
@ -144,7 +141,7 @@ std::string CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bo
|
||||
break;//direct damage
|
||||
}
|
||||
break;
|
||||
case Bonus::EARTH_IMMUNITY:
|
||||
case BonusType::EARTH_IMMUNITY:
|
||||
switch(bonus->subtype)
|
||||
{
|
||||
case 0:
|
||||
@ -156,7 +153,7 @@ std::string CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bo
|
||||
break;//not positive
|
||||
}
|
||||
break;
|
||||
case Bonus::LEVEL_SPELL_IMMUNITY:
|
||||
case BonusType::LEVEL_SPELL_IMMUNITY:
|
||||
{
|
||||
if(vstd::iswithin(bonus->val, 1, 5))
|
||||
{
|
||||
@ -164,7 +161,7 @@ std::string CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bo
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Bonus::KING:
|
||||
case BonusType::KING:
|
||||
{
|
||||
if(vstd::iswithin(bonus->val, 0, 3))
|
||||
{
|
||||
@ -172,7 +169,7 @@ std::string CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bo
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Bonus::GENERAL_DAMAGE_REDUCTION:
|
||||
case BonusType::GENERAL_DAMAGE_REDUCTION:
|
||||
{
|
||||
switch(bonus->subtype)
|
||||
{
|
||||
@ -188,7 +185,7 @@ std::string CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bo
|
||||
|
||||
default:
|
||||
{
|
||||
const CBonusType & bt = bonusTypes[bonus->type];
|
||||
const CBonusType & bt = bonusTypes[vstd::to_underlying(bonus->type)];
|
||||
fileName = bt.icon;
|
||||
fullPath = true;
|
||||
}
|
||||
@ -227,7 +224,7 @@ void CBonusTypeHandler::load(const JsonNode & config)
|
||||
}
|
||||
else
|
||||
{
|
||||
CBonusType & bt = bonusTypes[it->second];
|
||||
CBonusType & bt = bonusTypes[vstd::to_underlying(it->second)];
|
||||
|
||||
loadItem(node.second, bt, node.first);
|
||||
logBonus->trace("Loaded bonus type %s", node.first);
|
||||
|
@ -12,15 +12,13 @@
|
||||
|
||||
#include "IBonusTypeHandler.h"
|
||||
#include "IHandlerBase.h"
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
class JsonNode;
|
||||
|
||||
using BonusTypeID = Bonus::BonusType;
|
||||
|
||||
class DLL_LINKAGE CBonusType
|
||||
{
|
||||
public:
|
||||
@ -67,7 +65,7 @@ private:
|
||||
void load(const JsonNode & config);
|
||||
void loadItem(const JsonNode & source, CBonusType & dest, const std::string & name) const;
|
||||
|
||||
std::vector<CBonusType> bonusTypes; //index = BonusTypeID
|
||||
std::vector<CBonusType> bonusTypes; //index = BonusType
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "CModHandler.h"
|
||||
#include "GameSettings.h"
|
||||
#include "StringConstants.h"
|
||||
#include "bonuses/Limiters.h"
|
||||
#include "bonuses/Updaters.h"
|
||||
#include "serializer/JsonDeserializer.h"
|
||||
#include "serializer/JsonUpdater.h"
|
||||
#include "mapObjects/CObjectClassesHandler.h"
|
||||
@ -69,11 +71,6 @@ const IBonusBearer * CCreature::getBonusBearer() const
|
||||
return this;
|
||||
}
|
||||
|
||||
uint32_t CCreature::getMaxHealth() const
|
||||
{
|
||||
return CBonusSystemNode::MaxHealth();
|
||||
}
|
||||
|
||||
int32_t CCreature::getAdvMapAmountMin() const
|
||||
{
|
||||
return ammMin;
|
||||
@ -116,49 +113,49 @@ FactionID CCreature::getFaction() const
|
||||
|
||||
int32_t CCreature::getBaseAttack() const
|
||||
{
|
||||
static const auto SELECTOR = Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY));
|
||||
static const auto SELECTOR = Selector::typeSubtype(BonusType::PRIMARY_SKILL, PrimarySkill::ATTACK).And(Selector::sourceTypeSel(BonusSource::CREATURE_ABILITY));
|
||||
return getExportedBonusList().valOfBonuses(SELECTOR);
|
||||
}
|
||||
|
||||
int32_t CCreature::getBaseDefense() const
|
||||
{
|
||||
static const auto SELECTOR = Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY));
|
||||
static const auto SELECTOR = Selector::typeSubtype(BonusType::PRIMARY_SKILL, PrimarySkill::DEFENSE).And(Selector::sourceTypeSel(BonusSource::CREATURE_ABILITY));
|
||||
return getExportedBonusList().valOfBonuses(SELECTOR);
|
||||
}
|
||||
|
||||
int32_t CCreature::getBaseDamageMin() const
|
||||
{
|
||||
static const auto SELECTOR = Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY));
|
||||
static const auto SELECTOR = Selector::typeSubtype(BonusType::CREATURE_DAMAGE, 1).And(Selector::sourceTypeSel(BonusSource::CREATURE_ABILITY));
|
||||
return getExportedBonusList().valOfBonuses(SELECTOR);
|
||||
}
|
||||
|
||||
int32_t CCreature::getBaseDamageMax() const
|
||||
{
|
||||
static const auto SELECTOR = Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY));
|
||||
static const auto SELECTOR = Selector::typeSubtype(BonusType::CREATURE_DAMAGE, 2).And(Selector::sourceTypeSel(BonusSource::CREATURE_ABILITY));
|
||||
return getExportedBonusList().valOfBonuses(SELECTOR);
|
||||
}
|
||||
|
||||
int32_t CCreature::getBaseHitPoints() const
|
||||
{
|
||||
static const auto SELECTOR = Selector::type()(Bonus::STACK_HEALTH).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY));
|
||||
static const auto SELECTOR = Selector::type()(BonusType::STACK_HEALTH).And(Selector::sourceTypeSel(BonusSource::CREATURE_ABILITY));
|
||||
return getExportedBonusList().valOfBonuses(SELECTOR);
|
||||
}
|
||||
|
||||
int32_t CCreature::getBaseSpellPoints() const
|
||||
{
|
||||
static const auto SELECTOR = Selector::type()(Bonus::CASTS).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY));
|
||||
static const auto SELECTOR = Selector::type()(BonusType::CASTS).And(Selector::sourceTypeSel(BonusSource::CREATURE_ABILITY));
|
||||
return getExportedBonusList().valOfBonuses(SELECTOR);
|
||||
}
|
||||
|
||||
int32_t CCreature::getBaseSpeed() const
|
||||
{
|
||||
static const auto SELECTOR = Selector::type()(Bonus::STACKS_SPEED).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY));
|
||||
static const auto SELECTOR = Selector::type()(BonusType::STACKS_SPEED).And(Selector::sourceTypeSel(BonusSource::CREATURE_ABILITY));
|
||||
return getExportedBonusList().valOfBonuses(SELECTOR);
|
||||
}
|
||||
|
||||
int32_t CCreature::getBaseShots() const
|
||||
{
|
||||
static const auto SELECTOR = Selector::type()(Bonus::SHOTS).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY));
|
||||
static const auto SELECTOR = Selector::type()(BonusType::SHOTS).And(Selector::sourceTypeSel(BonusSource::CREATURE_ABILITY));
|
||||
return getExportedBonusList().valOfBonuses(SELECTOR);
|
||||
}
|
||||
|
||||
@ -294,9 +291,9 @@ CCreature::CCreature()
|
||||
fightValue = AIValue = growth = hordeGrowth = ammMin = ammMax = 0;
|
||||
}
|
||||
|
||||
void CCreature::addBonus(int val, Bonus::BonusType type, int subtype)
|
||||
void CCreature::addBonus(int val, BonusType type, int subtype)
|
||||
{
|
||||
auto selector = Selector::typeSubtype(type, subtype).And(Selector::source(Bonus::CREATURE_ABILITY, getIndex()));
|
||||
auto selector = Selector::typeSubtype(type, subtype).And(Selector::source(BonusSource::CREATURE_ABILITY, getIndex()));
|
||||
BonusList & exported = getExportedBonusList();
|
||||
|
||||
BonusList existing;
|
||||
@ -304,7 +301,7 @@ void CCreature::addBonus(int val, Bonus::BonusType type, int subtype)
|
||||
|
||||
if(existing.empty())
|
||||
{
|
||||
auto added = std::make_shared<Bonus>(Bonus::PERMANENT, type, Bonus::CREATURE_ABILITY, val, getIndex(), subtype, Bonus::BASE_NUMBER);
|
||||
auto added = std::make_shared<Bonus>(BonusDuration::PERMANENT, type, BonusSource::CREATURE_ABILITY, val, getIndex(), subtype, BonusValueType::BASE_NUMBER);
|
||||
addNewBonus(added);
|
||||
}
|
||||
else
|
||||
@ -342,28 +339,28 @@ void CCreature::updateFrom(const JsonNode & data)
|
||||
serializeJson(handler);
|
||||
|
||||
if(!configNode["hitPoints"].isNull())
|
||||
addBonus(configNode["hitPoints"].Integer(), Bonus::STACK_HEALTH);
|
||||
addBonus(configNode["hitPoints"].Integer(), BonusType::STACK_HEALTH);
|
||||
|
||||
if(!configNode["speed"].isNull())
|
||||
addBonus(configNode["speed"].Integer(), Bonus::STACKS_SPEED);
|
||||
addBonus(configNode["speed"].Integer(), BonusType::STACKS_SPEED);
|
||||
|
||||
if(!configNode["attack"].isNull())
|
||||
addBonus(configNode["attack"].Integer(), Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
|
||||
addBonus(configNode["attack"].Integer(), BonusType::PRIMARY_SKILL, PrimarySkill::ATTACK);
|
||||
|
||||
if(!configNode["defense"].isNull())
|
||||
addBonus(configNode["defense"].Integer(), Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE);
|
||||
addBonus(configNode["defense"].Integer(), BonusType::PRIMARY_SKILL, PrimarySkill::DEFENSE);
|
||||
|
||||
if(!configNode["damage"]["min"].isNull())
|
||||
addBonus(configNode["damage"]["min"].Integer(), Bonus::CREATURE_DAMAGE, 1);
|
||||
addBonus(configNode["damage"]["min"].Integer(), BonusType::CREATURE_DAMAGE, 1);
|
||||
|
||||
if(!configNode["damage"]["max"].isNull())
|
||||
addBonus(configNode["damage"]["max"].Integer(), Bonus::CREATURE_DAMAGE, 2);
|
||||
addBonus(configNode["damage"]["max"].Integer(), BonusType::CREATURE_DAMAGE, 2);
|
||||
|
||||
if(!configNode["shots"].isNull())
|
||||
addBonus(configNode["shots"].Integer(), Bonus::SHOTS);
|
||||
addBonus(configNode["shots"].Integer(), BonusType::SHOTS);
|
||||
|
||||
if(!configNode["spellPoints"].isNull())
|
||||
addBonus(configNode["spellPoints"].Integer(), Bonus::CASTS);
|
||||
addBonus(configNode["spellPoints"].Integer(), BonusType::CASTS);
|
||||
}
|
||||
|
||||
|
||||
@ -604,18 +601,18 @@ CCreature * CCreatureHandler::loadFromJson(const std::string & scope, const Json
|
||||
VLC->generaltexth->registerString(scope, cre->getNameSingularTextID(), node["name"]["singular"].String());
|
||||
VLC->generaltexth->registerString(scope, cre->getNamePluralTextID(), node["name"]["plural"].String());
|
||||
|
||||
cre->addBonus(node["hitPoints"].Integer(), Bonus::STACK_HEALTH);
|
||||
cre->addBonus(node["speed"].Integer(), Bonus::STACKS_SPEED);
|
||||
cre->addBonus(node["attack"].Integer(), Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
|
||||
cre->addBonus(node["defense"].Integer(), Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE);
|
||||
cre->addBonus(node["hitPoints"].Integer(), BonusType::STACK_HEALTH);
|
||||
cre->addBonus(node["speed"].Integer(), BonusType::STACKS_SPEED);
|
||||
cre->addBonus(node["attack"].Integer(), BonusType::PRIMARY_SKILL, PrimarySkill::ATTACK);
|
||||
cre->addBonus(node["defense"].Integer(), BonusType::PRIMARY_SKILL, PrimarySkill::DEFENSE);
|
||||
|
||||
cre->addBonus(node["damage"]["min"].Integer(), Bonus::CREATURE_DAMAGE, 1);
|
||||
cre->addBonus(node["damage"]["max"].Integer(), Bonus::CREATURE_DAMAGE, 2);
|
||||
cre->addBonus(node["damage"]["min"].Integer(), BonusType::CREATURE_DAMAGE, 1);
|
||||
cre->addBonus(node["damage"]["max"].Integer(), BonusType::CREATURE_DAMAGE, 2);
|
||||
|
||||
assert(node["damage"]["min"].Integer() <= node["damage"]["max"].Integer());
|
||||
|
||||
if(!node["shots"].isNull())
|
||||
cre->addBonus(node["shots"].Integer(), Bonus::SHOTS);
|
||||
cre->addBonus(node["shots"].Integer(), BonusType::SHOTS);
|
||||
|
||||
loadStackExperience(cre, node["stackExperience"]);
|
||||
loadJsonAnimation(cre, node["graphics"]);
|
||||
@ -711,8 +708,8 @@ void CCreatureHandler::loadCrExpMod()
|
||||
expBonParser.endLine();
|
||||
}
|
||||
//skeleton gets exp penalty
|
||||
objects[56].get()->addBonus(-50, Bonus::EXP_MULTIPLIER, -1);
|
||||
objects[57].get()->addBonus(-50, Bonus::EXP_MULTIPLIER, -1);
|
||||
objects[56].get()->addBonus(-50, BonusType::EXP_MULTIPLIER, -1);
|
||||
objects[57].get()->addBonus(-50, BonusType::EXP_MULTIPLIER, -1);
|
||||
//exp for tier >7, rank 11
|
||||
expRanks[0].push_back(147000);
|
||||
expAfterUpgrade = 75; //percent
|
||||
@ -743,10 +740,10 @@ void CCreatureHandler::loadCrExpBon(CBonusSystemNode & globalEffects)
|
||||
CLegacyConfigParser parser("DATA/CREXPBON.TXT");
|
||||
|
||||
Bonus b; //prototype with some default properties
|
||||
b.source = Bonus::STACK_EXPERIENCE;
|
||||
b.duration = Bonus::PERMANENT;
|
||||
b.valType = Bonus::ADDITIVE_VALUE;
|
||||
b.effectRange = Bonus::NO_LIMIT;
|
||||
b.source = BonusSource::STACK_EXPERIENCE;
|
||||
b.duration = BonusDuration::PERMANENT;
|
||||
b.valType = BonusValueType::ADDITIVE_VALUE;
|
||||
b.effectRange = BonusLimitEffect::NO_LIMIT;
|
||||
b.additionalInfo = 0;
|
||||
b.turnsRemain = 0;
|
||||
BonusList bl;
|
||||
@ -890,9 +887,9 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
|
||||
if (!ability.second.isNull())
|
||||
{
|
||||
auto b = JsonUtils::parseBonus(ability.second);
|
||||
b->source = Bonus::CREATURE_ABILITY;
|
||||
b->source = BonusSource::CREATURE_ABILITY;
|
||||
b->sid = creature->getIndex();
|
||||
b->duration = Bonus::PERMANENT;
|
||||
b->duration = BonusDuration::PERMANENT;
|
||||
creature->addNewBonus(b);
|
||||
}
|
||||
}
|
||||
@ -908,9 +905,9 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
|
||||
else
|
||||
{
|
||||
auto b = JsonUtils::parseBonus(ability);
|
||||
b->source = Bonus::CREATURE_ABILITY;
|
||||
b->source = BonusSource::CREATURE_ABILITY;
|
||||
b->sid = creature->getIndex();
|
||||
b->duration = Bonus::PERMANENT;
|
||||
b->duration = BonusDuration::PERMANENT;
|
||||
creature->addNewBonus(b);
|
||||
}
|
||||
}
|
||||
@ -980,8 +977,8 @@ void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode
|
||||
// we can not create copies since identifiers resolution does not tracks copies
|
||||
// leading to unset identifier values in copies
|
||||
auto bonus = JsonUtils::parseBonus (exp["bonus"]);
|
||||
bonus->source = Bonus::STACK_EXPERIENCE;
|
||||
bonus->duration = Bonus::PERMANENT;
|
||||
bonus->source = BonusSource::STACK_EXPERIENCE;
|
||||
bonus->duration = BonusDuration::PERMANENT;
|
||||
bonus->limiter = std::make_shared<RankRangeLimiter>(RankRangeLimiter(lowerLimit));
|
||||
creature->addNewBonus (bonus);
|
||||
break; //TODO: allow bonuses to turn off?
|
||||
@ -1000,8 +997,8 @@ void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode
|
||||
bonusInput["val"].Float() = static_cast<int>(val.Float()) - lastVal;
|
||||
|
||||
auto bonus = JsonUtils::parseBonus (bonusInput);
|
||||
bonus->source = Bonus::STACK_EXPERIENCE;
|
||||
bonus->duration = Bonus::PERMANENT;
|
||||
bonus->source = BonusSource::STACK_EXPERIENCE;
|
||||
bonus->duration = BonusDuration::PERMANENT;
|
||||
bonus->limiter.reset (new RankRangeLimiter(lowerLimit));
|
||||
creature->addNewBonus (bonus);
|
||||
}
|
||||
@ -1021,55 +1018,55 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
|
||||
switch (buf[0])
|
||||
{
|
||||
case 'H':
|
||||
b.type = Bonus::STACK_HEALTH;
|
||||
b.valType = Bonus::PERCENT_TO_BASE;
|
||||
b.type = BonusType::STACK_HEALTH;
|
||||
b.valType = BonusValueType::PERCENT_TO_BASE;
|
||||
break;
|
||||
case 'A':
|
||||
b.type = Bonus::PRIMARY_SKILL;
|
||||
b.type = BonusType::PRIMARY_SKILL;
|
||||
b.subtype = PrimarySkill::ATTACK;
|
||||
break;
|
||||
case 'D':
|
||||
b.type = Bonus::PRIMARY_SKILL;
|
||||
b.type = BonusType::PRIMARY_SKILL;
|
||||
b.subtype = PrimarySkill::DEFENSE;
|
||||
break;
|
||||
case 'M': //Max damage
|
||||
b.type = Bonus::CREATURE_DAMAGE;
|
||||
b.type = BonusType::CREATURE_DAMAGE;
|
||||
b.subtype = 2;
|
||||
break;
|
||||
case 'm': //Min damage
|
||||
b.type = Bonus::CREATURE_DAMAGE;
|
||||
b.type = BonusType::CREATURE_DAMAGE;
|
||||
b.subtype = 1;
|
||||
break;
|
||||
case 'S':
|
||||
b.type = Bonus::STACKS_SPEED; break;
|
||||
b.type = BonusType::STACKS_SPEED; break;
|
||||
case 'O':
|
||||
b.type = Bonus::SHOTS; break;
|
||||
b.type = BonusType::SHOTS; break;
|
||||
case 'b':
|
||||
b.type = Bonus::ENEMY_DEFENCE_REDUCTION; break;
|
||||
b.type = BonusType::ENEMY_DEFENCE_REDUCTION; break;
|
||||
case 'C':
|
||||
b.type = Bonus::CHANGES_SPELL_COST_FOR_ALLY; break;
|
||||
b.type = BonusType::CHANGES_SPELL_COST_FOR_ALLY; break;
|
||||
case 'd':
|
||||
b.type = Bonus::DEFENSIVE_STANCE; break;
|
||||
b.type = BonusType::DEFENSIVE_STANCE; break;
|
||||
case 'e':
|
||||
b.type = Bonus::DOUBLE_DAMAGE_CHANCE;
|
||||
b.type = BonusType::DOUBLE_DAMAGE_CHANCE;
|
||||
b.subtype = 0;
|
||||
break;
|
||||
case 'E':
|
||||
b.type = Bonus::DEATH_STARE;
|
||||
b.type = BonusType::DEATH_STARE;
|
||||
b.subtype = 0; //Gorgon
|
||||
break;
|
||||
case 'F':
|
||||
b.type = Bonus::FEAR; break;
|
||||
b.type = BonusType::FEAR; break;
|
||||
case 'g':
|
||||
b.type = Bonus::SPELL_DAMAGE_REDUCTION;
|
||||
b.type = BonusType::SPELL_DAMAGE_REDUCTION;
|
||||
b.subtype = -1; //all magic schools
|
||||
break;
|
||||
case 'P':
|
||||
b.type = Bonus::CASTS; break;
|
||||
b.type = BonusType::CASTS; break;
|
||||
case 'R':
|
||||
b.type = Bonus::ADDITIONAL_RETALIATION; break;
|
||||
b.type = BonusType::ADDITIONAL_RETALIATION; break;
|
||||
case 'W':
|
||||
b.type = Bonus::MAGIC_RESISTANCE;
|
||||
b.type = BonusType::MAGIC_RESISTANCE;
|
||||
b.subtype = 0; //otherwise creature window goes crazy
|
||||
break;
|
||||
case 'f': //on-off skill
|
||||
@ -1077,44 +1074,44 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
|
||||
switch (mod[0])
|
||||
{
|
||||
case 'A':
|
||||
b.type = Bonus::ATTACKS_ALL_ADJACENT; break;
|
||||
b.type = BonusType::ATTACKS_ALL_ADJACENT; break;
|
||||
case 'b':
|
||||
b.type = Bonus::RETURN_AFTER_STRIKE; break;
|
||||
b.type = BonusType::RETURN_AFTER_STRIKE; break;
|
||||
case 'B':
|
||||
b.type = Bonus::TWO_HEX_ATTACK_BREATH; break;
|
||||
b.type = BonusType::TWO_HEX_ATTACK_BREATH; break;
|
||||
case 'c':
|
||||
b.type = Bonus::JOUSTING;
|
||||
b.type = BonusType::JOUSTING;
|
||||
b.val = 5;
|
||||
break;
|
||||
case 'D':
|
||||
b.type = Bonus::ADDITIONAL_ATTACK; break;
|
||||
b.type = BonusType::ADDITIONAL_ATTACK; break;
|
||||
case 'f':
|
||||
b.type = Bonus::FEARLESS; break;
|
||||
b.type = BonusType::FEARLESS; break;
|
||||
case 'F':
|
||||
b.type = Bonus::FLYING; break;
|
||||
b.type = BonusType::FLYING; break;
|
||||
case 'm':
|
||||
b.type = Bonus::MORALE; break;
|
||||
b.type = BonusType::MORALE; break;
|
||||
b.val = 1;
|
||||
b.valType = Bonus::INDEPENDENT_MAX;
|
||||
b.valType = BonusValueType::INDEPENDENT_MAX;
|
||||
break;
|
||||
case 'M':
|
||||
b.type = Bonus::NO_MORALE; break;
|
||||
b.type = BonusType::NO_MORALE; break;
|
||||
case 'p': //Mind spells
|
||||
case 'P':
|
||||
b.type = Bonus::MIND_IMMUNITY; break;
|
||||
b.type = BonusType::MIND_IMMUNITY; break;
|
||||
case 'r':
|
||||
b.type = Bonus::REBIRTH; //on/off? makes sense?
|
||||
b.type = BonusType::REBIRTH; //on/off? makes sense?
|
||||
b.subtype = 0;
|
||||
b.val = 20; //arbitrary value
|
||||
break;
|
||||
case 'R':
|
||||
b.type = Bonus::BLOCKS_RETALIATION; break;
|
||||
b.type = BonusType::BLOCKS_RETALIATION; break;
|
||||
case 's':
|
||||
b.type = Bonus::FREE_SHOOTING; break;
|
||||
b.type = BonusType::FREE_SHOOTING; break;
|
||||
case 'u':
|
||||
b.type = Bonus::SPELL_RESISTANCE_AURA; break;
|
||||
b.type = BonusType::SPELL_RESISTANCE_AURA; break;
|
||||
case 'U':
|
||||
b.type = Bonus::UNDEAD; break;
|
||||
b.type = BonusType::UNDEAD; break;
|
||||
default:
|
||||
logGlobal->trace("Not parsed bonus %s %s", buf, mod);
|
||||
return;
|
||||
@ -1126,42 +1123,42 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
|
||||
switch (mod[0])
|
||||
{
|
||||
case 'B': //Blind
|
||||
b.type = Bonus::SPELL_IMMUNITY;
|
||||
b.type = BonusType::SPELL_IMMUNITY;
|
||||
b.subtype = SpellID::BLIND;
|
||||
b.additionalInfo = 0;//normal immunity
|
||||
break;
|
||||
case 'H': //Hypnotize
|
||||
b.type = Bonus::SPELL_IMMUNITY;
|
||||
b.type = BonusType::SPELL_IMMUNITY;
|
||||
b.subtype = SpellID::HYPNOTIZE;
|
||||
b.additionalInfo = 0;//normal immunity
|
||||
break;
|
||||
case 'I': //Implosion
|
||||
b.type = Bonus::SPELL_IMMUNITY;
|
||||
b.type = BonusType::SPELL_IMMUNITY;
|
||||
b.subtype = SpellID::IMPLOSION;
|
||||
b.additionalInfo = 0;//normal immunity
|
||||
break;
|
||||
case 'K': //Berserk
|
||||
b.type = Bonus::SPELL_IMMUNITY;
|
||||
b.type = BonusType::SPELL_IMMUNITY;
|
||||
b.subtype = SpellID::BERSERK;
|
||||
b.additionalInfo = 0;//normal immunity
|
||||
break;
|
||||
case 'M': //Meteor Shower
|
||||
b.type = Bonus::SPELL_IMMUNITY;
|
||||
b.type = BonusType::SPELL_IMMUNITY;
|
||||
b.subtype = SpellID::METEOR_SHOWER;
|
||||
b.additionalInfo = 0;//normal immunity
|
||||
break;
|
||||
case 'N': //dispell beneficial spells
|
||||
b.type = Bonus::SPELL_IMMUNITY;
|
||||
b.type = BonusType::SPELL_IMMUNITY;
|
||||
b.subtype = SpellID::DISPEL_HELPFUL_SPELLS;
|
||||
b.additionalInfo = 0;//normal immunity
|
||||
break;
|
||||
case 'R': //Armageddon
|
||||
b.type = Bonus::SPELL_IMMUNITY;
|
||||
b.type = BonusType::SPELL_IMMUNITY;
|
||||
b.subtype = SpellID::ARMAGEDDON;
|
||||
b.additionalInfo = 0;//normal immunity
|
||||
break;
|
||||
case 'S': //Slow
|
||||
b.type = Bonus::SPELL_IMMUNITY;
|
||||
b.type = BonusType::SPELL_IMMUNITY;
|
||||
b.subtype = SpellID::SLOW;
|
||||
b.additionalInfo = 0;//normal immunity
|
||||
break;
|
||||
@ -1169,61 +1166,61 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
b.type = Bonus::LEVEL_SPELL_IMMUNITY;
|
||||
b.type = BonusType::LEVEL_SPELL_IMMUNITY;
|
||||
b.val = std::atoi(mod.c_str()) - 5;
|
||||
break;
|
||||
case ':':
|
||||
b.type = Bonus::LEVEL_SPELL_IMMUNITY;
|
||||
b.type = BonusType::LEVEL_SPELL_IMMUNITY;
|
||||
b.val = GameConstants::SPELL_LEVELS; //in case someone adds higher level spells?
|
||||
break;
|
||||
case 'F':
|
||||
b.type = Bonus::FIRE_IMMUNITY;
|
||||
b.type = BonusType::FIRE_IMMUNITY;
|
||||
b.subtype = 1; //not positive
|
||||
break;
|
||||
case 'O':
|
||||
b.type = Bonus::FIRE_IMMUNITY;
|
||||
b.type = BonusType::FIRE_IMMUNITY;
|
||||
b.subtype = 2; //only direct damage
|
||||
break;
|
||||
case 'f':
|
||||
b.type = Bonus::FIRE_IMMUNITY;
|
||||
b.type = BonusType::FIRE_IMMUNITY;
|
||||
b.subtype = 0; //all
|
||||
break;
|
||||
case 'C':
|
||||
b.type = Bonus::WATER_IMMUNITY;
|
||||
b.type = BonusType::WATER_IMMUNITY;
|
||||
b.subtype = 1; //not positive
|
||||
break;
|
||||
case 'W':
|
||||
b.type = Bonus::WATER_IMMUNITY;
|
||||
b.type = BonusType::WATER_IMMUNITY;
|
||||
b.subtype = 2; //only direct damage
|
||||
break;
|
||||
case 'w':
|
||||
b.type = Bonus::WATER_IMMUNITY;
|
||||
b.type = BonusType::WATER_IMMUNITY;
|
||||
b.subtype = 0; //all
|
||||
break;
|
||||
case 'E':
|
||||
b.type = Bonus::EARTH_IMMUNITY;
|
||||
b.type = BonusType::EARTH_IMMUNITY;
|
||||
b.subtype = 2; //only direct damage
|
||||
break;
|
||||
case 'e':
|
||||
b.type = Bonus::EARTH_IMMUNITY;
|
||||
b.type = BonusType::EARTH_IMMUNITY;
|
||||
b.subtype = 0; //all
|
||||
break;
|
||||
case 'A':
|
||||
b.type = Bonus::AIR_IMMUNITY;
|
||||
b.type = BonusType::AIR_IMMUNITY;
|
||||
b.subtype = 2; //only direct damage
|
||||
break;
|
||||
case 'a':
|
||||
b.type = Bonus::AIR_IMMUNITY;
|
||||
b.type = BonusType::AIR_IMMUNITY;
|
||||
b.subtype = 0; //all
|
||||
break;
|
||||
case 'D':
|
||||
b.type = Bonus::DIRECT_DAMAGE_IMMUNITY;
|
||||
b.type = BonusType::DIRECT_DAMAGE_IMMUNITY;
|
||||
break;
|
||||
case '0':
|
||||
b.type = Bonus::RECEPTIVE;
|
||||
b.type = BonusType::RECEPTIVE;
|
||||
break;
|
||||
case 'm':
|
||||
b.type = Bonus::MIND_IMMUNITY;
|
||||
b.type = BonusType::MIND_IMMUNITY;
|
||||
break;
|
||||
default:
|
||||
logGlobal->trace("Not parsed bonus %s %s", buf, mod);
|
||||
@ -1233,38 +1230,38 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
|
||||
|
||||
case 'i':
|
||||
enable = true;
|
||||
b.type = Bonus::NO_DISTANCE_PENALTY;
|
||||
b.type = BonusType::NO_DISTANCE_PENALTY;
|
||||
break;
|
||||
case 'o':
|
||||
enable = true;
|
||||
b.type = Bonus::NO_WALL_PENALTY;
|
||||
b.type = BonusType::NO_WALL_PENALTY;
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
case 'c':
|
||||
case 'K':
|
||||
case 'k':
|
||||
b.type = Bonus::SPELL_AFTER_ATTACK;
|
||||
b.type = BonusType::SPELL_AFTER_ATTACK;
|
||||
b.subtype = stringToNumber(mod);
|
||||
break;
|
||||
case 'h':
|
||||
b.type= Bonus::HATE;
|
||||
b.type = BonusType::HATE;
|
||||
b.subtype = stringToNumber(mod);
|
||||
break;
|
||||
case 'p':
|
||||
case 'J':
|
||||
b.type = Bonus::SPELL_BEFORE_ATTACK;
|
||||
b.type = BonusType::SPELL_BEFORE_ATTACK;
|
||||
b.subtype = stringToNumber(mod);
|
||||
b.additionalInfo = 3; //always expert?
|
||||
break;
|
||||
case 'r':
|
||||
b.type = Bonus::HP_REGENERATION;
|
||||
b.type = BonusType::HP_REGENERATION;
|
||||
b.val = stringToNumber(mod);
|
||||
break;
|
||||
case 's':
|
||||
b.type = Bonus::ENCHANTED;
|
||||
b.type = BonusType::ENCHANTED;
|
||||
b.subtype = stringToNumber(mod);
|
||||
b.valType = Bonus::INDEPENDENT_MAX;
|
||||
b.valType = BonusValueType::INDEPENDENT_MAX;
|
||||
break;
|
||||
default:
|
||||
logGlobal->trace("Not parsed bonus %s %s", buf, mod);
|
||||
@ -1275,7 +1272,7 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
|
||||
{
|
||||
case '+':
|
||||
case '=': //should we allow percent values to stack or pick highest?
|
||||
b.valType = Bonus::ADDITIVE_VALUE;
|
||||
b.valType = BonusValueType::ADDITIVE_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1286,7 +1283,7 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
|
||||
|
||||
if (enable) //0 and 2 means non-active, 1 - active
|
||||
{
|
||||
if (b.type != Bonus::REBIRTH)
|
||||
if (b.type != BonusType::REBIRTH)
|
||||
b.val = 0; //on-off ability, no value specified
|
||||
parser.readNumber(); // 0 level is never active
|
||||
for (int i = 1; i < 11; ++i)
|
||||
@ -1303,13 +1300,13 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
|
||||
else
|
||||
{
|
||||
lastVal = static_cast<si32>(parser.readNumber());
|
||||
if (b.type == Bonus::HATE)
|
||||
if (b.type == BonusType::HATE)
|
||||
lastVal *= 10; //odd fix
|
||||
//FIXME: value for zero level should be stored in our config files (independent of stack exp)
|
||||
for (int i = 1; i < 11; ++i)
|
||||
{
|
||||
curVal = static_cast<si32>(parser.readNumber());
|
||||
if (b.type == Bonus::HATE)
|
||||
if (b.type == BonusType::HATE)
|
||||
curVal *= 10; //odd fix
|
||||
if (curVal > lastVal) //threshold, add new bonus
|
||||
{
|
||||
|
@ -9,7 +9,8 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "bonuses/CBonusSystemNode.h"
|
||||
#include "ConstTransitivePtr.h"
|
||||
#include "ResourceSet.h"
|
||||
#include "GameConstants.h"
|
||||
@ -168,7 +169,6 @@ public:
|
||||
void registerIcons(const IconRegistar & cb) const override;
|
||||
CreatureID getId() const override;
|
||||
virtual const IBonusBearer * getBonusBearer() const override;
|
||||
uint32_t getMaxHealth() const override;
|
||||
|
||||
int32_t getAdvMapAmountMin() const override;
|
||||
int32_t getAdvMapAmountMax() const override;
|
||||
@ -202,7 +202,7 @@ public:
|
||||
|
||||
bool valid() const;
|
||||
|
||||
void addBonus(int val, Bonus::BonusType type, int subtype = -1);
|
||||
void addBonus(int val, BonusType type, int subtype = -1);
|
||||
std::string nodeName() const override;
|
||||
|
||||
template<typename RanGen>
|
||||
|
@ -736,13 +736,6 @@ int CStackInstance::getLevel() const
|
||||
return std::max(1, static_cast<int>(type->getLevel()));
|
||||
}
|
||||
|
||||
si32 CStackInstance::magicResistance() const
|
||||
{
|
||||
si32 val = valOfBonuses(Selector::type()(Bonus::MAGIC_RESISTANCE));
|
||||
vstd::amin (val, 100);
|
||||
return val;
|
||||
}
|
||||
|
||||
void CStackInstance::giveStackExp(TExpType exp)
|
||||
{
|
||||
int level = type->getLevel();
|
||||
|
@ -9,7 +9,8 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "bonuses/CBonusSystemNode.h"
|
||||
#include "GameConstants.h"
|
||||
#include "CArtHandler.h"
|
||||
#include "CCreatureHandler.h"
|
||||
@ -63,7 +64,7 @@ public:
|
||||
void serializeJson(JsonSerializeFormat & handler);
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CStackInstance : public CBonusSystemNode, public CStackBasicDescriptor, public CArtifactSet, public IConstBonusNativeTerrainProvider
|
||||
class DLL_LINKAGE CStackInstance : public CBonusSystemNode, public CStackBasicDescriptor, public CArtifactSet, public ACreature
|
||||
{
|
||||
protected:
|
||||
const CArmedInstance *_armyObj; //stack must be part of some army, army must be part of some object
|
||||
@ -106,7 +107,6 @@ public:
|
||||
std::string getQuantityTXT(bool capitalized = true) const;
|
||||
virtual int getExpRank() const;
|
||||
virtual int getLevel() const; //different for regular stack and commander
|
||||
si32 magicResistance() const override;
|
||||
CreatureID getCreatureID() const; //-1 if not available
|
||||
std::string getName() const; //plural or singular
|
||||
virtual void init();
|
||||
|
@ -314,7 +314,7 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
|
||||
if(getPlayerRelations(getLocalPlayer(), hero->tempOwner) == PlayerRelations::ENEMIES)
|
||||
{
|
||||
//todo: bonus cashing
|
||||
int disguiseLevel = h->valOfBonuses(Selector::typeSubtype(Bonus::DISGUISED, 0));
|
||||
int disguiseLevel = h->valOfBonuses(Selector::typeSubtype(BonusType::DISGUISED, 0));
|
||||
|
||||
auto doBasicDisguise = [](InfoAboutHero & info)
|
||||
{
|
||||
|
@ -954,7 +954,7 @@ void CGameState::initGlobalBonuses()
|
||||
for(const auto & b : baseBonuses.Struct())
|
||||
{
|
||||
auto bonus = JsonUtils::parseBonus(b.second);
|
||||
bonus->source = Bonus::GLOBAL;//for all
|
||||
bonus->source = BonusSource::GLOBAL;//for all
|
||||
bonus->sid = -1; //there is one global object
|
||||
globalEffects.addNewBonus(bonus);
|
||||
}
|
||||
@ -1282,9 +1282,9 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
|
||||
{
|
||||
for(int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
|
||||
{
|
||||
auto sel = Selector::type()(Bonus::PRIMARY_SKILL)
|
||||
auto sel = Selector::type()(BonusType::PRIMARY_SKILL)
|
||||
.And(Selector::subtype()(g))
|
||||
.And(Selector::sourceType()(Bonus::HERO_BASE_SKILL));
|
||||
.And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL));
|
||||
|
||||
cgh->getBonusLocalFirst(sel)->val = cgh->type->heroClass->primarySkillInitial[g];
|
||||
}
|
||||
@ -1614,7 +1614,7 @@ void CGameState::giveCampaignBonusToHero(CGHeroInstance * hero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto bb = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::CAMPAIGN_BONUS, val, *scenarioOps->campState->currentMap, g);
|
||||
auto bb = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::PRIMARY_SKILL, BonusSource::CAMPAIGN_BONUS, val, *scenarioOps->campState->currentMap, g);
|
||||
hero->addNewBonus(bb);
|
||||
}
|
||||
}
|
||||
@ -2022,7 +2022,7 @@ UpgradeInfo CGameState::fillUpgradeInfo(const CStackInstance &stack) const
|
||||
t = dynamic_cast<const CGTownInstance *>(stack.armyObj);
|
||||
else if(h)
|
||||
{ //hero specialty
|
||||
TConstBonusListPtr lista = h->getBonuses(Selector::typeSubtype(Bonus::SPECIAL_UPGRADE, base->getId()));
|
||||
TConstBonusListPtr lista = h->getBonuses(Selector::typeSubtype(BonusType::SPECIAL_UPGRADE, base->getId()));
|
||||
for(const auto & it : *lista)
|
||||
{
|
||||
auto nid = CreatureID(it->additionalInfo[0]);
|
||||
@ -2590,7 +2590,7 @@ struct statsHLP
|
||||
//Heroes can produce gold as well - skill, specialty or arts
|
||||
for(const auto & h : ps->heroes)
|
||||
{
|
||||
totalIncome += h->valOfBonuses(Selector::typeSubtype(Bonus::GENERATE_RESOURCE, GameResID(EGameResID::GOLD)));
|
||||
totalIncome += h->valOfBonuses(Selector::typeSubtype(BonusType::GENERATE_RESOURCE, GameResID(EGameResID::GOLD)));
|
||||
|
||||
if(!heroOrTown)
|
||||
heroOrTown = h;
|
||||
@ -3089,8 +3089,8 @@ void InfoAboutHero::initFromHero(const CGHeroInstance *h, InfoAboutHero::EInfoLe
|
||||
{
|
||||
//include details about hero
|
||||
details = new Details();
|
||||
details->luck = h->LuckVal();
|
||||
details->morale = h->MoraleVal();
|
||||
details->luck = h->luckVal();
|
||||
details->morale = h->moraleVal();
|
||||
details->mana = h->mana;
|
||||
details->primskills.resize(GameConstants::PRIMARY_SKILLS);
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "CCreatureHandler.h"
|
||||
#include "VCMI_Lib.h"
|
||||
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "CCreatureSet.h"
|
||||
#include "ConstTransitivePtr.h"
|
||||
#include "IGameCallback.h"
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "CSkillHandler.h"
|
||||
#include "mapObjects/CObjectClassesHandler.h"
|
||||
#include "BattleFieldHandler.h"
|
||||
#include "bonuses/Limiters.h"
|
||||
#include "bonuses/Updaters.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -534,14 +536,14 @@ static std::vector<std::shared_ptr<Bonus>> createCreatureSpecialty(CreatureID ba
|
||||
{
|
||||
std::shared_ptr<Bonus> bonus = std::make_shared<Bonus>();
|
||||
bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, false));
|
||||
bonus->type = Bonus::STACKS_SPEED;
|
||||
bonus->type = BonusType::STACKS_SPEED;
|
||||
bonus->val = 1;
|
||||
result.push_back(bonus);
|
||||
}
|
||||
|
||||
{
|
||||
std::shared_ptr<Bonus> bonus = std::make_shared<Bonus>();
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->type = BonusType::PRIMARY_SKILL;
|
||||
bonus->subtype = PrimarySkill::ATTACK;
|
||||
bonus->val = 0;
|
||||
bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, false));
|
||||
@ -551,7 +553,7 @@ static std::vector<std::shared_ptr<Bonus>> createCreatureSpecialty(CreatureID ba
|
||||
|
||||
{
|
||||
std::shared_ptr<Bonus> bonus = std::make_shared<Bonus>();
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->type = BonusType::PRIMARY_SKILL;
|
||||
bonus->subtype = PrimarySkill::DEFENSE;
|
||||
bonus->val = 0;
|
||||
bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, false));
|
||||
@ -598,8 +600,8 @@ void CHeroHandler::loadHeroSpecialty(CHero * hero, const JsonNode & node)
|
||||
{
|
||||
auto prepSpec = [=](std::shared_ptr<Bonus> bonus)
|
||||
{
|
||||
bonus->duration = Bonus::PERMANENT;
|
||||
bonus->source = Bonus::HERO_SPECIAL;
|
||||
bonus->duration = BonusDuration::PERMANENT;
|
||||
bonus->source = BonusSource::HERO_SPECIAL;
|
||||
bonus->sid = hero->getIndex();
|
||||
return bonus;
|
||||
};
|
||||
|
@ -16,7 +16,8 @@
|
||||
|
||||
#include "../lib/ConstTransitivePtr.h"
|
||||
#include "GameConstants.h"
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "bonuses/BonusList.h"
|
||||
#include "IHandlerBase.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
@ -963,7 +963,7 @@ bool CPathfinderHelper::addTeleportOneWayRandom(const CGTeleport * obj) const
|
||||
|
||||
bool CPathfinderHelper::addTeleportWhirlpool(const CGWhirlpool * obj) const
|
||||
{
|
||||
return options.useTeleportWhirlpool && hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION) && obj;
|
||||
return options.useTeleportWhirlpool && hasBonusOfType(BonusType::WHIRLPOOL_PROTECTION) && obj;
|
||||
}
|
||||
|
||||
int CPathfinderHelper::movementPointsAfterEmbark(int movement, int basicCost, bool disembark) const
|
||||
@ -992,15 +992,15 @@ TurnInfo::BonusCache::BonusCache(const TConstBonusListPtr & bl)
|
||||
for(const auto & terrain : VLC->terrainTypeHandler->objects)
|
||||
{
|
||||
noTerrainPenalty.push_back(static_cast<bool>(
|
||||
bl->getFirst(Selector::type()(Bonus::NO_TERRAIN_PENALTY).And(Selector::subtype()(terrain->getIndex())))));
|
||||
bl->getFirst(Selector::type()(BonusType::NO_TERRAIN_PENALTY).And(Selector::subtype()(terrain->getIndex())))));
|
||||
}
|
||||
|
||||
freeShipBoarding = static_cast<bool>(bl->getFirst(Selector::type()(Bonus::FREE_SHIP_BOARDING)));
|
||||
flyingMovement = static_cast<bool>(bl->getFirst(Selector::type()(Bonus::FLYING_MOVEMENT)));
|
||||
flyingMovementVal = bl->valOfBonuses(Selector::type()(Bonus::FLYING_MOVEMENT));
|
||||
waterWalking = static_cast<bool>(bl->getFirst(Selector::type()(Bonus::WATER_WALKING)));
|
||||
waterWalkingVal = bl->valOfBonuses(Selector::type()(Bonus::WATER_WALKING));
|
||||
pathfindingVal = bl->valOfBonuses(Selector::type()(Bonus::ROUGH_TERRAIN_DISCOUNT));
|
||||
freeShipBoarding = static_cast<bool>(bl->getFirst(Selector::type()(BonusType::FREE_SHIP_BOARDING)));
|
||||
flyingMovement = static_cast<bool>(bl->getFirst(Selector::type()(BonusType::FLYING_MOVEMENT)));
|
||||
flyingMovementVal = bl->valOfBonuses(Selector::type()(BonusType::FLYING_MOVEMENT));
|
||||
waterWalking = static_cast<bool>(bl->getFirst(Selector::type()(BonusType::WATER_WALKING)));
|
||||
waterWalkingVal = bl->valOfBonuses(Selector::type()(BonusType::WATER_WALKING));
|
||||
pathfindingVal = bl->valOfBonuses(Selector::type()(BonusType::ROUGH_TERRAIN_DISCOUNT));
|
||||
}
|
||||
|
||||
TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn):
|
||||
@ -1022,7 +1022,7 @@ bool TurnInfo::isLayerAvailable(const EPathfindingLayer & layer) const
|
||||
if(hero && hero->boat && hero->boat->layer == EPathfindingLayer::AIR)
|
||||
break;
|
||||
|
||||
if(!hasBonusOfType(Bonus::FLYING_MOVEMENT))
|
||||
if(!hasBonusOfType(BonusType::FLYING_MOVEMENT))
|
||||
return false;
|
||||
|
||||
break;
|
||||
@ -1031,7 +1031,7 @@ bool TurnInfo::isLayerAvailable(const EPathfindingLayer & layer) const
|
||||
if(hero && hero->boat && hero->boat->layer == EPathfindingLayer::WATER)
|
||||
break;
|
||||
|
||||
if(!hasBonusOfType(Bonus::WATER_WALKING))
|
||||
if(!hasBonusOfType(BonusType::WATER_WALKING))
|
||||
return false;
|
||||
|
||||
break;
|
||||
@ -1040,17 +1040,17 @@ bool TurnInfo::isLayerAvailable(const EPathfindingLayer & layer) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TurnInfo::hasBonusOfType(Bonus::BonusType type, int subtype) const
|
||||
bool TurnInfo::hasBonusOfType(BonusType type, int subtype) const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case Bonus::FREE_SHIP_BOARDING:
|
||||
case BonusType::FREE_SHIP_BOARDING:
|
||||
return bonusCache->freeShipBoarding;
|
||||
case Bonus::FLYING_MOVEMENT:
|
||||
case BonusType::FLYING_MOVEMENT:
|
||||
return bonusCache->flyingMovement;
|
||||
case Bonus::WATER_WALKING:
|
||||
case BonusType::WATER_WALKING:
|
||||
return bonusCache->waterWalking;
|
||||
case Bonus::NO_TERRAIN_PENALTY:
|
||||
case BonusType::NO_TERRAIN_PENALTY:
|
||||
return bonusCache->noTerrainPenalty[subtype];
|
||||
}
|
||||
|
||||
@ -1058,15 +1058,15 @@ bool TurnInfo::hasBonusOfType(Bonus::BonusType type, int subtype) const
|
||||
bonuses->getFirst(Selector::type()(type).And(Selector::subtype()(subtype))));
|
||||
}
|
||||
|
||||
int TurnInfo::valOfBonuses(Bonus::BonusType type, int subtype) const
|
||||
int TurnInfo::valOfBonuses(BonusType type, int subtype) const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case Bonus::FLYING_MOVEMENT:
|
||||
case BonusType::FLYING_MOVEMENT:
|
||||
return bonusCache->flyingMovementVal;
|
||||
case Bonus::WATER_WALKING:
|
||||
case BonusType::WATER_WALKING:
|
||||
return bonusCache->waterWalkingVal;
|
||||
case Bonus::ROUGH_TERRAIN_DISCOUNT:
|
||||
case BonusType::ROUGH_TERRAIN_DISCOUNT:
|
||||
return bonusCache->pathfindingVal;
|
||||
}
|
||||
|
||||
@ -1083,23 +1083,23 @@ int TurnInfo::getMaxMovePoints(const EPathfindingLayer & layer) const
|
||||
return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
|
||||
}
|
||||
|
||||
void TurnInfo::updateHeroBonuses(Bonus::BonusType type, const CSelector& sel) const
|
||||
void TurnInfo::updateHeroBonuses(BonusType type, const CSelector& sel) const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case Bonus::FREE_SHIP_BOARDING:
|
||||
bonusCache->freeShipBoarding = static_cast<bool>(bonuses->getFirst(Selector::type()(Bonus::FREE_SHIP_BOARDING)));
|
||||
case BonusType::FREE_SHIP_BOARDING:
|
||||
bonusCache->freeShipBoarding = static_cast<bool>(bonuses->getFirst(Selector::type()(BonusType::FREE_SHIP_BOARDING)));
|
||||
break;
|
||||
case Bonus::FLYING_MOVEMENT:
|
||||
bonusCache->flyingMovement = static_cast<bool>(bonuses->getFirst(Selector::type()(Bonus::FLYING_MOVEMENT)));
|
||||
bonusCache->flyingMovementVal = bonuses->valOfBonuses(Selector::type()(Bonus::FLYING_MOVEMENT));
|
||||
case BonusType::FLYING_MOVEMENT:
|
||||
bonusCache->flyingMovement = static_cast<bool>(bonuses->getFirst(Selector::type()(BonusType::FLYING_MOVEMENT)));
|
||||
bonusCache->flyingMovementVal = bonuses->valOfBonuses(Selector::type()(BonusType::FLYING_MOVEMENT));
|
||||
break;
|
||||
case Bonus::WATER_WALKING:
|
||||
bonusCache->waterWalking = static_cast<bool>(bonuses->getFirst(Selector::type()(Bonus::WATER_WALKING)));
|
||||
bonusCache->waterWalkingVal = bonuses->valOfBonuses(Selector::type()(Bonus::WATER_WALKING));
|
||||
case BonusType::WATER_WALKING:
|
||||
bonusCache->waterWalking = static_cast<bool>(bonuses->getFirst(Selector::type()(BonusType::WATER_WALKING)));
|
||||
bonusCache->waterWalkingVal = bonuses->valOfBonuses(Selector::type()(BonusType::WATER_WALKING));
|
||||
break;
|
||||
case Bonus::ROUGH_TERRAIN_DISCOUNT:
|
||||
bonusCache->pathfindingVal = bonuses->valOfBonuses(Selector::type()(Bonus::ROUGH_TERRAIN_DISCOUNT));
|
||||
case BonusType::ROUGH_TERRAIN_DISCOUNT:
|
||||
bonusCache->pathfindingVal = bonuses->valOfBonuses(Selector::type()(BonusType::ROUGH_TERRAIN_DISCOUNT));
|
||||
break;
|
||||
default:
|
||||
bonuses = hero->getAllBonuses(Selector::days(turn), Selector::all, nullptr, "");
|
||||
@ -1162,7 +1162,7 @@ const TurnInfo * CPathfinderHelper::getTurnInfo() const
|
||||
return turnsInfo[turn];
|
||||
}
|
||||
|
||||
bool CPathfinderHelper::hasBonusOfType(const Bonus::BonusType type, const int subtype) const
|
||||
bool CPathfinderHelper::hasBonusOfType(const BonusType type, const int subtype) const
|
||||
{
|
||||
return turnsInfo[turn]->hasBonusOfType(type, subtype);
|
||||
}
|
||||
@ -1248,11 +1248,11 @@ int CPathfinderHelper::getMovementCost(
|
||||
|
||||
bool isWaterLayer;
|
||||
if(indeterminate(isDstWaterLayer))
|
||||
isWaterLayer = ((hero->boat && hero->boat->layer == EPathfindingLayer::WATER) || ti->hasBonusOfType(Bonus::WATER_WALKING)) && dt->terType->isWater();
|
||||
isWaterLayer = ((hero->boat && hero->boat->layer == EPathfindingLayer::WATER) || ti->hasBonusOfType(BonusType::WATER_WALKING)) && dt->terType->isWater();
|
||||
else
|
||||
isWaterLayer = static_cast<bool>(isDstWaterLayer);
|
||||
|
||||
bool isAirLayer = (hero->boat && hero->boat->layer == EPathfindingLayer::AIR) || ti->hasBonusOfType(Bonus::FLYING_MOVEMENT);
|
||||
bool isAirLayer = (hero->boat && hero->boat->layer == EPathfindingLayer::AIR) || ti->hasBonusOfType(BonusType::FLYING_MOVEMENT);
|
||||
|
||||
int ret = hero->getTileCost(*dt, *ct, ti);
|
||||
if(isSailLayer)
|
||||
@ -1261,9 +1261,9 @@ int CPathfinderHelper::getMovementCost(
|
||||
ret = static_cast<int>(ret * 2.0 / 3);
|
||||
}
|
||||
else if(isAirLayer)
|
||||
vstd::amin(ret, GameConstants::BASE_MOVEMENT_COST + ti->valOfBonuses(Bonus::FLYING_MOVEMENT));
|
||||
else if(isWaterLayer && ti->hasBonusOfType(Bonus::WATER_WALKING))
|
||||
ret = static_cast<int>(ret * (100.0 + ti->valOfBonuses(Bonus::WATER_WALKING)) / 100.0);
|
||||
vstd::amin(ret, GameConstants::BASE_MOVEMENT_COST + ti->valOfBonuses(BonusType::FLYING_MOVEMENT));
|
||||
else if(isWaterLayer && ti->hasBonusOfType(BonusType::WATER_WALKING))
|
||||
ret = static_cast<int>(ret * (100.0 + ti->valOfBonuses(BonusType::WATER_WALKING)) / 100.0);
|
||||
|
||||
if(src.x != dst.x && src.y != dst.y) //it's diagonal move
|
||||
{
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
#include "VCMI_Lib.h"
|
||||
#include "IGameCallback.h"
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "int3.h"
|
||||
|
||||
#include <boost/heap/fibonacci_heap.hpp>
|
||||
@ -530,9 +530,9 @@ struct DLL_LINKAGE TurnInfo
|
||||
|
||||
TurnInfo(const CGHeroInstance * Hero, const int Turn = 0);
|
||||
bool isLayerAvailable(const EPathfindingLayer & layer) const;
|
||||
bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
|
||||
int valOfBonuses(const Bonus::BonusType type, const int subtype = -1) const;
|
||||
void updateHeroBonuses(Bonus::BonusType type, const CSelector& sel) const;
|
||||
bool hasBonusOfType(const BonusType type, const int subtype = -1) const;
|
||||
int valOfBonuses(const BonusType type, const int subtype = -1) const;
|
||||
void updateHeroBonuses(BonusType type, const CSelector& sel) const;
|
||||
int getMaxMovePoints(const EPathfindingLayer & layer) const;
|
||||
};
|
||||
|
||||
@ -561,7 +561,7 @@ public:
|
||||
void updateTurnInfo(const int turn = 0);
|
||||
bool isLayerAvailable(const EPathfindingLayer & layer) const;
|
||||
const TurnInfo * getTurnInfo() const;
|
||||
bool hasBonusOfType(const Bonus::BonusType type, const int subtype = -1) const;
|
||||
bool hasBonusOfType(const BonusType type, const int subtype = -1) const;
|
||||
int getMaxMovePoints(const EPathfindingLayer & layer) const;
|
||||
|
||||
std::vector<int3> getCastleGates(const PathNodeInfo & source) const;
|
||||
|
@ -12,7 +12,8 @@
|
||||
#include <vcmi/Player.h>
|
||||
#include <vcmi/Team.h>
|
||||
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "bonuses/CBonusSystemNode.h"
|
||||
#include "ResourceSet.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
@ -90,9 +90,9 @@ SecondarySkill CSkill::getId() const
|
||||
|
||||
void CSkill::addNewBonus(const std::shared_ptr<Bonus> & b, int level)
|
||||
{
|
||||
b->source = Bonus::SECONDARY_SKILL;
|
||||
b->source = BonusSource::SECONDARY_SKILL;
|
||||
b->sid = id;
|
||||
b->duration = Bonus::PERMANENT;
|
||||
b->duration = BonusDuration::PERMANENT;
|
||||
b->description = getNameTranslated();
|
||||
levels[level-1].effects.push_back(b);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <vcmi/Skill.h>
|
||||
#include <vcmi/SkillService.h>
|
||||
|
||||
#include "../lib/HeroBonus.h"
|
||||
#include "../lib/bonuses/Bonus.h"
|
||||
#include "GameConstants.h"
|
||||
#include "IHandlerBase.h"
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include <vstd/RNG.h>
|
||||
|
||||
#include <vcmi/Entity.h>
|
||||
#include <vcmi/ServerCallback.h>
|
||||
|
||||
#include "CGeneralTextHandler.h"
|
||||
@ -88,14 +89,14 @@ ui32 CStack::level() const
|
||||
|
||||
si32 CStack::magicResistance() const
|
||||
{
|
||||
auto magicResistance = IBonusBearer::magicResistance();
|
||||
auto magicResistance = AFactionMember::magicResistance();
|
||||
|
||||
si32 auraBonus = 0;
|
||||
|
||||
for(const auto * one : battle->battleAdjacentUnits(this))
|
||||
{
|
||||
if(one->unitOwner() == owner)
|
||||
vstd::amax(auraBonus, one->valOfBonuses(Bonus::SPELL_RESISTANCE_AURA)); //max value
|
||||
vstd::amax(auraBonus, one->valOfBonuses(BonusType::SPELL_RESISTANCE_AURA)); //max value
|
||||
}
|
||||
vstd::abetween(auraBonus, 0, 100);
|
||||
vstd::abetween(magicResistance, 0, 100);
|
||||
@ -124,11 +125,11 @@ std::vector<si32> CStack::activeSpells() const
|
||||
std::vector<si32> ret;
|
||||
|
||||
std::stringstream cachingStr;
|
||||
cachingStr << "!type_" << Bonus::NONE << "source_" << Bonus::SPELL_EFFECT;
|
||||
CSelector selector = Selector::sourceType()(Bonus::SPELL_EFFECT)
|
||||
cachingStr << "!type_" << vstd::to_underlying(BonusType::NONE) << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT);
|
||||
CSelector selector = Selector::sourceType()(BonusSource::SPELL_EFFECT)
|
||||
.And(CSelector([](const Bonus * b)->bool
|
||||
{
|
||||
return b->type != Bonus::NONE && SpellID(b->sid).toSpell() && !SpellID(b->sid).toSpell()->isAdventure();
|
||||
return b->type != BonusType::NONE && SpellID(b->sid).toSpell() && !SpellID(b->sid).toSpell()->isAdventure();
|
||||
}));
|
||||
|
||||
TConstBonusListPtr spellEffects = getBonuses(selector, Selector::all, cachingStr.str());
|
||||
@ -197,7 +198,7 @@ void CStack::prepareAttacked(BattleStackAttacked & bsa, vstd::RNG & rand, const
|
||||
{
|
||||
bsa.flags |= BattleStackAttacked::KILLED;
|
||||
|
||||
auto resurrectValue = customState->valOfBonuses(Bonus::REBIRTH);
|
||||
auto resurrectValue = customState->valOfBonuses(BonusType::REBIRTH);
|
||||
|
||||
if(resurrectValue > 0 && customState->canCast()) //there must be casts left
|
||||
{
|
||||
@ -219,7 +220,7 @@ void CStack::prepareAttacked(BattleStackAttacked & bsa, vstd::RNG & rand, const
|
||||
resurrectedCount += 1;
|
||||
}
|
||||
|
||||
if(customState->hasBonusOfType(Bonus::REBIRTH, 1))
|
||||
if(customState->hasBonusOfType(BonusType::REBIRTH, 1))
|
||||
{
|
||||
// resurrect at least one Sacred Phoenix
|
||||
vstd::amax(resurrectedCount, 1);
|
||||
@ -229,7 +230,7 @@ void CStack::prepareAttacked(BattleStackAttacked & bsa, vstd::RNG & rand, const
|
||||
{
|
||||
customState->casts.use();
|
||||
bsa.flags |= BattleStackAttacked::REBIRTH;
|
||||
int64_t toHeal = customState->MaxHealth() * resurrectedCount;
|
||||
int64_t toHeal = customState->getMaxHealth() * resurrectedCount;
|
||||
//TODO: add one-battle rebirth?
|
||||
customState->heal(toHeal, EHealLevel::RESURRECT, EHealPower::PERMANENT);
|
||||
customState->counterAttacks.use(customState->counterAttacks.available());
|
||||
@ -307,7 +308,7 @@ std::string CStack::getName() const
|
||||
|
||||
bool CStack::canBeHealed() const
|
||||
{
|
||||
return getFirstHPleft() < static_cast<int32_t>(MaxHealth()) && isValidTarget() && !hasBonusOfType(Bonus::SIEGE_WEAPON);
|
||||
return getFirstHPleft() < static_cast<int32_t>(getMaxHealth()) && isValidTarget() && !hasBonusOfType(BonusType::SIEGE_WEAPON);
|
||||
}
|
||||
|
||||
bool CStack::isOnNativeTerrain() const
|
||||
|
@ -10,7 +10,8 @@
|
||||
|
||||
#pragma once
|
||||
#include "JsonNode.h"
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "bonuses/CBonusSystemNode.h"
|
||||
#include "CCreatureHandler.h" //todo: remove
|
||||
#include "battle/BattleHex.h"
|
||||
#include "mapObjects/CGHeroInstance.h" // for commander serialization
|
||||
|
@ -23,7 +23,8 @@
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "mapObjects/CObjectClassesHandler.h"
|
||||
#include "mapObjects/CObjectHandler.h"
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "bonuses/Propagators.h"
|
||||
#include "ResourceSet.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
@ -497,29 +498,29 @@ void CTownHandler::addBonusesForVanilaBuilding(CBuilding * building) const
|
||||
|
||||
if(building->bid == BuildingID::TAVERN)
|
||||
{
|
||||
b = createBonus(building, Bonus::MORALE, +1);
|
||||
b = createBonus(building, BonusType::MORALE, +1);
|
||||
}
|
||||
|
||||
switch(building->subId)
|
||||
{
|
||||
case BuildingSubID::BROTHERHOOD_OF_SWORD:
|
||||
b = createBonus(building, Bonus::MORALE, +2);
|
||||
b = createBonus(building, BonusType::MORALE, +2);
|
||||
building->overrideBids.insert(BuildingID::TAVERN);
|
||||
break;
|
||||
case BuildingSubID::FOUNTAIN_OF_FORTUNE:
|
||||
b = createBonus(building, Bonus::LUCK, +2);
|
||||
b = createBonus(building, BonusType::LUCK, +2);
|
||||
break;
|
||||
case BuildingSubID::SPELL_POWER_GARRISON_BONUS:
|
||||
b = createBonus(building, Bonus::PRIMARY_SKILL, +2, PrimarySkill::SPELL_POWER);
|
||||
b = createBonus(building, BonusType::PRIMARY_SKILL, +2, PrimarySkill::SPELL_POWER);
|
||||
break;
|
||||
case BuildingSubID::ATTACK_GARRISON_BONUS:
|
||||
b = createBonus(building, Bonus::PRIMARY_SKILL, +2, PrimarySkill::ATTACK);
|
||||
b = createBonus(building, BonusType::PRIMARY_SKILL, +2, PrimarySkill::ATTACK);
|
||||
break;
|
||||
case BuildingSubID::DEFENSE_GARRISON_BONUS:
|
||||
b = createBonus(building, Bonus::PRIMARY_SKILL, +2, PrimarySkill::DEFENSE);
|
||||
b = createBonus(building, BonusType::PRIMARY_SKILL, +2, PrimarySkill::DEFENSE);
|
||||
break;
|
||||
case BuildingSubID::LIGHTHOUSE:
|
||||
b = createBonus(building, Bonus::MOVEMENT, +500, playerPropagator, 0);
|
||||
b = createBonus(building, BonusType::MOVEMENT, +500, playerPropagator, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -527,12 +528,12 @@ void CTownHandler::addBonusesForVanilaBuilding(CBuilding * building) const
|
||||
building->addNewBonus(b, building->buildingBonuses);
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, Bonus::BonusType type, int val, int subtype) const
|
||||
std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, BonusType type, int val, int subtype) const
|
||||
{
|
||||
return createBonus(build, type, val, emptyPropagator(), subtype);
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype) const
|
||||
std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, BonusType type, int val, TPropagatorPtr & prop, int subtype) const
|
||||
{
|
||||
std::ostringstream descr;
|
||||
descr << build->getNameTranslated();
|
||||
@ -540,13 +541,13 @@ std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, Bonus::Bonus
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> CTownHandler::createBonusImpl(const BuildingID & building,
|
||||
Bonus::BonusType type,
|
||||
BonusType type,
|
||||
int val,
|
||||
TPropagatorPtr & prop,
|
||||
const std::string & description,
|
||||
int subtype) const
|
||||
{
|
||||
auto b = std::make_shared<Bonus>(Bonus::PERMANENT, type, Bonus::TOWN_STRUCTURE, val, building, description, subtype);
|
||||
auto b = std::make_shared<Bonus>(BonusDuration::PERMANENT, type, BonusSource::TOWN_STRUCTURE, val, building, description, subtype);
|
||||
|
||||
if(prop)
|
||||
b->addPropagator(prop);
|
||||
|
@ -19,7 +19,8 @@
|
||||
#include "IHandlerBase.h"
|
||||
#include "LogicalExpression.h"
|
||||
#include "battle/BattleHex.h"
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "bonuses/BonusList.h"
|
||||
#include "Point.h"
|
||||
#include "mapObjects/CRewardableConstructor.h"
|
||||
|
||||
@ -379,10 +380,10 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase<FactionID, Faction, CFactio
|
||||
void loadBuilding(CTown * town, const std::string & stringID, const JsonNode & source);
|
||||
void loadBuildings(CTown * town, const JsonNode & source);
|
||||
|
||||
std::shared_ptr<Bonus> createBonus(CBuilding * build, Bonus::BonusType type, int val, int subtype = -1) const;
|
||||
std::shared_ptr<Bonus> createBonus(CBuilding * build, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype = -1) const;
|
||||
std::shared_ptr<Bonus> createBonus(CBuilding * build, BonusType type, int val, int subtype = -1) const;
|
||||
std::shared_ptr<Bonus> createBonus(CBuilding * build, BonusType type, int val, TPropagatorPtr & prop, int subtype = -1) const;
|
||||
std::shared_ptr<Bonus> createBonusImpl(const BuildingID & building,
|
||||
Bonus::BonusType type,
|
||||
BonusType type,
|
||||
int val,
|
||||
TPropagatorPtr & prop,
|
||||
const std::string & description,
|
||||
|
@ -398,7 +398,7 @@ class TeleportChannelID : public BaseForID<TeleportChannelID, si32>
|
||||
// Enum declarations
|
||||
namespace PrimarySkill
|
||||
{
|
||||
enum PrimarySkill { NONE = -1, ATTACK, DEFENSE, SPELL_POWER, KNOWLEDGE,
|
||||
enum PrimarySkill : int8_t { NONE = -1, ATTACK, DEFENSE, SPELL_POWER, KNOWLEDGE,
|
||||
EXPERIENCE = 4}; //for some reason changePrimSkill uses it
|
||||
}
|
||||
|
||||
@ -1367,7 +1367,6 @@ enum class EHealPower : ui8
|
||||
|
||||
// Typedef declarations
|
||||
using TExpType = si64;
|
||||
using TBonusSubtype = si32;
|
||||
using TQuantity = si32;
|
||||
|
||||
using TRmgTemplateZoneId = int;
|
||||
|
2794
lib/HeroBonus.cpp
2794
lib/HeroBonus.cpp
File diff suppressed because it is too large
Load Diff
1370
lib/HeroBonus.h
1370
lib/HeroBonus.h
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,10 @@
|
||||
#include "CModHandler.h"
|
||||
#include "BattleFieldHandler.h"
|
||||
#include "ObstacleHandler.h"
|
||||
#include "bonuses/CBonusSystemNode.h"
|
||||
#include "bonuses/Limiters.h"
|
||||
#include "bonuses/Propagators.h"
|
||||
#include "bonuses/Updaters.h"
|
||||
|
||||
#include "serializer/CSerializer.h" // for SAVEGAME_MAGIC
|
||||
#include "serializer/BinaryDeserializer.h"
|
||||
|
@ -13,7 +13,11 @@
|
||||
|
||||
#include "ScopeGuard.h"
|
||||
|
||||
#include "HeroBonus.h"
|
||||
#include "bonuses/BonusParams.h"
|
||||
#include "bonuses/Bonus.h"
|
||||
#include "bonuses/Limiters.h"
|
||||
#include "bonuses/Propagators.h"
|
||||
#include "bonuses/Updaters.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "VCMI_Lib.h" //for identifier resolution
|
||||
#include "CModHandler.h"
|
||||
@ -481,7 +485,7 @@ void JsonUtils::parseTypedBonusShort(const JsonVector & source, const std::share
|
||||
dest->val = static_cast<si32>(source[1].Float());
|
||||
resolveIdentifier(source[2],dest->subtype);
|
||||
dest->additionalInfo = static_cast<si32>(source[3].Float());
|
||||
dest->duration = Bonus::PERMANENT; //TODO: handle flags (as integer)
|
||||
dest->duration = BonusDuration::PERMANENT; //TODO: handle flags (as integer)
|
||||
dest->turnsRemain = 0;
|
||||
}
|
||||
|
||||
@ -817,11 +821,11 @@ std::shared_ptr<Bonus> JsonUtils::parseBonus(const JsonNode &ability)
|
||||
|
||||
std::shared_ptr<Bonus> JsonUtils::parseBuildingBonus(const JsonNode & ability, const BuildingID & building, const std::string & description)
|
||||
{
|
||||
/* duration = Bonus::PERMANENT
|
||||
source = Bonus::TOWN_STRUCTURE
|
||||
/* duration = BonusDuration::PERMANENT
|
||||
source = BonusSource::TOWN_STRUCTURE
|
||||
bonusType, val, subtype - get from json
|
||||
*/
|
||||
auto b = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::NONE, Bonus::TOWN_STRUCTURE, 0, building, description, -1);
|
||||
auto b = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::NONE, BonusSource::TOWN_STRUCTURE, 0, building, description, -1);
|
||||
|
||||
if(!parseBonus(ability, b.get()))
|
||||
return nullptr;
|
||||
@ -842,14 +846,14 @@ static BonusParams convertDeprecatedBonus(const JsonNode &ability)
|
||||
params.val = static_cast<si32>(ability["val"].Float());
|
||||
params.valRelevant = true;
|
||||
}
|
||||
Bonus::ValueType valueType = Bonus::ADDITIVE_VALUE;
|
||||
BonusValueType valueType = BonusValueType::ADDITIVE_VALUE;
|
||||
if(!ability["valueType"].isNull())
|
||||
valueType = bonusValueMap.find(ability["valueType"].String())->second;
|
||||
|
||||
if(ability["type"].String() == "SECONDARY_SKILL_PREMY" && valueType == Bonus::PERCENT_TO_BASE) //assume secondary skill special
|
||||
if(ability["type"].String() == "SECONDARY_SKILL_PREMY" && valueType == BonusValueType::PERCENT_TO_BASE) //assume secondary skill special
|
||||
{
|
||||
params.valueType = Bonus::PERCENT_TO_TARGET_TYPE;
|
||||
params.targetType = Bonus::SECONDARY_SKILL;
|
||||
params.valueType = BonusValueType::PERCENT_TO_TARGET_TYPE;
|
||||
params.targetType = BonusSource::SECONDARY_SKILL;
|
||||
params.targetTypeRelevant = true;
|
||||
}
|
||||
|
||||
@ -942,7 +946,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
|
||||
value = &ability["valueType"];
|
||||
if (!value->isNull())
|
||||
b->valType = static_cast<Bonus::ValueType>(parseByMapN(bonusValueMap, value, "value type "));
|
||||
b->valType = static_cast<BonusValueType>(parseByMapN(bonusValueMap, value, "value type "));
|
||||
}
|
||||
|
||||
b->stacking = ability["stacking"].String();
|
||||
@ -963,7 +967,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
|
||||
value = &ability["effectRange"];
|
||||
if (!value->isNull())
|
||||
b->effectRange = static_cast<Bonus::LimitEffect>(parseByMapN(bonusLimitEffect, value, "effect range "));
|
||||
b->effectRange = static_cast<BonusLimitEffect>(parseByMapN(bonusLimitEffect, value, "effect range "));
|
||||
|
||||
value = &ability["duration"];
|
||||
if (!value->isNull())
|
||||
@ -971,17 +975,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
switch (value->getType())
|
||||
{
|
||||
case JsonNode::JsonType::DATA_STRING:
|
||||
b->duration = static_cast<Bonus::BonusDuration>(parseByMap(bonusDurationMap, value, "duration type "));
|
||||
break;
|
||||
case JsonNode::JsonType::DATA_VECTOR:
|
||||
{
|
||||
ui16 dur = 0;
|
||||
for (const JsonNode & d : value->Vector())
|
||||
{
|
||||
dur |= parseByMapN(bonusDurationMap, &d, "duration type ");
|
||||
}
|
||||
b->duration = static_cast<Bonus::BonusDuration>(dur);
|
||||
}
|
||||
b->duration = static_cast<BonusDuration>(parseByMap(bonusDurationMap, value, "duration type "));
|
||||
break;
|
||||
default:
|
||||
logMod->error("Error! Wrong bonus duration format.");
|
||||
@ -990,11 +984,11 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
|
||||
value = &ability["sourceType"];
|
||||
if (!value->isNull())
|
||||
b->source = static_cast<Bonus::BonusSource>(parseByMap(bonusSourceMap, value, "source type "));
|
||||
b->source = static_cast<BonusSource>(parseByMap(bonusSourceMap, value, "source type "));
|
||||
|
||||
value = &ability["targetSourceType"];
|
||||
if (!value->isNull())
|
||||
b->targetSourceType = static_cast<Bonus::BonusSource>(parseByMap(bonusSourceMap, value, "target type "));
|
||||
b->targetSourceType = static_cast<BonusSource>(parseByMap(bonusSourceMap, value, "target type "));
|
||||
|
||||
value = &ability["limiters"];
|
||||
if (!value->isNull())
|
||||
@ -1071,7 +1065,7 @@ CSelector JsonUtils::parseSelector(const JsonNode & ability)
|
||||
ret = ret.And(Selector::subtype()(subtype));
|
||||
}
|
||||
value = &ability["sourceType"];
|
||||
Bonus::BonusSource src = Bonus::OTHER; //Fixes for GCC false maybe-uninitialized
|
||||
BonusSource src = BonusSource::OTHER; //Fixes for GCC false maybe-uninitialized
|
||||
si32 id = 0;
|
||||
auto sourceIDRelevant = false;
|
||||
auto sourceTypeRelevant = false;
|
||||
|
@ -982,13 +982,13 @@ void GiveBonus::applyGs(CGameState *gs)
|
||||
|
||||
std::string &descr = b->description;
|
||||
|
||||
if(bdescr.message.empty() && (bonus.type == Bonus::LUCK || bonus.type == Bonus::MORALE))
|
||||
if(bdescr.message.empty() && (bonus.type == BonusType::LUCK || bonus.type == BonusType::MORALE))
|
||||
{
|
||||
if (bonus.source == Bonus::OBJECT)
|
||||
if (bonus.source == BonusSource::OBJECT)
|
||||
{
|
||||
descr = VLC->generaltexth->arraytxt[bonus.val > 0 ? 110 : 109]; //+/-%d Temporary until next battle"
|
||||
}
|
||||
else if(bonus.source == Bonus::TOWN_STRUCTURE)
|
||||
else if(bonus.source == BonusSource::TOWN_STRUCTURE)
|
||||
{
|
||||
descr = bonus.description;
|
||||
return;
|
||||
@ -1124,7 +1124,7 @@ void RemoveBonus::applyGs(CGameState *gs)
|
||||
|
||||
for(const auto & b : bonuses)
|
||||
{
|
||||
if(b->source == source && b->sid == id)
|
||||
if(vstd::to_underlying(b->source) == source && b->sid == id)
|
||||
{
|
||||
bonus = *b; //backup bonus (to show to interfaces later)
|
||||
node->removeBonus(b);
|
||||
@ -2155,15 +2155,15 @@ void BattleTriggerEffect::applyGs(CGameState * gs) const
|
||||
{
|
||||
CStack * st = gs->curB->getStack(stackID);
|
||||
assert(st);
|
||||
switch(effect)
|
||||
switch(static_cast<BonusType>(effect))
|
||||
{
|
||||
case Bonus::HP_REGENERATION:
|
||||
case BonusType::HP_REGENERATION:
|
||||
{
|
||||
int64_t toHeal = val;
|
||||
st->heal(toHeal, EHealLevel::HEAL, EHealPower::PERMANENT);
|
||||
break;
|
||||
}
|
||||
case Bonus::MANA_DRAIN:
|
||||
case BonusType::MANA_DRAIN:
|
||||
{
|
||||
CGHeroInstance * h = gs->getHero(ObjectInstanceID(additionalInfo));
|
||||
st->drainedMana = true;
|
||||
@ -2171,18 +2171,18 @@ void BattleTriggerEffect::applyGs(CGameState * gs) const
|
||||
vstd::amax(h->mana, 0);
|
||||
break;
|
||||
}
|
||||
case Bonus::POISON:
|
||||
case BonusType::POISON:
|
||||
{
|
||||
auto b = st->getBonusLocalFirst(Selector::source(Bonus::SPELL_EFFECT, SpellID::POISON)
|
||||
.And(Selector::type()(Bonus::STACK_HEALTH)));
|
||||
auto b = st->getBonusLocalFirst(Selector::source(BonusSource::SPELL_EFFECT, SpellID::POISON)
|
||||
.And(Selector::type()(BonusType::STACK_HEALTH)));
|
||||
if (b)
|
||||
b->val = val;
|
||||
break;
|
||||
}
|
||||
case Bonus::ENCHANTER:
|
||||
case Bonus::MORALE:
|
||||
case BonusType::ENCHANTER:
|
||||
case BonusType::MORALE:
|
||||
break;
|
||||
case Bonus::FEAR:
|
||||
case BonusType::FEAR:
|
||||
st->fear = true;
|
||||
break;
|
||||
default:
|
||||
@ -2477,7 +2477,7 @@ void BattleSetStackProperty::applyGs(CGameState * gs) const
|
||||
}
|
||||
case UNBIND:
|
||||
{
|
||||
stack->removeBonusesRecursive(Selector::type()(Bonus::BIND_EFFECT));
|
||||
stack->removeBonusesRecursive(Selector::type()(BonusType::BIND_EFFECT));
|
||||
break;
|
||||
}
|
||||
case CLONED:
|
||||
|
@ -245,7 +245,7 @@
|
||||
<Unit filename="GameConstants.cpp" />
|
||||
<Unit filename="GameConstants.h" />
|
||||
<Unit filename="HeroBonus.cpp" />
|
||||
<Unit filename="HeroBonus.h" />
|
||||
<Unit filename="Bonus.h" />
|
||||
<Unit filename="IBonusTypeHandler.h" />
|
||||
<Unit filename="IGameCallback.cpp" />
|
||||
<Unit filename="IGameCallback.h" />
|
||||
|
@ -426,7 +426,7 @@
|
||||
<ClInclude Include="logging\CLogger.h" />
|
||||
<ClInclude Include="logging\CBasicLogConfigurator.h" />
|
||||
<ClInclude Include="GameConstants.h" />
|
||||
<ClInclude Include="HeroBonus.h" />
|
||||
<ClInclude Include="Bonus.h" />
|
||||
<ClInclude Include="IGameCallback.h" />
|
||||
<ClInclude Include="CGameInfoCallback.h" />
|
||||
<ClInclude Include="IGameEventsReceiver.h" />
|
||||
|
@ -407,7 +407,7 @@
|
||||
<ClInclude Include="CondSh.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HeroBonus.h">
|
||||
<ClInclude Include="Bonus.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IGameCallback.h">
|
||||
|
@ -52,7 +52,7 @@ BattleAction BattleAction::makeMeleeAttack(const battle::Unit * stack, BattleHex
|
||||
ba.stackNumber = stack->unitId();
|
||||
ba.aimToHex(attackFrom);
|
||||
ba.aimToHex(destination);
|
||||
if(returnAfterAttack && stack->hasBonusOfType(Bonus::RETURN_AFTER_STRIKE))
|
||||
if(returnAfterAttack && stack->hasBonusOfType(BonusType::RETURN_AFTER_STRIKE))
|
||||
ba.aimToHex(stack->getPosition());
|
||||
return ba;
|
||||
}
|
||||
|
@ -9,6 +9,8 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "BattleInfo.h"
|
||||
#include "bonuses/Limiters.h"
|
||||
#include "bonuses/Updaters.h"
|
||||
#include "../CStack.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../NetPacks.h"
|
||||
@ -471,9 +473,9 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
|
||||
//native terrain bonuses
|
||||
static auto nativeTerrain = std::make_shared<CreatureTerrainLimiter>();
|
||||
|
||||
curB->addNewBonus(std::make_shared<Bonus>(Bonus::ONE_BATTLE, Bonus::STACKS_SPEED, Bonus::TERRAIN_NATIVE, 1, 0, 0)->addLimiter(nativeTerrain));
|
||||
curB->addNewBonus(std::make_shared<Bonus>(Bonus::ONE_BATTLE, Bonus::PRIMARY_SKILL, Bonus::TERRAIN_NATIVE, 1, 0, PrimarySkill::ATTACK)->addLimiter(nativeTerrain));
|
||||
curB->addNewBonus(std::make_shared<Bonus>(Bonus::ONE_BATTLE, Bonus::PRIMARY_SKILL, Bonus::TERRAIN_NATIVE, 1, 0, PrimarySkill::DEFENSE)->addLimiter(nativeTerrain));
|
||||
curB->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::STACKS_SPEED, BonusSource::TERRAIN_NATIVE, 1, 0, 0)->addLimiter(nativeTerrain));
|
||||
curB->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, 0, PrimarySkill::ATTACK)->addLimiter(nativeTerrain));
|
||||
curB->addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::PRIMARY_SKILL, BonusSource::TERRAIN_NATIVE, 1, 0, PrimarySkill::DEFENSE)->addLimiter(nativeTerrain));
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//tactics
|
||||
@ -487,8 +489,8 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
|
||||
{
|
||||
if(heroes[i])
|
||||
{
|
||||
battleRepositionHex[i] += heroes[i]->valOfBonuses(Selector::type()(Bonus::BEFORE_BATTLE_REPOSITION));
|
||||
battleRepositionHexBlock[i] += heroes[i]->valOfBonuses(Selector::type()(Bonus::BEFORE_BATTLE_REPOSITION_BLOCK));
|
||||
battleRepositionHex[i] += heroes[i]->valOfBonuses(Selector::type()(BonusType::BEFORE_BATTLE_REPOSITION));
|
||||
battleRepositionHexBlock[i] += heroes[i]->valOfBonuses(Selector::type()(BonusType::BEFORE_BATTLE_REPOSITION_BLOCK));
|
||||
}
|
||||
}
|
||||
int tacticsSkillDiffAttacker = battleRepositionHex[BattleSide::ATTACKER] - battleRepositionHexBlock[BattleSide::DEFENDER];
|
||||
@ -801,7 +803,7 @@ void BattleInfo::setUnitState(uint32_t id, const JsonNode & data, int64_t health
|
||||
auto selector = [](const Bonus * b)
|
||||
{
|
||||
//Special case: DISRUPTING_RAY is absolutely permanent
|
||||
return b->source == Bonus::SPELL_EFFECT && b->sid != SpellID::DISRUPTING_RAY;
|
||||
return b->source == BonusSource::SPELL_EFFECT && b->sid != SpellID::DISRUPTING_RAY;
|
||||
};
|
||||
changedStack->removeBonusesRecursive(selector);
|
||||
}
|
||||
@ -927,7 +929,7 @@ uint32_t BattleInfo::nextUnitId() const
|
||||
|
||||
void BattleInfo::addOrUpdateUnitBonus(CStack * sta, const Bonus & value, bool forceAdd)
|
||||
{
|
||||
if(forceAdd || !sta->hasBonus(Selector::source(Bonus::SPELL_EFFECT, value.sid).And(Selector::typeSubtype(value.type, value.subtype))))
|
||||
if(forceAdd || !sta->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, value.sid).And(Selector::typeSubtype(value.type, value.subtype))))
|
||||
{
|
||||
//no such effect or cumulative - add new
|
||||
logBonus->trace("%s receives a new bonus: %s", sta->nodeName(), value.Description());
|
||||
|
@ -9,7 +9,8 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include "../int3.h"
|
||||
#include "../HeroBonus.h"
|
||||
#include "../bonuses/Bonus.h"
|
||||
#include "../bonuses/CBonusSystemNode.h"
|
||||
#include "CBattleInfoCallback.h"
|
||||
#include "IBattleState.h"
|
||||
#include "SiegeInfo.h"
|
||||
|
@ -126,7 +126,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(con
|
||||
|
||||
if(!hero)
|
||||
return ESpellCastProblem::NO_HERO_TO_CAST_SPELL;
|
||||
if(hero->hasBonusOfType(Bonus::BLOCK_ALL_MAGIC))
|
||||
if(hero->hasBonusOfType(BonusType::BLOCK_ALL_MAGIC))
|
||||
return ESpellCastProblem::MAGIC_IS_BLOCKED;
|
||||
}
|
||||
break;
|
||||
@ -204,7 +204,7 @@ bool CBattleInfoCallback::battleHasWallPenalty(const IBonusBearer * shooter, Bat
|
||||
return false;
|
||||
|
||||
const std::string cachingStrNoWallPenalty = "type_NO_WALL_PENALTY";
|
||||
static const auto selectorNoWallPenalty = Selector::type()(Bonus::NO_WALL_PENALTY);
|
||||
static const auto selectorNoWallPenalty = Selector::type()(BonusType::NO_WALL_PENALTY);
|
||||
|
||||
if(shooter->hasBonus(selectorNoWallPenalty, cachingStrNoWallPenalty))
|
||||
return false;
|
||||
@ -227,7 +227,7 @@ std::vector<PossiblePlayerBattleAction> CBattleInfoCallback::getClientActionsFor
|
||||
{
|
||||
if(stack->canCast()) //TODO: check for battlefield effects that prevent casting?
|
||||
{
|
||||
if(stack->hasBonusOfType(Bonus::SPELLCASTER))
|
||||
if(stack->hasBonusOfType(BonusType::SPELLCASTER))
|
||||
{
|
||||
for(const auto & spellID : data.creatureSpellsToCast)
|
||||
{
|
||||
@ -236,24 +236,24 @@ std::vector<PossiblePlayerBattleAction> CBattleInfoCallback::getClientActionsFor
|
||||
allowedActionList.push_back(act);
|
||||
}
|
||||
}
|
||||
if(stack->hasBonusOfType(Bonus::RANDOM_SPELLCASTER))
|
||||
if(stack->hasBonusOfType(BonusType::RANDOM_SPELLCASTER))
|
||||
allowedActionList.push_back(PossiblePlayerBattleAction::RANDOM_GENIE_SPELL);
|
||||
}
|
||||
if(stack->canShoot())
|
||||
allowedActionList.push_back(PossiblePlayerBattleAction::SHOOT);
|
||||
if(stack->hasBonusOfType(Bonus::RETURN_AFTER_STRIKE))
|
||||
if(stack->hasBonusOfType(BonusType::RETURN_AFTER_STRIKE))
|
||||
allowedActionList.push_back(PossiblePlayerBattleAction::ATTACK_AND_RETURN);
|
||||
|
||||
allowedActionList.push_back(PossiblePlayerBattleAction::ATTACK); //all active stacks can attack
|
||||
allowedActionList.push_back(PossiblePlayerBattleAction::WALK_AND_ATTACK); //not all stacks can always walk, but we will check this elsewhere
|
||||
|
||||
if(stack->canMove() && stack->Speed(0, true)) //probably no reason to try move war machines or bound stacks
|
||||
if(stack->canMove() && stack->speed(0, true)) //probably no reason to try move war machines or bound stacks
|
||||
allowedActionList.push_back(PossiblePlayerBattleAction::MOVE_STACK);
|
||||
|
||||
const auto * siegedTown = battleGetDefendedTown();
|
||||
if(siegedTown && siegedTown->hasFort() && stack->hasBonusOfType(Bonus::CATAPULT)) //TODO: check shots
|
||||
if(siegedTown && siegedTown->hasFort() && stack->hasBonusOfType(BonusType::CATAPULT)) //TODO: check shots
|
||||
allowedActionList.push_back(PossiblePlayerBattleAction::CATAPULT);
|
||||
if(stack->hasBonusOfType(Bonus::HEALER))
|
||||
if(stack->hasBonusOfType(BonusType::HEALER))
|
||||
allowedActionList.push_back(PossiblePlayerBattleAction::HEAL);
|
||||
}
|
||||
|
||||
@ -566,7 +566,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const Reacha
|
||||
if(!unit->getPosition().isValid()) //turrets
|
||||
return ret;
|
||||
|
||||
auto unitSpeed = unit->Speed(0, true);
|
||||
auto unitSpeed = unit->speed(0, true);
|
||||
|
||||
const bool tacticsPhase = battleTacticDist() && battleGetTacticsSide() == unit->unitSide();
|
||||
|
||||
@ -686,10 +686,10 @@ bool CBattleInfoCallback::battleCanShoot(const battle::Unit * attacker) const
|
||||
return false;
|
||||
|
||||
//forgetfulness
|
||||
TConstBonusListPtr forgetfulList = attacker->getBonuses(Selector::type()(Bonus::FORGETFULL));
|
||||
TConstBonusListPtr forgetfulList = attacker->getBonuses(Selector::type()(BonusType::FORGETFULL));
|
||||
if(!forgetfulList->empty())
|
||||
{
|
||||
int forgetful = forgetfulList->valOfBonuses(Selector::type()(Bonus::FORGETFULL));
|
||||
int forgetful = forgetfulList->valOfBonuses(Selector::type()(BonusType::FORGETFULL));
|
||||
|
||||
//advanced+ level
|
||||
if(forgetful > 1)
|
||||
@ -697,7 +697,7 @@ bool CBattleInfoCallback::battleCanShoot(const battle::Unit * attacker) const
|
||||
}
|
||||
|
||||
return attacker->canShoot() && (!battleIsUnitBlocked(attacker)
|
||||
|| attacker->hasBonusOfType(Bonus::FREE_SHOOTING));
|
||||
|| attacker->hasBonusOfType(BonusType::FREE_SHOOTING));
|
||||
}
|
||||
|
||||
bool CBattleInfoCallback::battleCanShoot(const battle::Unit * attacker, BattleHex dest) const
|
||||
@ -712,7 +712,7 @@ bool CBattleInfoCallback::battleCanShoot(const battle::Unit * attacker, BattleHe
|
||||
{
|
||||
if(battleCanShoot(attacker))
|
||||
{
|
||||
auto limitedRangeBonus = attacker->getBonus(Selector::type()(Bonus::LIMITED_SHOOTING_RANGE));
|
||||
auto limitedRangeBonus = attacker->getBonus(Selector::type()(BonusType::LIMITED_SHOOTING_RANGE));
|
||||
if(limitedRangeBonus == nullptr)
|
||||
{
|
||||
return true;
|
||||
@ -1239,11 +1239,11 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(const battle:
|
||||
{
|
||||
attackOriginHex = attacker->occupiedHex(attackOriginHex); //the other hex stack stands on
|
||||
}
|
||||
if(attacker->hasBonusOfType(Bonus::ATTACKS_ALL_ADJACENT))
|
||||
if(attacker->hasBonusOfType(BonusType::ATTACKS_ALL_ADJACENT))
|
||||
{
|
||||
boost::copy(attacker->getSurroundingHexes(attackerPos), vstd::set_inserter(at.hostileCreaturePositions));
|
||||
}
|
||||
if(attacker->hasBonusOfType(Bonus::THREE_HEADED_ATTACK))
|
||||
if(attacker->hasBonusOfType(BonusType::THREE_HEADED_ATTACK))
|
||||
{
|
||||
std::vector<BattleHex> hexes = attacker->getSurroundingHexes(attackerPos);
|
||||
for(BattleHex tile : hexes)
|
||||
@ -1256,7 +1256,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(const battle:
|
||||
}
|
||||
}
|
||||
}
|
||||
if(attacker->hasBonusOfType(Bonus::WIDE_BREATH))
|
||||
if(attacker->hasBonusOfType(BonusType::WIDE_BREATH))
|
||||
{
|
||||
std::vector<BattleHex> hexes = destinationTile.neighbouringTiles();
|
||||
for(int i = 0; i<hexes.size(); i++)
|
||||
@ -1275,7 +1275,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(const battle:
|
||||
at.friendlyCreaturePositions.insert(tile);
|
||||
}
|
||||
}
|
||||
else if(attacker->hasBonusOfType(Bonus::TWO_HEX_ATTACK_BREATH))
|
||||
else if(attacker->hasBonusOfType(BonusType::TWO_HEX_ATTACK_BREATH))
|
||||
{
|
||||
auto direction = BattleHex::mutualPosition(attackOriginHex, destinationTile);
|
||||
if(direction != BattleHex::NONE) //only adjacent hexes are subject of dragon breath calculation
|
||||
@ -1312,7 +1312,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyShootableHexes(const battle::
|
||||
AttackableTiles at;
|
||||
RETURN_IF_NOT_BATTLE(at);
|
||||
|
||||
if(attacker->hasBonusOfType(Bonus::SHOOTS_ALL_ADJACENT) && !vstd::contains(attackerPos.neighbouringTiles(), destinationTile))
|
||||
if(attacker->hasBonusOfType(BonusType::SHOOTS_ALL_ADJACENT) && !vstd::contains(attackerPos.neighbouringTiles(), destinationTile))
|
||||
{
|
||||
std::vector<BattleHex> targetHexes = destinationTile.neighbouringTiles();
|
||||
targetHexes.push_back(destinationTile);
|
||||
@ -1448,7 +1448,7 @@ bool CBattleInfoCallback::battleHasDistancePenalty(const IBonusBearer * shooter,
|
||||
RETURN_IF_NOT_BATTLE(false);
|
||||
|
||||
const std::string cachingStrNoDistancePenalty = "type_NO_DISTANCE_PENALTY";
|
||||
static const auto selectorNoDistancePenalty = Selector::type()(Bonus::NO_DISTANCE_PENALTY);
|
||||
static const auto selectorNoDistancePenalty = Selector::type()(BonusType::NO_DISTANCE_PENALTY);
|
||||
|
||||
if(shooter->hasBonus(selectorNoDistancePenalty, cachingStrNoDistancePenalty))
|
||||
return false;
|
||||
@ -1458,7 +1458,7 @@ bool CBattleInfoCallback::battleHasDistancePenalty(const IBonusBearer * shooter,
|
||||
//If any hex of target creature is within range, there is no penalty
|
||||
int range = GameConstants::BATTLE_PENALTY_DISTANCE;
|
||||
|
||||
auto bonus = shooter->getBonus(Selector::type()(Bonus::LIMITED_SHOOTING_RANGE));
|
||||
auto bonus = shooter->getBonus(Selector::type()(BonusType::LIMITED_SHOOTING_RANGE));
|
||||
if(bonus != nullptr && bonus->additionalInfo != CAddInfo::NONE)
|
||||
range = bonus->additionalInfo[0];
|
||||
|
||||
@ -1542,13 +1542,13 @@ int32_t CBattleInfoCallback::battleGetSpellCost(const spells::Spell * sp, const
|
||||
|
||||
for(const auto * unit : battleAliveUnits())
|
||||
{
|
||||
if(unit->unitOwner() == caster->tempOwner && unit->hasBonusOfType(Bonus::CHANGES_SPELL_COST_FOR_ALLY))
|
||||
if(unit->unitOwner() == caster->tempOwner && unit->hasBonusOfType(BonusType::CHANGES_SPELL_COST_FOR_ALLY))
|
||||
{
|
||||
vstd::amax(manaReduction, unit->valOfBonuses(Bonus::CHANGES_SPELL_COST_FOR_ALLY));
|
||||
vstd::amax(manaReduction, unit->valOfBonuses(BonusType::CHANGES_SPELL_COST_FOR_ALLY));
|
||||
}
|
||||
if(unit->unitOwner() != caster->tempOwner && unit->hasBonusOfType(Bonus::CHANGES_SPELL_COST_FOR_ENEMY))
|
||||
if(unit->unitOwner() != caster->tempOwner && unit->hasBonusOfType(BonusType::CHANGES_SPELL_COST_FOR_ENEMY))
|
||||
{
|
||||
vstd::amax(manaIncrease, unit->valOfBonuses(Bonus::CHANGES_SPELL_COST_FOR_ENEMY));
|
||||
vstd::amax(manaIncrease, unit->valOfBonuses(BonusType::CHANGES_SPELL_COST_FOR_ENEMY));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1564,7 +1564,7 @@ bool CBattleInfoCallback::battleIsUnitBlocked(const battle::Unit * unit) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(false);
|
||||
|
||||
if(unit->hasBonusOfType(Bonus::SIEGE_WEAPON)) //siege weapons cannot be blocked
|
||||
if(unit->hasBonusOfType(BonusType::SIEGE_WEAPON)) //siege weapons cannot be blocked
|
||||
return false;
|
||||
|
||||
for(const auto * adjacent : battleAdjacentUnits(unit))
|
||||
@ -1635,9 +1635,9 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c
|
||||
for(const SpellID& spellID : allPossibleSpells)
|
||||
{
|
||||
std::stringstream cachingStr;
|
||||
cachingStr << "source_" << Bonus::SPELL_EFFECT << "id_" << spellID.num;
|
||||
cachingStr << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT) << "id_" << spellID.num;
|
||||
|
||||
if(subject->hasBonus(Selector::source(Bonus::SPELL_EFFECT, spellID), Selector::all, cachingStr.str())
|
||||
if(subject->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, spellID), Selector::all, cachingStr.str())
|
||||
//TODO: this ability has special limitations
|
||||
|| !(spellID.toSpell()->canBeCast(this, spells::Mode::CREATURE_ACTIVE, subject)))
|
||||
continue;
|
||||
@ -1702,7 +1702,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c
|
||||
{
|
||||
const auto * kingMonster = getAliveEnemy([&](const CStack * stack) -> bool //look for enemy, non-shooting stack
|
||||
{
|
||||
const auto isKing = Selector::type()(Bonus::KING);
|
||||
const auto isKing = Selector::type()(BonusType::KING);
|
||||
|
||||
return stack->hasBonus(isKing);
|
||||
});
|
||||
@ -1729,7 +1729,7 @@ SpellID CBattleInfoCallback::getRandomCastedSpell(CRandomGenerator & rand,const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(SpellID::NONE);
|
||||
|
||||
TConstBonusListPtr bl = caster->getBonuses(Selector::type()(Bonus::SPELLCASTER));
|
||||
TConstBonusListPtr bl = caster->getBonuses(Selector::type()(BonusType::SPELLCASTER));
|
||||
if (!bl->size())
|
||||
return SpellID::NONE;
|
||||
int totalWeight = 0;
|
||||
@ -1772,7 +1772,7 @@ int CBattleInfoCallback::battleGetSurrenderCost(const PlayerColor & Player) cons
|
||||
ret += unit->getRawSurrenderCost();
|
||||
|
||||
if(const CGHeroInstance * h = battleGetFightingHero(side))
|
||||
discount += h->valOfBonuses(Bonus::SURRENDER_DISCOUNT);
|
||||
discount += h->valOfBonuses(BonusType::SURRENDER_DISCOUNT);
|
||||
|
||||
ret = static_cast<int>(ret * (100.0 - discount) / 100.0);
|
||||
vstd::amax(ret, 0); //no negative costs for >100% discounts (impossible in original H3 mechanics, but some day...)
|
||||
@ -1790,7 +1790,7 @@ si8 CBattleInfoCallback::battleMinSpellLevel(ui8 side) const
|
||||
if(!node)
|
||||
return 0;
|
||||
|
||||
auto b = node->getBonuses(Selector::type()(Bonus::BLOCK_MAGIC_BELOW));
|
||||
auto b = node->getBonuses(Selector::type()(BonusType::BLOCK_MAGIC_BELOW));
|
||||
if(b->size())
|
||||
return b->totalValue();
|
||||
|
||||
@ -1809,7 +1809,7 @@ si8 CBattleInfoCallback::battleMaxSpellLevel(ui8 side) const
|
||||
return GameConstants::SPELL_LEVELS;
|
||||
|
||||
//We can't "just get value" - it'd be 0 if there are bonuses (and all would be blocked)
|
||||
auto b = node->getBonuses(Selector::type()(Bonus::BLOCK_MAGIC_ABOVE));
|
||||
auto b = node->getBonuses(Selector::type()(BonusType::BLOCK_MAGIC_ABOVE));
|
||||
if(b->size())
|
||||
return b->totalValue();
|
||||
|
||||
@ -1820,7 +1820,7 @@ std::optional<int> CBattleInfoCallback::battleIsFinished() const
|
||||
{
|
||||
auto units = battleGetUnitsIf([=](const battle::Unit * unit)
|
||||
{
|
||||
return unit->alive() && !unit->isTurret() && !unit->hasBonusOfType(Bonus::SIEGE_WEAPON);
|
||||
return unit->alive() && !unit->isTurret() && !unit->hasBonusOfType(BonusType::SIEGE_WEAPON);
|
||||
});
|
||||
|
||||
std::array<bool, 2> hasUnit = {false, false}; //index is BattleSide
|
||||
|
@ -271,7 +271,7 @@ bool CBattleInfoEssentials::battleCanFlee(const PlayerColor & player) const
|
||||
return false;
|
||||
|
||||
//eg. one of heroes is wearing shakles of war
|
||||
if(myHero->hasBonusOfType(Bonus::BATTLE_NO_FLEEING))
|
||||
if(myHero->hasBonusOfType(BonusType::BATTLE_NO_FLEEING))
|
||||
return false;
|
||||
|
||||
//we are besieged defender
|
||||
@ -394,7 +394,7 @@ PlayerColor CBattleInfoEssentials::battleGetOwner(const battle::Unit * unit) con
|
||||
|
||||
PlayerColor initialOwner = getBattle()->getSidePlayer(unit->unitSide());
|
||||
|
||||
static CSelector selector = Selector::type()(Bonus::HYPNOTIZED);
|
||||
static CSelector selector = Selector::type()(BonusType::HYPNOTIZED);
|
||||
static std::string cachingString = "type_103s-1";
|
||||
|
||||
if(unit->hasBonus(selector, cachingString))
|
||||
|
@ -85,8 +85,8 @@ void CAmmo::serializeJson(JsonSerializeFormat & handler)
|
||||
|
||||
///CShots
|
||||
CShots::CShots(const battle::Unit * Owner)
|
||||
: CAmmo(Owner, Selector::type()(Bonus::SHOTS)),
|
||||
shooter(Owner, Selector::type()(Bonus::SHOOTER))
|
||||
: CAmmo(Owner, Selector::type()(BonusType::SHOTS)),
|
||||
shooter(Owner, Selector::type()(BonusType::SHOOTER))
|
||||
{
|
||||
}
|
||||
|
||||
@ -117,16 +117,16 @@ int32_t CShots::total() const
|
||||
|
||||
///CCasts
|
||||
CCasts::CCasts(const battle::Unit * Owner):
|
||||
CAmmo(Owner, Selector::type()(Bonus::CASTS))
|
||||
CAmmo(Owner, Selector::type()(BonusType::CASTS))
|
||||
{
|
||||
}
|
||||
|
||||
///CRetaliations
|
||||
CRetaliations::CRetaliations(const battle::Unit * Owner)
|
||||
: CAmmo(Owner, Selector::type()(Bonus::ADDITIONAL_RETALIATION)),
|
||||
: CAmmo(Owner, Selector::type()(BonusType::ADDITIONAL_RETALIATION)),
|
||||
totalCache(0),
|
||||
noRetaliation(Owner, Selector::type()(Bonus::SIEGE_WEAPON).Or(Selector::type()(Bonus::HYPNOTIZED)).Or(Selector::type()(Bonus::NO_RETALIATION))),
|
||||
unlimited(Owner, Selector::type()(Bonus::UNLIMITED_RETALIATIONS))
|
||||
noRetaliation(Owner, Selector::type()(BonusType::SIEGE_WEAPON).Or(Selector::type()(BonusType::HYPNOTIZED)).Or(Selector::type()(BonusType::NO_RETALIATION))),
|
||||
unlimited(Owner, Selector::type()(BonusType::UNLIMITED_RETALIATIONS))
|
||||
{
|
||||
}
|
||||
|
||||
@ -179,7 +179,7 @@ void CHealth::init()
|
||||
{
|
||||
reset();
|
||||
fullUnits = owner->unitBaseAmount() > 1 ? owner->unitBaseAmount() - 1 : 0;
|
||||
firstHPleft = owner->unitBaseAmount() > 0 ? owner->MaxHealth() : 0;
|
||||
firstHPleft = owner->unitBaseAmount() > 0 ? owner->getMaxHealth() : 0;
|
||||
}
|
||||
|
||||
void CHealth::addResurrected(int32_t amount)
|
||||
@ -190,12 +190,12 @@ void CHealth::addResurrected(int32_t amount)
|
||||
|
||||
int64_t CHealth::available() const
|
||||
{
|
||||
return static_cast<int64_t>(firstHPleft) + owner->MaxHealth() * fullUnits;
|
||||
return static_cast<int64_t>(firstHPleft) + owner->getMaxHealth() * fullUnits;
|
||||
}
|
||||
|
||||
int64_t CHealth::total() const
|
||||
{
|
||||
return static_cast<int64_t>(owner->MaxHealth()) * owner->unitBaseAmount();
|
||||
return static_cast<int64_t>(owner->getMaxHealth()) * owner->unitBaseAmount();
|
||||
}
|
||||
|
||||
void CHealth::damage(int64_t & amount)
|
||||
@ -230,7 +230,7 @@ void CHealth::damage(int64_t & amount)
|
||||
|
||||
void CHealth::heal(int64_t & amount, EHealLevel level, EHealPower power)
|
||||
{
|
||||
const int32_t unitHealth = owner->MaxHealth();
|
||||
const int32_t unitHealth = owner->getMaxHealth();
|
||||
const int32_t oldCount = getCount();
|
||||
|
||||
int64_t maxHeal = std::numeric_limits<int64_t>::max();
|
||||
@ -267,7 +267,7 @@ void CHealth::heal(int64_t & amount, EHealLevel level, EHealPower power)
|
||||
|
||||
void CHealth::setFromTotal(const int64_t totalHealth)
|
||||
{
|
||||
const int32_t unitHealth = owner->MaxHealth();
|
||||
const int32_t unitHealth = owner->getMaxHealth();
|
||||
firstHPleft = totalHealth % unitHealth;
|
||||
fullUnits = static_cast<int32_t>(totalHealth / unitHealth);
|
||||
|
||||
@ -306,7 +306,7 @@ void CHealth::takeResurrected()
|
||||
{
|
||||
int64_t totalHealth = available();
|
||||
|
||||
totalHealth -= resurrected * owner->MaxHealth();
|
||||
totalHealth -= resurrected * owner->getMaxHealth();
|
||||
vstd::amax(totalHealth, 0);
|
||||
setFromTotal(totalHealth);
|
||||
resurrected = 0;
|
||||
@ -339,13 +339,13 @@ CUnitState::CUnitState():
|
||||
counterAttacks(this),
|
||||
health(this),
|
||||
shots(this),
|
||||
totalAttacks(this, Selector::type()(Bonus::ADDITIONAL_ATTACK), 1),
|
||||
minDamage(this, Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1)), 0),
|
||||
maxDamage(this, Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2)), 0),
|
||||
attack(this, Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), 0),
|
||||
defence(this, Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE), 0),
|
||||
inFrenzy(this, Selector::type()(Bonus::IN_FRENZY)),
|
||||
cloneLifetimeMarker(this, Selector::type()(Bonus::NONE).And(Selector::source(Bonus::SPELL_EFFECT, SpellID::CLONE))),
|
||||
totalAttacks(this, Selector::type()(BonusType::ADDITIONAL_ATTACK), 1),
|
||||
minDamage(this, Selector::typeSubtype(BonusType::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(BonusType::CREATURE_DAMAGE, 1)), 0),
|
||||
maxDamage(this, Selector::typeSubtype(BonusType::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(BonusType::CREATURE_DAMAGE, 2)), 0),
|
||||
attack(this, Selector::typeSubtype(BonusType::PRIMARY_SKILL, PrimarySkill::ATTACK), 0),
|
||||
defence(this, Selector::typeSubtype(BonusType::PRIMARY_SKILL, PrimarySkill::DEFENSE), 0),
|
||||
inFrenzy(this, Selector::type()(BonusType::IN_FRENZY)),
|
||||
cloneLifetimeMarker(this, Selector::type()(BonusType::NONE).And(Selector::source(BonusSource::SPELL_EFFECT, SpellID::CLONE))),
|
||||
cloneID(-1)
|
||||
{
|
||||
|
||||
@ -430,7 +430,7 @@ const CGHeroInstance * CUnitState::getHeroCaster() const
|
||||
|
||||
int32_t CUnitState::getSpellSchoolLevel(const spells::Spell * spell, int32_t * outSelectedSchool) const
|
||||
{
|
||||
int32_t skill = valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, spell->getIndex()));
|
||||
int32_t skill = valOfBonuses(Selector::typeSubtype(BonusType::SPELLCASTER, spell->getIndex()));
|
||||
vstd::abetween(skill, 0, 3);
|
||||
return skill;
|
||||
}
|
||||
@ -453,12 +453,12 @@ int32_t CUnitState::getEffectLevel(const spells::Spell * spell) const
|
||||
|
||||
int32_t CUnitState::getEffectPower(const spells::Spell * spell) const
|
||||
{
|
||||
return valOfBonuses(Bonus::CREATURE_SPELL_POWER) * getCount() / 100;
|
||||
return valOfBonuses(BonusType::CREATURE_SPELL_POWER) * getCount() / 100;
|
||||
}
|
||||
|
||||
int32_t CUnitState::getEnchantPower(const spells::Spell * spell) const
|
||||
{
|
||||
int32_t res = valOfBonuses(Bonus::CREATURE_ENCHANT_POWER);
|
||||
int32_t res = valOfBonuses(BonusType::CREATURE_ENCHANT_POWER);
|
||||
if(res <= 0)
|
||||
res = 3;//default for creatures
|
||||
return res;
|
||||
@ -466,7 +466,7 @@ int32_t CUnitState::getEnchantPower(const spells::Spell * spell) const
|
||||
|
||||
int64_t CUnitState::getEffectValue(const spells::Spell * spell) const
|
||||
{
|
||||
return static_cast<int64_t>(getCount()) * valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, spell->getIndex());
|
||||
return static_cast<int64_t>(getCount()) * valOfBonuses(BonusType::SPECIFIC_SPELL_POWER, spell->getIndex());
|
||||
}
|
||||
|
||||
PlayerColor CUnitState::getCasterOwner() const
|
||||
@ -511,7 +511,7 @@ bool CUnitState::isGhost() const
|
||||
|
||||
bool CUnitState::isFrozen() const
|
||||
{
|
||||
return hasBonus(Selector::source(Bonus::SPELL_EFFECT, SpellID::STONE_GAZE), Selector::all);
|
||||
return hasBonus(Selector::source(BonusSource::SPELL_EFFECT, SpellID::STONE_GAZE), Selector::all);
|
||||
}
|
||||
|
||||
bool CUnitState::isValidTarget(bool allowDead) const
|
||||
@ -588,12 +588,12 @@ void CUnitState::setPosition(BattleHex hex)
|
||||
|
||||
int32_t CUnitState::getInitiative(int turn) const
|
||||
{
|
||||
return valOfBonuses(Selector::type()(Bonus::STACKS_SPEED).And(Selector::turns(turn)));
|
||||
return valOfBonuses(Selector::type()(BonusType::STACKS_SPEED).And(Selector::turns(turn)));
|
||||
}
|
||||
|
||||
bool CUnitState::canMove(int turn) const
|
||||
{
|
||||
return alive() && !hasBonus(Selector::type()(Bonus::NOT_ACTIVE).And(Selector::turns(turn))); //eg. Ammo Cart or blinded creature
|
||||
return alive() && !hasBonus(Selector::type()(BonusType::NOT_ACTIVE).And(Selector::turns(turn))); //eg. Ammo Cart or blinded creature
|
||||
}
|
||||
|
||||
bool CUnitState::defended(int turn) const
|
||||
|
@ -11,6 +11,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Unit.h"
|
||||
#include "../bonuses/CBonusProxy.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "CBattleInfoCallback.h"
|
||||
#include "Unit.h"
|
||||
|
||||
#include "../HeroBonus.h"
|
||||
#include "../bonuses/Bonus.h"
|
||||
#include "../mapObjects/CGTownInstance.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../GameSettings.h"
|
||||
@ -46,13 +46,13 @@ DamageRange DamageCalculator::getBaseDamageSingle() const
|
||||
}
|
||||
|
||||
const std::string cachingStrSiedgeWeapon = "type_SIEGE_WEAPON";
|
||||
static const auto selectorSiedgeWeapon = Selector::type()(Bonus::SIEGE_WEAPON);
|
||||
static const auto selectorSiedgeWeapon = Selector::type()(BonusType::SIEGE_WEAPON);
|
||||
|
||||
if(info.attacker->hasBonus(selectorSiedgeWeapon, cachingStrSiedgeWeapon) && info.attacker->creatureIndex() != CreatureID::ARROW_TOWERS)
|
||||
{
|
||||
auto retrieveHeroPrimSkill = [&](int skill) -> int
|
||||
{
|
||||
std::shared_ptr<const Bonus> b = info.attacker->getBonus(Selector::sourceTypeSel(Bonus::HERO_BASE_SKILL).And(Selector::typeSubtype(Bonus::PRIMARY_SKILL, skill)));
|
||||
std::shared_ptr<const Bonus> b = info.attacker->getBonus(Selector::sourceTypeSel(BonusSource::HERO_BASE_SKILL).And(Selector::typeSubtype(BonusType::PRIMARY_SKILL, skill)));
|
||||
return b ? b->val : 0;
|
||||
};
|
||||
|
||||
@ -66,10 +66,10 @@ DamageRange DamageCalculator::getBaseDamageSingle() const
|
||||
DamageRange DamageCalculator::getBaseDamageBlessCurse() const
|
||||
{
|
||||
const std::string cachingStrForcedMinDamage = "type_ALWAYS_MINIMUM_DAMAGE";
|
||||
static const auto selectorForcedMinDamage = Selector::type()(Bonus::ALWAYS_MINIMUM_DAMAGE);
|
||||
static const auto selectorForcedMinDamage = Selector::type()(BonusType::ALWAYS_MINIMUM_DAMAGE);
|
||||
|
||||
const std::string cachingStrForcedMaxDamage = "type_ALWAYS_MAXIMUM_DAMAGE";
|
||||
static const auto selectorForcedMaxDamage = Selector::type()(Bonus::ALWAYS_MAXIMUM_DAMAGE);
|
||||
static const auto selectorForcedMaxDamage = Selector::type()(BonusType::ALWAYS_MAXIMUM_DAMAGE);
|
||||
|
||||
TConstBonusListPtr curseEffects = info.attacker->getBonuses(selectorForcedMinDamage, cachingStrForcedMinDamage);
|
||||
TConstBonusListPtr blessEffects = info.attacker->getBonuses(selectorForcedMaxDamage, cachingStrForcedMaxDamage);
|
||||
@ -130,10 +130,10 @@ int DamageCalculator::getActorAttackEffective() const
|
||||
int DamageCalculator::getActorAttackSlayer() const
|
||||
{
|
||||
const std::string cachingStrSlayer = "type_SLAYER";
|
||||
static const auto selectorSlayer = Selector::type()(Bonus::SLAYER);
|
||||
static const auto selectorSlayer = Selector::type()(BonusType::SLAYER);
|
||||
|
||||
auto slayerEffects = info.attacker->getBonuses(selectorSlayer, cachingStrSlayer);
|
||||
auto slayerAffected = info.defender->unitType()->valOfBonuses(Selector::type()(Bonus::KING));
|
||||
auto slayerAffected = info.defender->unitType()->valOfBonuses(Selector::type()(BonusType::KING));
|
||||
|
||||
if(std::shared_ptr<const Bonus> slayerEffect = slayerEffects->getFirst(Selector::all))
|
||||
{
|
||||
@ -143,7 +143,7 @@ int DamageCalculator::getActorAttackSlayer() const
|
||||
if(isAffected)
|
||||
{
|
||||
int attackBonus = SpellID(SpellID::SLAYER).toSpell()->getLevelPower(spLevel);
|
||||
if(info.attacker->hasBonusOfType(Bonus::SPECIAL_PECULIAR_ENCHANT, SpellID::SLAYER))
|
||||
if(info.attacker->hasBonusOfType(BonusType::SPECIAL_PECULIAR_ENCHANT, SpellID::SLAYER))
|
||||
{
|
||||
ui8 attackerTier = info.attacker->unitType()->getLevel();
|
||||
ui8 specialtyBonus = std::max(5 - attackerTier, 0);
|
||||
@ -167,7 +167,7 @@ int DamageCalculator::getTargetDefenseEffective() const
|
||||
|
||||
int DamageCalculator::getTargetDefenseIgnored() const
|
||||
{
|
||||
double multDefenceReduction = battleBonusValue(info.attacker, Selector::type()(Bonus::ENEMY_DEFENCE_REDUCTION)) / 100.0;
|
||||
double multDefenceReduction = battleBonusValue(info.attacker, Selector::type()(BonusType::ENEMY_DEFENCE_REDUCTION)) / 100.0;
|
||||
|
||||
if(multDefenceReduction > 0)
|
||||
{
|
||||
@ -195,7 +195,7 @@ double DamageCalculator::getAttackSkillFactor() const
|
||||
double DamageCalculator::getAttackBlessFactor() const
|
||||
{
|
||||
const std::string cachingStrDamage = "type_GENERAL_DAMAGE_PREMY";
|
||||
static const auto selectorDamage = Selector::type()(Bonus::GENERAL_DAMAGE_PREMY);
|
||||
static const auto selectorDamage = Selector::type()(BonusType::GENERAL_DAMAGE_PREMY);
|
||||
return info.attacker->valOfBonuses(selectorDamage, cachingStrDamage) / 100.0;
|
||||
}
|
||||
|
||||
@ -205,11 +205,11 @@ double DamageCalculator::getAttackOffenseArcheryFactor() const
|
||||
if(info.shooting)
|
||||
{
|
||||
const std::string cachingStrArchery = "type_PERCENTAGE_DAMAGE_BOOSTs_1";
|
||||
static const auto selectorArchery = Selector::typeSubtype(Bonus::PERCENTAGE_DAMAGE_BOOST, 1);
|
||||
static const auto selectorArchery = Selector::typeSubtype(BonusType::PERCENTAGE_DAMAGE_BOOST, 1);
|
||||
return info.attacker->valOfBonuses(selectorArchery, cachingStrArchery) / 100.0;
|
||||
}
|
||||
const std::string cachingStrOffence = "type_PERCENTAGE_DAMAGE_BOOSTs_0";
|
||||
static const auto selectorOffence = Selector::typeSubtype(Bonus::PERCENTAGE_DAMAGE_BOOST, 0);
|
||||
static const auto selectorOffence = Selector::typeSubtype(BonusType::PERCENTAGE_DAMAGE_BOOST, 0);
|
||||
return info.attacker->valOfBonuses(selectorOffence, cachingStrOffence) / 100.0;
|
||||
}
|
||||
|
||||
@ -231,7 +231,7 @@ double DamageCalculator::getAttackDoubleDamageFactor() const
|
||||
{
|
||||
if(info.doubleDamage) {
|
||||
const auto cachingStr = "type_BONUS_DAMAGE_PERCENTAGEs_" + std::to_string(info.attacker->creatureIndex());
|
||||
const auto selector = Selector::typeSubtype(Bonus::BONUS_DAMAGE_PERCENTAGE, info.attacker->creatureIndex());
|
||||
const auto selector = Selector::typeSubtype(BonusType::BONUS_DAMAGE_PERCENTAGE, info.attacker->creatureIndex());
|
||||
return info.attacker->valOfBonuses(selector, cachingStr) / 100.0;
|
||||
}
|
||||
return 0.0;
|
||||
@ -240,10 +240,10 @@ double DamageCalculator::getAttackDoubleDamageFactor() const
|
||||
double DamageCalculator::getAttackJoustingFactor() const
|
||||
{
|
||||
const std::string cachingStrJousting = "type_JOUSTING";
|
||||
static const auto selectorJousting = Selector::type()(Bonus::JOUSTING);
|
||||
static const auto selectorJousting = Selector::type()(BonusType::JOUSTING);
|
||||
|
||||
const std::string cachingStrChargeImmunity = "type_CHARGE_IMMUNITY";
|
||||
static const auto selectorChargeImmunity = Selector::type()(Bonus::CHARGE_IMMUNITY);
|
||||
static const auto selectorChargeImmunity = Selector::type()(BonusType::CHARGE_IMMUNITY);
|
||||
|
||||
//applying jousting bonus
|
||||
if(info.chargeDistance > 0 && info.attacker->hasBonus(selectorJousting, cachingStrJousting) && !info.defender->hasBonus(selectorChargeImmunity, cachingStrChargeImmunity))
|
||||
@ -255,7 +255,7 @@ double DamageCalculator::getAttackHateFactor() const
|
||||
{
|
||||
//assume that unit have only few HATE features and cache them all
|
||||
const std::string cachingStrHate = "type_HATE";
|
||||
static const auto selectorHate = Selector::type()(Bonus::HATE);
|
||||
static const auto selectorHate = Selector::type()(BonusType::HATE);
|
||||
|
||||
auto allHateEffects = info.attacker->getBonuses(selectorHate, cachingStrHate);
|
||||
|
||||
@ -281,7 +281,7 @@ double DamageCalculator::getDefenseSkillFactor() const
|
||||
double DamageCalculator::getDefenseArmorerFactor() const
|
||||
{
|
||||
const std::string cachingStrArmorer = "type_GENERAL_DAMAGE_REDUCTIONs_N1_NsrcSPELL_EFFECT";
|
||||
static const auto selectorArmorer = Selector::typeSubtype(Bonus::GENERAL_DAMAGE_REDUCTION, -1).And(Selector::sourceTypeSel(Bonus::SPELL_EFFECT).Not());
|
||||
static const auto selectorArmorer = Selector::typeSubtype(BonusType::GENERAL_DAMAGE_REDUCTION, -1).And(Selector::sourceTypeSel(BonusSource::SPELL_EFFECT).Not());
|
||||
return info.defender->valOfBonuses(selectorArmorer, cachingStrArmorer) / 100.0;
|
||||
|
||||
}
|
||||
@ -289,10 +289,10 @@ double DamageCalculator::getDefenseArmorerFactor() const
|
||||
double DamageCalculator::getDefenseMagicShieldFactor() const
|
||||
{
|
||||
const std::string cachingStrMeleeReduction = "type_GENERAL_DAMAGE_REDUCTIONs_0";
|
||||
static const auto selectorMeleeReduction = Selector::typeSubtype(Bonus::GENERAL_DAMAGE_REDUCTION, 0);
|
||||
static const auto selectorMeleeReduction = Selector::typeSubtype(BonusType::GENERAL_DAMAGE_REDUCTION, 0);
|
||||
|
||||
const std::string cachingStrRangedReduction = "type_GENERAL_DAMAGE_REDUCTIONs_1";
|
||||
static const auto selectorRangedReduction = Selector::typeSubtype(Bonus::GENERAL_DAMAGE_REDUCTION, 1);
|
||||
static const auto selectorRangedReduction = Selector::typeSubtype(BonusType::GENERAL_DAMAGE_REDUCTION, 1);
|
||||
|
||||
//handling spell effects - shield and air shield
|
||||
if(info.shooting)
|
||||
@ -311,7 +311,7 @@ double DamageCalculator::getDefenseRangePenaltiesFactor() const
|
||||
const std::string cachingStrAdvAirShield = "isAdvancedAirShield";
|
||||
auto isAdvancedAirShield = [](const Bonus* bonus)
|
||||
{
|
||||
return bonus->source == Bonus::SPELL_EFFECT
|
||||
return bonus->source == BonusSource::SPELL_EFFECT
|
||||
&& bonus->sid == SpellID::AIR_SHIELD
|
||||
&& bonus->val >= SecSkillLevel::ADVANCED;
|
||||
};
|
||||
@ -325,7 +325,7 @@ double DamageCalculator::getDefenseRangePenaltiesFactor() const
|
||||
else
|
||||
{
|
||||
const std::string cachingStrNoMeleePenalty = "type_NO_MELEE_PENALTY";
|
||||
static const auto selectorNoMeleePenalty = Selector::type()(Bonus::NO_MELEE_PENALTY);
|
||||
static const auto selectorNoMeleePenalty = Selector::type()(BonusType::NO_MELEE_PENALTY);
|
||||
|
||||
if(info.attacker->isShooter() && !info.attacker->hasBonus(selectorNoMeleePenalty, cachingStrNoMeleePenalty))
|
||||
return 0.5;
|
||||
@ -356,7 +356,7 @@ double DamageCalculator::getDefenseUnluckyFactor() const
|
||||
|
||||
double DamageCalculator::getDefenseBlindParalysisFactor() const
|
||||
{
|
||||
double multAttackReduction = battleBonusValue(info.attacker, Selector::type()(Bonus::GENERAL_ATTACK_REDUCTION)) / 100.0;
|
||||
double multAttackReduction = battleBonusValue(info.attacker, Selector::type()(BonusType::GENERAL_ATTACK_REDUCTION)) / 100.0;
|
||||
return multAttackReduction;
|
||||
}
|
||||
|
||||
@ -366,7 +366,7 @@ double DamageCalculator::getDefenseForgetfulnessFactor() const
|
||||
{
|
||||
//todo: set actual percentage in spell bonus configuration instead of just level; requires non trivial backward compatibility handling
|
||||
//get list first, total value of 0 also counts
|
||||
TConstBonusListPtr forgetfulList = info.attacker->getBonuses(Selector::type()(Bonus::FORGETFULL),"type_FORGETFULL");
|
||||
TConstBonusListPtr forgetfulList = info.attacker->getBonuses(Selector::type()(BonusType::FORGETFULL),"type_FORGETFULL");
|
||||
|
||||
if(!forgetfulList->empty())
|
||||
{
|
||||
@ -386,7 +386,7 @@ double DamageCalculator::getDefensePetrificationFactor() const
|
||||
{
|
||||
// Creatures that are petrified by a Basilisk's Petrifying attack or a Medusa's Stone gaze take 50% damage (R8 = 0.50) from ranged and melee attacks. Taking damage also deactivates the effect.
|
||||
const std::string cachingStrAllReduction = "type_GENERAL_DAMAGE_REDUCTIONs_N1_srcSPELL_EFFECT";
|
||||
static const auto selectorAllReduction = Selector::typeSubtype(Bonus::GENERAL_DAMAGE_REDUCTION, -1).And(Selector::sourceTypeSel(Bonus::SPELL_EFFECT));
|
||||
static const auto selectorAllReduction = Selector::typeSubtype(BonusType::GENERAL_DAMAGE_REDUCTION, -1).And(Selector::sourceTypeSel(BonusSource::SPELL_EFFECT));
|
||||
|
||||
return info.defender->valOfBonuses(selectorAllReduction, cachingStrAllReduction) / 100.0;
|
||||
}
|
||||
@ -397,7 +397,7 @@ double DamageCalculator::getDefenseMagicFactor() const
|
||||
if(info.attacker->creatureIndex() == CreatureID::MAGIC_ELEMENTAL)
|
||||
{
|
||||
const std::string cachingStrMagicImmunity = "type_LEVEL_SPELL_IMMUNITY";
|
||||
static const auto selectorMagicImmunity = Selector::type()(Bonus::LEVEL_SPELL_IMMUNITY);
|
||||
static const auto selectorMagicImmunity = Selector::type()(BonusType::LEVEL_SPELL_IMMUNITY);
|
||||
|
||||
if(info.defender->valOfBonuses(selectorMagicImmunity, cachingStrMagicImmunity) >= 5)
|
||||
return 0.5;
|
||||
@ -411,7 +411,7 @@ double DamageCalculator::getDefenseMindFactor() const
|
||||
if(info.attacker->creatureIndex() == CreatureID::PSYCHIC_ELEMENTAL)
|
||||
{
|
||||
const std::string cachingStrMindImmunity = "type_MIND_IMMUNITY";
|
||||
static const auto selectorMindImmunity = Selector::type()(Bonus::MIND_IMMUNITY);
|
||||
static const auto selectorMindImmunity = Selector::type()(BonusType::MIND_IMMUNITY);
|
||||
|
||||
if(info.defender->hasBonus(selectorMindImmunity, cachingStrMindImmunity))
|
||||
return 0.5;
|
||||
@ -464,17 +464,17 @@ int64_t DamageCalculator::getCasualties(int64_t damageDealt) const
|
||||
return 0;
|
||||
|
||||
int64_t damageLeft = damageDealt - info.defender->getFirstHPleft();
|
||||
int64_t killsLeft = damageLeft / info.defender->MaxHealth();
|
||||
int64_t killsLeft = damageLeft / info.defender->getMaxHealth();
|
||||
|
||||
return 1 + killsLeft;
|
||||
}
|
||||
|
||||
int DamageCalculator::battleBonusValue(const IBonusBearer * bearer, const CSelector & selector) const
|
||||
{
|
||||
auto noLimit = Selector::effectRange()(Bonus::NO_LIMIT);
|
||||
auto noLimit = Selector::effectRange()(BonusLimitEffect::NO_LIMIT);
|
||||
auto limitMatches = info.shooting
|
||||
? Selector::effectRange()(Bonus::ONLY_DISTANCE_FIGHT)
|
||||
: Selector::effectRange()(Bonus::ONLY_MELEE_FIGHT);
|
||||
? Selector::effectRange()(BonusLimitEffect::ONLY_DISTANCE_FIGHT)
|
||||
: Selector::effectRange()(BonusLimitEffect::ONLY_MELEE_FIGHT);
|
||||
|
||||
//any regular bonuses or just ones for melee/ranged
|
||||
return bearer->getBonuses(selector, noLimit.Or(limitMatches))->totalValue();
|
||||
|
@ -19,7 +19,7 @@ ReachabilityInfo::Parameters::Parameters(const battle::Unit * Stack, BattleHex S
|
||||
startPosition(StartPosition),
|
||||
doubleWide(Stack->doubleWide()),
|
||||
side(Stack->unitSide()),
|
||||
flying(Stack->hasBonusOfType(Bonus::FLYING))
|
||||
flying(Stack->hasBonusOfType(BonusType::FLYING))
|
||||
{
|
||||
knownAccessible = battle::Unit::getHexes(startPosition, doubleWide, side);
|
||||
}
|
||||
|
@ -10,10 +10,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vcmi/Entity.h>
|
||||
#include <vcmi/Creature.h>
|
||||
#include <vcmi/spells/Caster.h>
|
||||
|
||||
#include "../HeroBonus.h"
|
||||
#include "../bonuses/Bonus.h"
|
||||
#include "../bonuses/IBonusBearer.h"
|
||||
|
||||
#include "IUnitInfo.h"
|
||||
#include "BattleHex.h"
|
||||
@ -41,7 +42,7 @@ namespace BattlePhases
|
||||
|
||||
class CUnitState;
|
||||
|
||||
class DLL_LINKAGE Unit : public IUnitInfo, public spells::Caster, public virtual IBonusBearer, public IConstBonusNativeTerrainProvider
|
||||
class DLL_LINKAGE Unit : public IUnitInfo, public spells::Caster, public virtual IBonusBearer, public ACreature
|
||||
{
|
||||
public:
|
||||
virtual ~Unit();
|
||||
|
324
lib/bonuses/Bonus.cpp
Normal file
324
lib/bonuses/Bonus.cpp
Normal file
@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Bonus.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 "Bonus.h"
|
||||
#include "CBonusSystemNode.h"
|
||||
#include "Limiters.h"
|
||||
#include "Updaters.h"
|
||||
#include "Propagators.h"
|
||||
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CCreatureHandler.h"
|
||||
#include "../CCreatureSet.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../CGeneralTextHandler.h"
|
||||
#include "../CSkillHandler.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CModHandler.h"
|
||||
#include "../TerrainHandler.h"
|
||||
#include "../StringConstants.h"
|
||||
#include "../battle/BattleInfo.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
const std::set<std::string> deprecatedBonusSet = {
|
||||
"SECONDARY_SKILL_PREMY",
|
||||
"SECONDARY_SKILL_VAL2",
|
||||
"MAXED_SPELL",
|
||||
"LAND_MOVEMENT",
|
||||
"SEA_MOVEMENT",
|
||||
"SIGHT_RADIOUS",
|
||||
"NO_TYPE",
|
||||
"SPECIAL_SECONDARY_SKILL",
|
||||
"FULL_HP_REGENERATION",
|
||||
"KING1",
|
||||
"KING2",
|
||||
"KING3",
|
||||
"BLOCK_MORALE",
|
||||
"BLOCK_LUCK",
|
||||
"SELF_MORALE",
|
||||
"SELF_LUCK",
|
||||
"DIRECT_DAMAGE_IMMUNITY"
|
||||
};
|
||||
|
||||
//This constructor should be placed here to avoid side effects
|
||||
CAddInfo::CAddInfo() = default;
|
||||
|
||||
CAddInfo::CAddInfo(si32 value)
|
||||
{
|
||||
if(value != CAddInfo::NONE)
|
||||
push_back(value);
|
||||
}
|
||||
|
||||
bool CAddInfo::operator==(si32 value) const
|
||||
{
|
||||
switch(size())
|
||||
{
|
||||
case 0:
|
||||
return value == CAddInfo::NONE;
|
||||
case 1:
|
||||
return operator[](0) == value;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CAddInfo::operator!=(si32 value) const
|
||||
{
|
||||
return !operator==(value);
|
||||
}
|
||||
|
||||
si32 & CAddInfo::operator[](size_type pos)
|
||||
{
|
||||
if(pos >= size())
|
||||
resize(pos + 1, CAddInfo::NONE);
|
||||
return vector::operator[](pos);
|
||||
}
|
||||
|
||||
si32 CAddInfo::operator[](size_type pos) const
|
||||
{
|
||||
return pos < size() ? vector::operator[](pos) : CAddInfo::NONE;
|
||||
}
|
||||
|
||||
std::string CAddInfo::toString() const
|
||||
{
|
||||
return toJsonNode().toJson(true);
|
||||
}
|
||||
|
||||
JsonNode CAddInfo::toJsonNode() const
|
||||
{
|
||||
if(size() < 2)
|
||||
{
|
||||
return JsonUtils::intNode(operator[](0));
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonNode node(JsonNode::JsonType::DATA_VECTOR);
|
||||
for(si32 value : *this)
|
||||
node.Vector().push_back(JsonUtils::intNode(value));
|
||||
return node;
|
||||
}
|
||||
}
|
||||
std::string Bonus::Description(std::optional<si32> customValue) const
|
||||
{
|
||||
std::ostringstream str;
|
||||
|
||||
if(description.empty())
|
||||
{
|
||||
if(stacking.empty() || stacking == "ALWAYS")
|
||||
{
|
||||
switch(source)
|
||||
{
|
||||
case BonusSource::ARTIFACT:
|
||||
str << ArtifactID(sid).toArtifact(VLC->artifacts())->getNameTranslated();
|
||||
break;
|
||||
case BonusSource::SPELL_EFFECT:
|
||||
str << SpellID(sid).toSpell(VLC->spells())->getNameTranslated();
|
||||
break;
|
||||
case BonusSource::CREATURE_ABILITY:
|
||||
str << CreatureID(sid).toCreature(VLC->creatures())->getNamePluralTranslated();
|
||||
break;
|
||||
case BonusSource::SECONDARY_SKILL:
|
||||
str << VLC->skills()->getByIndex(sid)->getNameTranslated();
|
||||
break;
|
||||
case BonusSource::HERO_SPECIAL:
|
||||
str << VLC->heroTypes()->getByIndex(sid)->getNameTranslated();
|
||||
break;
|
||||
default:
|
||||
//todo: handle all possible sources
|
||||
str << "Unknown";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
str << stacking;
|
||||
}
|
||||
else
|
||||
{
|
||||
str << description;
|
||||
}
|
||||
|
||||
if(auto value = customValue.value_or(val))
|
||||
str << " " << std::showpos << value;
|
||||
|
||||
return str.str();
|
||||
}
|
||||
|
||||
JsonNode subtypeToJson(BonusType type, int subtype)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case BonusType::PRIMARY_SKILL:
|
||||
return JsonUtils::stringNode("primSkill." + PrimarySkill::names[subtype]);
|
||||
case BonusType::SPECIAL_SPELL_LEV:
|
||||
case BonusType::SPECIFIC_SPELL_DAMAGE:
|
||||
case BonusType::SPELL:
|
||||
case BonusType::SPECIAL_PECULIAR_ENCHANT:
|
||||
case BonusType::SPECIAL_ADD_VALUE_ENCHANT:
|
||||
case BonusType::SPECIAL_FIXED_VALUE_ENCHANT:
|
||||
return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "spell", SpellID::encode(subtype)));
|
||||
case BonusType::IMPROVED_NECROMANCY:
|
||||
case BonusType::SPECIAL_UPGRADE:
|
||||
return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "creature", CreatureID::encode(subtype)));
|
||||
case BonusType::GENERATE_RESOURCE:
|
||||
return JsonUtils::stringNode("resource." + GameConstants::RESOURCE_NAMES[subtype]);
|
||||
default:
|
||||
return JsonUtils::intNode(subtype);
|
||||
}
|
||||
}
|
||||
|
||||
JsonNode additionalInfoToJson(BonusType type, CAddInfo addInfo)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case BonusType::SPECIAL_UPGRADE:
|
||||
return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "creature", CreatureID::encode(addInfo[0])));
|
||||
default:
|
||||
return addInfo.toJsonNode();
|
||||
}
|
||||
}
|
||||
|
||||
JsonNode Bonus::toJsonNode() const
|
||||
{
|
||||
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
|
||||
// only add values that might reasonably be found in config files
|
||||
root["type"].String() = vstd::findKey(bonusNameMap, type);
|
||||
if(subtype != -1)
|
||||
root["subtype"] = subtypeToJson(type, subtype);
|
||||
if(additionalInfo != CAddInfo::NONE)
|
||||
root["addInfo"] = additionalInfoToJson(type, additionalInfo);
|
||||
if(turnsRemain != 0)
|
||||
root["turns"].Integer() = turnsRemain;
|
||||
if(source != BonusSource::OTHER)
|
||||
root["sourceType"].String() = vstd::findKey(bonusSourceMap, source);
|
||||
if(targetSourceType != BonusSource::OTHER)
|
||||
root["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetSourceType);
|
||||
if(sid != 0)
|
||||
root["sourceID"].Integer() = sid;
|
||||
if(val != 0)
|
||||
root["val"].Integer() = val;
|
||||
if(valType != BonusValueType::ADDITIVE_VALUE)
|
||||
root["valueType"].String() = vstd::findKey(bonusValueMap, valType);
|
||||
if(!stacking.empty())
|
||||
root["stacking"].String() = stacking;
|
||||
if(!description.empty())
|
||||
root["description"].String() = description;
|
||||
if(effectRange != BonusLimitEffect::NO_LIMIT)
|
||||
root["effectRange"].String() = vstd::findKey(bonusLimitEffect, effectRange);
|
||||
if(duration != BonusDuration::PERMANENT)
|
||||
root["duration"].String() = vstd::findKey(bonusDurationMap, duration);
|
||||
if(turnsRemain)
|
||||
root["turns"].Integer() = turnsRemain;
|
||||
if(limiter)
|
||||
root["limiters"] = limiter->toJsonNode();
|
||||
if(updater)
|
||||
root["updater"] = updater->toJsonNode();
|
||||
if(propagator)
|
||||
root["propagator"].String() = vstd::findKey(bonusPropagatorMap, propagator);
|
||||
return root;
|
||||
}
|
||||
|
||||
Bonus::Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype):
|
||||
duration(Duration),
|
||||
type(Type),
|
||||
subtype(Subtype),
|
||||
source(Src),
|
||||
val(Val),
|
||||
sid(ID),
|
||||
description(std::move(Desc))
|
||||
{
|
||||
boost::algorithm::trim(description);
|
||||
targetSourceType = BonusSource::OTHER;
|
||||
}
|
||||
|
||||
Bonus::Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, BonusValueType ValType):
|
||||
duration(Duration),
|
||||
type(Type),
|
||||
subtype(Subtype),
|
||||
source(Src),
|
||||
val(Val),
|
||||
sid(ID),
|
||||
valType(ValType)
|
||||
{
|
||||
turnsRemain = 0;
|
||||
effectRange = BonusLimitEffect::NO_LIMIT;
|
||||
targetSourceType = BonusSource::OTHER;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> Bonus::addPropagator(const TPropagatorPtr & Propagator)
|
||||
{
|
||||
propagator = Propagator;
|
||||
return this->shared_from_this();
|
||||
}
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus)
|
||||
{
|
||||
for(const auto & i : bonusNameMap)
|
||||
if(i.second == bonus.type)
|
||||
out << "\tType: " << i.first << " \t";
|
||||
|
||||
#define printField(field) out << "\t" #field ": " << (int)bonus.field << "\n"
|
||||
printField(val);
|
||||
printField(subtype);
|
||||
printField(duration);
|
||||
printField(source);
|
||||
printField(sid);
|
||||
if(bonus.additionalInfo != CAddInfo::NONE)
|
||||
out << "\taddInfo: " << bonus.additionalInfo.toString() << "\n";
|
||||
printField(turnsRemain);
|
||||
printField(valType);
|
||||
if(!bonus.stacking.empty())
|
||||
out << "\tstacking: \"" << bonus.stacking << "\"\n";
|
||||
printField(effectRange);
|
||||
#undef printField
|
||||
|
||||
if(bonus.limiter)
|
||||
out << "\tLimiter: " << bonus.limiter->toString() << "\n";
|
||||
if(bonus.updater)
|
||||
out << "\tUpdater: " << bonus.updater->toString() << "\n";
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> Bonus::addLimiter(const TLimiterPtr & Limiter)
|
||||
{
|
||||
if (limiter)
|
||||
{
|
||||
//If we already have limiter list, retrieve it
|
||||
auto limiterList = std::dynamic_pointer_cast<AllOfLimiter>(limiter);
|
||||
if(!limiterList)
|
||||
{
|
||||
//Create a new limiter list with old limiter and the new one will be pushed later
|
||||
limiterList = std::make_shared<AllOfLimiter>();
|
||||
limiterList->add(limiter);
|
||||
limiter = limiterList;
|
||||
}
|
||||
|
||||
limiterList->add(Limiter);
|
||||
}
|
||||
else
|
||||
{
|
||||
limiter = Limiter;
|
||||
}
|
||||
return this->shared_from_this();
|
||||
}
|
||||
|
||||
// Updaters
|
||||
|
||||
std::shared_ptr<Bonus> Bonus::addUpdater(const TUpdaterPtr & Updater)
|
||||
{
|
||||
updater = Updater;
|
||||
return this->shared_from_this();
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
183
lib/bonuses/Bonus.h
Normal file
183
lib/bonuses/Bonus.h
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Bonus.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 "BonusEnum.h"
|
||||
#include "../JsonNode.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
struct Bonus;
|
||||
class IBonusBearer;
|
||||
class CBonusSystemNode;
|
||||
class ILimiter;
|
||||
class IPropagator;
|
||||
class IUpdater;
|
||||
class BonusList;
|
||||
|
||||
using TBonusSubtype = int32_t;
|
||||
using TBonusListPtr = std::shared_ptr<BonusList>;
|
||||
using TConstBonusListPtr = std::shared_ptr<const BonusList>;
|
||||
using TLimiterPtr = std::shared_ptr<ILimiter>;
|
||||
using TPropagatorPtr = std::shared_ptr<IPropagator>;
|
||||
using TUpdaterPtr = std::shared_ptr<IUpdater>;
|
||||
|
||||
class DLL_LINKAGE CAddInfo : public std::vector<si32>
|
||||
{
|
||||
public:
|
||||
enum { NONE = -1 };
|
||||
|
||||
CAddInfo();
|
||||
CAddInfo(si32 value);
|
||||
|
||||
bool operator==(si32 value) const;
|
||||
bool operator!=(si32 value) const;
|
||||
|
||||
si32 & operator[](size_type pos);
|
||||
si32 operator[](size_type pos) const;
|
||||
|
||||
std::string toString() const;
|
||||
JsonNode toJsonNode() const;
|
||||
};
|
||||
|
||||
#define BONUS_TREE_DESERIALIZATION_FIX if(!h.saving && h.smartPointerSerialization) deserializationFix();
|
||||
|
||||
/// Struct for handling bonuses of several types. Can be transferred to any hero
|
||||
struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
||||
{
|
||||
BonusDuration duration = BonusDuration::PERMANENT; //uses BonusDuration values
|
||||
si16 turnsRemain = 0; //used if duration is N_TURNS, N_DAYS or ONE_WEEK
|
||||
|
||||
BonusType type = BonusType::NONE; //uses BonusType values - says to what is this bonus - 1 byte
|
||||
TBonusSubtype subtype = -1; //-1 if not applicable - 4 bytes
|
||||
|
||||
BonusSource source = BonusSource::OTHER; //source type" uses BonusSource values - what gave that bonus
|
||||
BonusSource targetSourceType;//Bonuses of what origin this amplifies, uses BonusSource values. Needed for PERCENT_TO_TARGET_TYPE.
|
||||
si32 val = 0;
|
||||
ui32 sid = 0; //source id: id of object/artifact/spell
|
||||
BonusValueType valType = BonusValueType::ADDITIVE_VALUE;
|
||||
std::string stacking; // bonuses with the same stacking value don't stack (e.g. Angel/Archangel morale bonus)
|
||||
|
||||
CAddInfo additionalInfo;
|
||||
BonusLimitEffect effectRange = BonusLimitEffect::NO_LIMIT; //if not NO_LIMIT, bonus will be omitted by default
|
||||
|
||||
TLimiterPtr limiter;
|
||||
TPropagatorPtr propagator;
|
||||
TUpdaterPtr updater;
|
||||
TUpdaterPtr propagationUpdater;
|
||||
|
||||
std::string description;
|
||||
|
||||
Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype=-1);
|
||||
Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype=-1, BonusValueType ValType = BonusValueType::ADDITIVE_VALUE);
|
||||
Bonus() = default;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & duration;
|
||||
h & type;
|
||||
h & subtype;
|
||||
h & source;
|
||||
h & val;
|
||||
h & sid;
|
||||
h & description;
|
||||
h & additionalInfo;
|
||||
h & turnsRemain;
|
||||
h & valType;
|
||||
h & stacking;
|
||||
h & effectRange;
|
||||
h & limiter;
|
||||
h & propagator;
|
||||
h & updater;
|
||||
h & propagationUpdater;
|
||||
h & targetSourceType;
|
||||
}
|
||||
|
||||
template <typename Ptr>
|
||||
static bool compareByAdditionalInfo(const Ptr& a, const Ptr& b)
|
||||
{
|
||||
return a->additionalInfo < b->additionalInfo;
|
||||
}
|
||||
static bool NDays(const Bonus *hb)
|
||||
{
|
||||
return hb->duration == BonusDuration::N_DAYS;
|
||||
}
|
||||
static bool NTurns(const Bonus *hb)
|
||||
{
|
||||
return hb->duration == BonusDuration::N_TURNS;
|
||||
}
|
||||
static bool OneDay(const Bonus *hb)
|
||||
{
|
||||
return hb->duration == BonusDuration::ONE_DAY;
|
||||
}
|
||||
static bool OneWeek(const Bonus *hb)
|
||||
{
|
||||
return hb->duration == BonusDuration::ONE_WEEK;
|
||||
}
|
||||
static bool OneBattle(const Bonus *hb)
|
||||
{
|
||||
return hb->duration == BonusDuration::ONE_BATTLE;
|
||||
}
|
||||
static bool Permanent(const Bonus *hb)
|
||||
{
|
||||
return hb->duration == BonusDuration::PERMANENT;
|
||||
}
|
||||
static bool UntilGetsTurn(const Bonus *hb)
|
||||
{
|
||||
return hb->duration == BonusDuration::STACK_GETS_TURN;
|
||||
}
|
||||
static bool UntilAttack(const Bonus *hb)
|
||||
{
|
||||
return hb->duration == BonusDuration::UNTIL_ATTACK;
|
||||
}
|
||||
static bool UntilBeingAttacked(const Bonus *hb)
|
||||
{
|
||||
return hb->duration == BonusDuration::UNTIL_BEING_ATTACKED;
|
||||
}
|
||||
static bool UntilCommanderKilled(const Bonus *hb)
|
||||
{
|
||||
return hb->duration == BonusDuration::COMMANDER_KILLED;
|
||||
}
|
||||
inline bool operator == (const BonusType & cf) const
|
||||
{
|
||||
return type == cf;
|
||||
}
|
||||
inline void operator += (const ui32 Val) //no return
|
||||
{
|
||||
val += Val;
|
||||
}
|
||||
STRONG_INLINE static ui32 getSid32(ui32 high, ui32 low)
|
||||
{
|
||||
return (high << 16) + low;
|
||||
}
|
||||
|
||||
STRONG_INLINE static ui32 getHighFromSid32(ui32 sid)
|
||||
{
|
||||
return sid >> 16;
|
||||
}
|
||||
|
||||
STRONG_INLINE static ui32 getLowFromSid32(ui32 sid)
|
||||
{
|
||||
return sid & 0x0000FFFF;
|
||||
}
|
||||
|
||||
std::string Description(std::optional<si32> customValue = {}) const;
|
||||
JsonNode toJsonNode() const;
|
||||
|
||||
std::shared_ptr<Bonus> addLimiter(const TLimiterPtr & Limiter); //returns this for convenient chain-calls
|
||||
std::shared_ptr<Bonus> addPropagator(const TPropagatorPtr & Propagator); //returns this for convenient chain-calls
|
||||
std::shared_ptr<Bonus> addUpdater(const TUpdaterPtr & Updater); //returns this for convenient chain-calls
|
||||
};
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus);
|
||||
|
||||
extern DLL_LINKAGE const std::set<std::string> deprecatedBonusSet;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
58
lib/bonuses/BonusEnum.cpp
Normal file
58
lib/bonuses/BonusEnum.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* BonusEnum.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 "BonusEnum.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
#define BONUS_NAME(x) { #x, BonusType::x },
|
||||
const std::map<std::string, BonusType> bonusNameMap = {
|
||||
BONUS_LIST
|
||||
};
|
||||
#undef BONUS_NAME
|
||||
|
||||
#define BONUS_VALUE(x) { #x, BonusValueType::x },
|
||||
const std::map<std::string, BonusValueType> bonusValueMap = { BONUS_VALUE_LIST };
|
||||
#undef BONUS_VALUE
|
||||
|
||||
#define BONUS_SOURCE(x) { #x, BonusSource::x },
|
||||
const std::map<std::string, BonusSource> bonusSourceMap = { BONUS_SOURCE_LIST };
|
||||
#undef BONUS_SOURCE
|
||||
|
||||
#define BONUS_ITEM(x) { #x, BonusDuration::x },
|
||||
const std::map<std::string, BonusDuration> bonusDurationMap =
|
||||
{
|
||||
BONUS_ITEM(PERMANENT)
|
||||
BONUS_ITEM(ONE_BATTLE)
|
||||
BONUS_ITEM(ONE_DAY)
|
||||
BONUS_ITEM(ONE_WEEK)
|
||||
BONUS_ITEM(N_TURNS)
|
||||
BONUS_ITEM(N_DAYS)
|
||||
BONUS_ITEM(UNTIL_BEING_ATTACKED)
|
||||
BONUS_ITEM(UNTIL_ATTACK)
|
||||
BONUS_ITEM(STACK_GETS_TURN)
|
||||
BONUS_ITEM(COMMANDER_KILLED)
|
||||
{ "UNITL_BEING_ATTACKED", BonusDuration::UNTIL_BEING_ATTACKED }//typo, but used in some mods
|
||||
};
|
||||
#undef BONUS_ITEM
|
||||
|
||||
#define BONUS_ITEM(x) { #x, BonusLimitEffect::x },
|
||||
const std::map<std::string, BonusLimitEffect> bonusLimitEffect =
|
||||
{
|
||||
BONUS_ITEM(NO_LIMIT)
|
||||
BONUS_ITEM(ONLY_DISTANCE_FIGHT)
|
||||
BONUS_ITEM(ONLY_MELEE_FIGHT)
|
||||
};
|
||||
#undef BONUS_ITEM
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
264
lib/bonuses/BonusEnum.h
Normal file
264
lib/bonuses/BonusEnum.h
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* BonusEnum.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
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
#define BONUS_LIST \
|
||||
BONUS_NAME(NONE) \
|
||||
BONUS_NAME(LEVEL_COUNTER) /* for commander artifacts*/ \
|
||||
BONUS_NAME(MOVEMENT) /*Subtype is 1 - land, 0 - sea*/ \
|
||||
BONUS_NAME(MORALE) \
|
||||
BONUS_NAME(LUCK) \
|
||||
BONUS_NAME(PRIMARY_SKILL) /*uses subtype to pick skill; additional info if set: 1 - only melee, 2 - only distance*/ \
|
||||
BONUS_NAME(SIGHT_RADIUS) \
|
||||
BONUS_NAME(MANA_REGENERATION) /*points per turn apart from normal (1 + mysticism)*/ \
|
||||
BONUS_NAME(FULL_MANA_REGENERATION) /*all mana points are replenished every day*/ \
|
||||
BONUS_NAME(NONEVIL_ALIGNMENT_MIX) /*good and neutral creatures can be mixed without morale penalty*/ \
|
||||
BONUS_NAME(SURRENDER_DISCOUNT) /*%*/ \
|
||||
BONUS_NAME(STACKS_SPEED) /*additional info - percent of speed bonus applied after direct bonuses; >0 - added, <0 - subtracted to this part*/ \
|
||||
BONUS_NAME(FLYING_MOVEMENT) /*value - penalty percentage*/ \
|
||||
BONUS_NAME(SPELL_DURATION) \
|
||||
BONUS_NAME(AIR_SPELL_DMG_PREMY) \
|
||||
BONUS_NAME(EARTH_SPELL_DMG_PREMY) \
|
||||
BONUS_NAME(FIRE_SPELL_DMG_PREMY) \
|
||||
BONUS_NAME(WATER_SPELL_DMG_PREMY) \
|
||||
BONUS_NAME(WATER_WALKING) /*value - penalty percentage*/ \
|
||||
BONUS_NAME(NEGATE_ALL_NATURAL_IMMUNITIES) \
|
||||
BONUS_NAME(STACK_HEALTH) \
|
||||
BONUS_NAME(FIRE_SPELLS) \
|
||||
BONUS_NAME(AIR_SPELLS) \
|
||||
BONUS_NAME(WATER_SPELLS) \
|
||||
BONUS_NAME(EARTH_SPELLS) \
|
||||
BONUS_NAME(GENERATE_RESOURCE) /*daily value, uses subtype (resource type)*/ \
|
||||
BONUS_NAME(CREATURE_GROWTH) /*for legion artifacts: value - week growth bonus, subtype - monster level if aplicable*/ \
|
||||
BONUS_NAME(WHIRLPOOL_PROTECTION) /*hero won't lose army when teleporting through whirlpool*/ \
|
||||
BONUS_NAME(SPELL) /*hero knows spell, val - skill level (0 - 3), subtype - spell id*/ \
|
||||
BONUS_NAME(SPELLS_OF_LEVEL) /*hero knows all spells of given level, val - skill level; subtype - level*/ \
|
||||
BONUS_NAME(BATTLE_NO_FLEEING) /*for shackles of war*/ \
|
||||
BONUS_NAME(MAGIC_SCHOOL_SKILL) /* //eg. for magic plains terrain, subtype: school of magic (0 - all, 1 - fire, 2 - air, 4 - water, 8 - earth), value - level*/ \
|
||||
BONUS_NAME(FREE_SHOOTING) /*stacks can shoot even if otherwise blocked (sharpshooter's bow effect)*/ \
|
||||
BONUS_NAME(OPENING_BATTLE_SPELL) /*casts a spell at expert level at beginning of battle, val - spell power, subtype - spell id*/ \
|
||||
BONUS_NAME(IMPROVED_NECROMANCY) /* raise more powerful creatures: subtype - creature type raised, addInfo - [required necromancy level, required stack level], val - necromancy level for this purpose */ \
|
||||
BONUS_NAME(CREATURE_GROWTH_PERCENT) /*increases growth of all units in all towns, val - percentage*/ \
|
||||
BONUS_NAME(FREE_SHIP_BOARDING) /*movement points preserved with ship boarding and landing*/ \
|
||||
BONUS_NAME(FLYING) \
|
||||
BONUS_NAME(SHOOTER) \
|
||||
BONUS_NAME(CHARGE_IMMUNITY) \
|
||||
BONUS_NAME(ADDITIONAL_ATTACK) \
|
||||
BONUS_NAME(UNLIMITED_RETALIATIONS) \
|
||||
BONUS_NAME(NO_MELEE_PENALTY) \
|
||||
BONUS_NAME(JOUSTING) /*for champions*/ \
|
||||
BONUS_NAME(HATE) /*eg. angels hate devils, subtype - ID of hated creature, val - damage bonus percent */ \
|
||||
BONUS_NAME(KING) /* val - required slayer bonus val to affect */\
|
||||
BONUS_NAME(MAGIC_RESISTANCE) /*in % (value)*/ \
|
||||
BONUS_NAME(CHANGES_SPELL_COST_FOR_ALLY) /*in mana points (value) , eg. mage*/ \
|
||||
BONUS_NAME(CHANGES_SPELL_COST_FOR_ENEMY) /*in mana points (value) , eg. pegasus */ \
|
||||
BONUS_NAME(SPELL_AFTER_ATTACK) /* subtype - spell id, value - chance %, addInfo[0] - level, addInfo[1] -> [0 - all attacks, 1 - shot only, 2 - melee only] */ \
|
||||
BONUS_NAME(SPELL_BEFORE_ATTACK) /* subtype - spell id, value - chance %, addInfo[0] - level, addInfo[1] -> [0 - all attacks, 1 - shot only, 2 - melee only] */ \
|
||||
BONUS_NAME(SPELL_RESISTANCE_AURA) /*eg. unicorns, value - resistance bonus in % for adjacent creatures*/ \
|
||||
BONUS_NAME(LEVEL_SPELL_IMMUNITY) /*creature is immune to all spell with level below or equal to value of this bonus */ \
|
||||
BONUS_NAME(BLOCK_MAGIC_ABOVE) /*blocks casting spells of the level > value */ \
|
||||
BONUS_NAME(BLOCK_ALL_MAGIC) /*blocks casting spells*/ \
|
||||
BONUS_NAME(TWO_HEX_ATTACK_BREATH) /*eg. dragons*/ \
|
||||
BONUS_NAME(SPELL_DAMAGE_REDUCTION) /*eg. golems; value - reduction in %, subtype - spell school; -1 - all, 0 - air, 1 - fire, 2 - water, 3 - earth*/ \
|
||||
BONUS_NAME(NO_WALL_PENALTY) \
|
||||
BONUS_NAME(NON_LIVING) /*eg. golems, cannot be rised or healed, only neutral morale */ \
|
||||
BONUS_NAME(RANDOM_SPELLCASTER) /*eg. master genie, val - level*/ \
|
||||
BONUS_NAME(BLOCKS_RETALIATION) /*eg. naga*/ \
|
||||
BONUS_NAME(SPELL_IMMUNITY) /*subid - spell id*/ \
|
||||
BONUS_NAME(MANA_CHANNELING) /*value in %, eg. familiar*/ \
|
||||
BONUS_NAME(SPELL_LIKE_ATTACK) /*subtype - spell, value - spell level; range is taken from spell, but damage from creature; eg. magog*/ \
|
||||
BONUS_NAME(THREE_HEADED_ATTACK) /*eg. cerberus*/ \
|
||||
BONUS_NAME(GENERAL_DAMAGE_PREMY) \
|
||||
BONUS_NAME(FIRE_IMMUNITY) /*subtype 0 - all, 1 - all except positive, 2 - only damage spells*/ \
|
||||
BONUS_NAME(WATER_IMMUNITY) \
|
||||
BONUS_NAME(EARTH_IMMUNITY) \
|
||||
BONUS_NAME(AIR_IMMUNITY) \
|
||||
BONUS_NAME(MIND_IMMUNITY) \
|
||||
BONUS_NAME(FIRE_SHIELD) \
|
||||
BONUS_NAME(UNDEAD) \
|
||||
BONUS_NAME(HP_REGENERATION) /*creature regenerates val HP every new round*/ \
|
||||
BONUS_NAME(MANA_DRAIN) /*value - spell points per turn*/ \
|
||||
BONUS_NAME(LIFE_DRAIN) \
|
||||
BONUS_NAME(DOUBLE_DAMAGE_CHANCE) /*value in %, eg. dread knight*/ \
|
||||
BONUS_NAME(RETURN_AFTER_STRIKE) \
|
||||
BONUS_NAME(SPELLCASTER) /*subtype - spell id, value - level of school, additional info - weighted chance. use SPECIFIC_SPELL_POWER, CREATURE_SPELL_POWER or CREATURE_ENCHANT_POWER for calculating the power*/ \
|
||||
BONUS_NAME(CATAPULT) \
|
||||
BONUS_NAME(ENEMY_DEFENCE_REDUCTION) /*in % (value) eg. behemots*/ \
|
||||
BONUS_NAME(GENERAL_DAMAGE_REDUCTION) /* shield / air shield effect, also armorer skill/petrify effect for subtype -1*/ \
|
||||
BONUS_NAME(GENERAL_ATTACK_REDUCTION) /*eg. while stoned or blinded - in %,// subtype not used, use ONLY_MELEE_FIGHT / DISTANCE_FIGHT*/ \
|
||||
BONUS_NAME(DEFENSIVE_STANCE) /* val - bonus to defense while defending */ \
|
||||
BONUS_NAME(ATTACKS_ALL_ADJACENT) /*eg. hydra*/ \
|
||||
BONUS_NAME(MORE_DAMAGE_FROM_SPELL) /*value - damage increase in %, subtype - spell id*/ \
|
||||
BONUS_NAME(FEAR) \
|
||||
BONUS_NAME(FEARLESS) \
|
||||
BONUS_NAME(NO_DISTANCE_PENALTY) \
|
||||
BONUS_NAME(ENCHANTER)/* for Enchanter spells, val - skill level, subtype - spell id, additionalInfo - cooldown */ \
|
||||
BONUS_NAME(HEALER) \
|
||||
BONUS_NAME(SIEGE_WEAPON) \
|
||||
BONUS_NAME(HYPNOTIZED) \
|
||||
BONUS_NAME(NO_RETALIATION) /*temporary bonus for basilisk, unicorn and scorpicore paralyze*/\
|
||||
BONUS_NAME(ADDITIONAL_RETALIATION) /*value - number of additional retaliations*/ \
|
||||
BONUS_NAME(MAGIC_MIRROR) /* value - chance of redirecting in %*/ \
|
||||
BONUS_NAME(ALWAYS_MINIMUM_DAMAGE) /*unit does its minimum damage from range; subtype: -1 - any attack, 0 - melee, 1 - ranged, value: additional damage penalty (it'll subtracted from dmg), additional info - multiplicative anti-bonus for dmg in % [eg 20 means that creature will inflict 80% of normal minimal dmg]*/ \
|
||||
BONUS_NAME(ALWAYS_MAXIMUM_DAMAGE) /*eg. bless effect, subtype: -1 - any attack, 0 - melee, 1 - ranged, value: additional damage, additional info - multiplicative bonus for dmg in %*/ \
|
||||
BONUS_NAME(ATTACKS_NEAREST_CREATURE) /*while in berserk*/ \
|
||||
BONUS_NAME(IN_FRENZY) /*value - level*/ \
|
||||
BONUS_NAME(SLAYER) /*value - level*/ \
|
||||
BONUS_NAME(FORGETFULL) /*forgetfulness spell effect, value - level*/ \
|
||||
BONUS_NAME(NOT_ACTIVE) /* subtype - spell ID (paralyze, blind, stone gaze) for graphical effect*/ \
|
||||
BONUS_NAME(NO_LUCK) /*eg. when fighting on cursed ground*/ \
|
||||
BONUS_NAME(NO_MORALE) /*eg. when fighting on cursed ground*/ \
|
||||
BONUS_NAME(DARKNESS) /*val = radius */ \
|
||||
BONUS_NAME(SPECIAL_SPELL_LEV) /*subtype = id, val = value per level in percent*/\
|
||||
BONUS_NAME(SPELL_DAMAGE) /*val = value, now works for sorcery*/\
|
||||
BONUS_NAME(SPECIFIC_SPELL_DAMAGE) /*subtype = id of spell, val = value*/\
|
||||
BONUS_NAME(SPECIAL_PECULIAR_ENCHANT) /*blesses and curses with id = val dependent on unit's level, subtype = 0 or 1 for Coronius*/\
|
||||
BONUS_NAME(SPECIAL_UPGRADE) /*subtype = base, additionalInfo = target */\
|
||||
BONUS_NAME(DRAGON_NATURE) \
|
||||
BONUS_NAME(CREATURE_DAMAGE)/*subtype 0 = both, 1 = min, 2 = max*/\
|
||||
BONUS_NAME(EXP_MULTIPLIER)/* val - percent of additional exp gained by stack/commander (base value 100)*/\
|
||||
BONUS_NAME(SHOTS)\
|
||||
BONUS_NAME(DEATH_STARE) /*subtype 0 - gorgon, 1 - commander*/\
|
||||
BONUS_NAME(POISON) /*val - max health penalty from poison possible*/\
|
||||
BONUS_NAME(BIND_EFFECT) /*doesn't do anything particular, works as a marker)*/\
|
||||
BONUS_NAME(ACID_BREATH) /*additional val damage per creature after attack, additional info - chance in percent*/\
|
||||
BONUS_NAME(RECEPTIVE) /*accepts friendly spells even with immunity*/\
|
||||
BONUS_NAME(DIRECT_DAMAGE_IMMUNITY) /*direct damage spells, that is*/\
|
||||
BONUS_NAME(CASTS) /*how many times creature can cast activated spell*/ \
|
||||
BONUS_NAME(SPECIFIC_SPELL_POWER) /* value used for Thunderbolt and Resurrection cast by units, subtype - spell id */\
|
||||
BONUS_NAME(CREATURE_SPELL_POWER) /* value per unit, divided by 100 (so faerie Dragons have 800)*/ \
|
||||
BONUS_NAME(CREATURE_ENCHANT_POWER) /* total duration of spells cast by creature */ \
|
||||
BONUS_NAME(ENCHANTED) /* permanently enchanted with spell subID of level = val, if val > 3 then spell is mass and has level of val-3*/ \
|
||||
BONUS_NAME(REBIRTH) /* val - percent of life restored, subtype = 0 - regular, 1 - at least one unit (sacred Phoenix) */\
|
||||
BONUS_NAME(ADDITIONAL_UNITS) /*val of units with id = subtype will be added to hero's army at the beginning of battle */\
|
||||
BONUS_NAME(SPOILS_OF_WAR) /*val * 10^-6 * gained exp resources of subtype will be given to hero after battle*/\
|
||||
BONUS_NAME(BLOCK)\
|
||||
BONUS_NAME(DISGUISED) /* subtype - spell level */\
|
||||
BONUS_NAME(VISIONS) /* subtype - spell level */\
|
||||
BONUS_NAME(NO_TERRAIN_PENALTY) /* subtype - terrain type */\
|
||||
BONUS_NAME(SOUL_STEAL) /*val - number of units gained per enemy killed, subtype = 0 - gained units survive after battle, 1 - they do not*/ \
|
||||
BONUS_NAME(TRANSMUTATION) /*val - chance to trigger in %, subtype = 0 - resurrection based on HP, 1 - based on unit count, additional info - target creature ID (attacker default)*/\
|
||||
BONUS_NAME(SUMMON_GUARDIANS) /*val - amount in % of stack count, subtype = creature ID*/\
|
||||
BONUS_NAME(CATAPULT_EXTRA_SHOTS) /*val - power of catapult effect, requires CATAPULT bonus to work*/\
|
||||
BONUS_NAME(RANGED_RETALIATION) /*allows shooters to perform ranged retaliation*/\
|
||||
BONUS_NAME(BLOCKS_RANGED_RETALIATION) /*disallows ranged retaliation for shooter unit, BLOCKS_RETALIATION bonus is for melee retaliation only*/\
|
||||
BONUS_NAME(MANUAL_CONTROL) /* manually control warmachine with id = subtype, chance = val */ \
|
||||
BONUS_NAME(WIDE_BREATH) /* initial desigh: dragon breath affecting multiple nearby hexes */\
|
||||
BONUS_NAME(FIRST_STRIKE) /* first counterattack, then attack if possible */\
|
||||
BONUS_NAME(SYNERGY_TARGET) /* dummy skill for alternative upgrades mod */\
|
||||
BONUS_NAME(SHOOTS_ALL_ADJACENT) /* H4 Cyclops-like shoot (attacks all hexes neighboring with target) without spell-like mechanics */\
|
||||
BONUS_NAME(BLOCK_MAGIC_BELOW) /*blocks casting spells of the level < value */ \
|
||||
BONUS_NAME(DESTRUCTION) /*kills extra units after hit, subtype = 0 - kill percentage of units, 1 - kill amount, val = chance in percent to trigger, additional info - amount/percentage to kill*/ \
|
||||
BONUS_NAME(SPECIAL_CRYSTAL_GENERATION) /*crystal dragon crystal generation*/ \
|
||||
BONUS_NAME(NO_SPELLCAST_BY_DEFAULT) /*spellcast will not be default attack option for this creature*/ \
|
||||
BONUS_NAME(GARGOYLE) /* gargoyle is special than NON_LIVING, cannot be rised or healed */ \
|
||||
BONUS_NAME(SPECIAL_ADD_VALUE_ENCHANT) /*specialty spell like Aenin has, increased effect of spell, additionalInfo = value to add*/\
|
||||
BONUS_NAME(SPECIAL_FIXED_VALUE_ENCHANT) /*specialty spell like Melody has, constant spell effect (i.e. 3 luck), additionalInfo = value to fix.*/\
|
||||
BONUS_NAME(TOWN_MAGIC_WELL) /*one-time pseudo-bonus to implement Magic Well in the town*/\
|
||||
BONUS_NAME(LIMITED_SHOOTING_RANGE) /*limits range of shooting creatures, doesn't adjust any other mechanics (half vs full damage etc). val - range in hexes, additional info - optional new range for broken arrow mechanic */\
|
||||
BONUS_NAME(LEARN_BATTLE_SPELL_CHANCE) /*skill-agnostic eagle eye chance. subtype = 0 - from enemy, 1 - TODO: from entire battlefield*/\
|
||||
BONUS_NAME(LEARN_BATTLE_SPELL_LEVEL_LIMIT) /*skill-agnostic eagle eye limit, subtype - school (-1 for all), others TODO*/\
|
||||
BONUS_NAME(PERCENTAGE_DAMAGE_BOOST) /*skill-agnostic archery and offence, subtype is 0 for offence and 1 for archery*/\
|
||||
BONUS_NAME(LEARN_MEETING_SPELL_LIMIT) /*skill-agnostic scholar, subtype is -1 for all, TODO for others (> 0)*/\
|
||||
BONUS_NAME(ROUGH_TERRAIN_DISCOUNT) /*skill-agnostic pathfinding*/\
|
||||
BONUS_NAME(WANDERING_CREATURES_JOIN_BONUS) /*skill-agnostic diplomacy*/\
|
||||
BONUS_NAME(BEFORE_BATTLE_REPOSITION) /*skill-agnostic tactics, bonus for allowing tactics*/\
|
||||
BONUS_NAME(BEFORE_BATTLE_REPOSITION_BLOCK) /*skill-agnostic tactics, bonus for blocking opposite tactics. For now donble side tactics is TODO.*/\
|
||||
BONUS_NAME(HERO_EXPERIENCE_GAIN_PERCENT) /*skill-agnostic learning, and we can use it as a global effect also*/\
|
||||
BONUS_NAME(UNDEAD_RAISE_PERCENTAGE) /*Percentage of killed enemy creatures to be raised after battle as undead*/\
|
||||
BONUS_NAME(MANA_PER_KNOWLEDGE) /*Percentage rate of translating 10 hero knowledge to mana, used to intelligence and global bonus*/\
|
||||
BONUS_NAME(HERO_GRANTS_ATTACKS) /*If hero can grant additional attacks to creature, value is number of attacks, subtype is creatureID*/\
|
||||
BONUS_NAME(BONUS_DAMAGE_PERCENTAGE) /*If hero can grant conditional damage to creature, value is percentage, subtype is creatureID*/\
|
||||
BONUS_NAME(BONUS_DAMAGE_CHANCE) /*If hero can grant additional damage to creature, value is chance, subtype is creatureID*/\
|
||||
BONUS_NAME(MAX_LEARNABLE_SPELL_LEVEL) /*This can work as wisdom before. val = max learnable spell level*/\
|
||||
/* end of list */
|
||||
|
||||
|
||||
#define BONUS_SOURCE_LIST \
|
||||
BONUS_SOURCE(ARTIFACT)\
|
||||
BONUS_SOURCE(ARTIFACT_INSTANCE)\
|
||||
BONUS_SOURCE(OBJECT)\
|
||||
BONUS_SOURCE(CREATURE_ABILITY)\
|
||||
BONUS_SOURCE(TERRAIN_NATIVE)\
|
||||
BONUS_SOURCE(TERRAIN_OVERLAY)\
|
||||
BONUS_SOURCE(SPELL_EFFECT)\
|
||||
BONUS_SOURCE(TOWN_STRUCTURE)\
|
||||
BONUS_SOURCE(HERO_BASE_SKILL)\
|
||||
BONUS_SOURCE(SECONDARY_SKILL)\
|
||||
BONUS_SOURCE(HERO_SPECIAL)\
|
||||
BONUS_SOURCE(ARMY)\
|
||||
BONUS_SOURCE(CAMPAIGN_BONUS)\
|
||||
BONUS_SOURCE(SPECIAL_WEEK)\
|
||||
BONUS_SOURCE(STACK_EXPERIENCE)\
|
||||
BONUS_SOURCE(COMMANDER) /*TODO: consider using simply STACK_INSTANCE */\
|
||||
BONUS_SOURCE(GLOBAL) /*used for base bonuses which all heroes or all stacks should have*/\
|
||||
BONUS_SOURCE(OTHER) /*used for defensive stance and default value of spell level limit*/
|
||||
|
||||
#define BONUS_VALUE_LIST \
|
||||
BONUS_VALUE(ADDITIVE_VALUE)\
|
||||
BONUS_VALUE(BASE_NUMBER)\
|
||||
BONUS_VALUE(PERCENT_TO_ALL)\
|
||||
BONUS_VALUE(PERCENT_TO_BASE)\
|
||||
BONUS_VALUE(PERCENT_TO_SOURCE) /*Adds value only to bonuses with same source*/\
|
||||
BONUS_VALUE(PERCENT_TO_TARGET_TYPE) /*Adds value only to bonuses with SourceType target*/\
|
||||
BONUS_VALUE(INDEPENDENT_MAX) /*used for SPELL bonus */\
|
||||
BONUS_VALUE(INDEPENDENT_MIN) //used for SECONDARY_SKILL_PREMY bonus
|
||||
|
||||
|
||||
enum class BonusType
|
||||
{
|
||||
#define BONUS_NAME(x) x,
|
||||
BONUS_LIST
|
||||
#undef BONUS_NAME
|
||||
};
|
||||
enum class BonusDuration : uint16_t //when bonus is automatically removed
|
||||
{
|
||||
PERMANENT = 1,
|
||||
ONE_BATTLE = 2, //at the end of battle
|
||||
ONE_DAY = 4, //at the end of day
|
||||
ONE_WEEK = 8, //at the end of week (bonus lasts till the end of week, thats NOT 7 days
|
||||
N_TURNS = 16, //used during battles, after battle bonus is always removed
|
||||
N_DAYS = 32,
|
||||
UNTIL_BEING_ATTACKED = 64, /*removed after attack and counterattacks are performed*/
|
||||
UNTIL_ATTACK = 128, /*removed after attack and counterattacks are performed*/
|
||||
STACK_GETS_TURN = 256, /*removed when stack gets its turn - used for defensive stance*/
|
||||
COMMANDER_KILLED = 512
|
||||
};
|
||||
enum class BonusSource
|
||||
{
|
||||
#define BONUS_SOURCE(x) x,
|
||||
BONUS_SOURCE_LIST
|
||||
#undef BONUS_SOURCE
|
||||
NUM_BONUS_SOURCE /*This is a dummy value, which will be always last*/
|
||||
};
|
||||
|
||||
enum class BonusLimitEffect
|
||||
{
|
||||
NO_LIMIT = 0,
|
||||
ONLY_DISTANCE_FIGHT=1, ONLY_MELEE_FIGHT, //used to mark bonuses for attack/defense primary skills from spells like Precision (distance only)
|
||||
};
|
||||
|
||||
enum class BonusValueType
|
||||
{
|
||||
#define BONUS_VALUE(x) x,
|
||||
BONUS_VALUE_LIST
|
||||
#undef BONUS_VALUE
|
||||
};
|
||||
|
||||
extern DLL_LINKAGE const std::map<std::string, BonusType> bonusNameMap;
|
||||
extern DLL_LINKAGE const std::map<std::string, BonusValueType> bonusValueMap;
|
||||
extern DLL_LINKAGE const std::map<std::string, BonusSource> bonusSourceMap;
|
||||
extern DLL_LINKAGE const std::map<std::string, BonusDuration> bonusDurationMap;
|
||||
extern DLL_LINKAGE const std::map<std::string, BonusLimitEffect> bonusLimitEffect;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
276
lib/bonuses/BonusList.cpp
Normal file
276
lib/bonuses/BonusList.cpp
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* BonusList.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 "CBonusSystemNode.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
BonusList::BonusList(bool BelongsToTree) : belongsToTree(BelongsToTree)
|
||||
{
|
||||
}
|
||||
|
||||
BonusList::BonusList(const BonusList & bonusList): belongsToTree(false)
|
||||
{
|
||||
bonuses.resize(bonusList.size());
|
||||
std::copy(bonusList.begin(), bonusList.end(), bonuses.begin());
|
||||
}
|
||||
|
||||
BonusList::BonusList(BonusList && other) noexcept: belongsToTree(false)
|
||||
{
|
||||
std::swap(belongsToTree, other.belongsToTree);
|
||||
std::swap(bonuses, other.bonuses);
|
||||
}
|
||||
|
||||
BonusList& BonusList::operator=(const BonusList &bonusList)
|
||||
{
|
||||
bonuses.resize(bonusList.size());
|
||||
std::copy(bonusList.begin(), bonusList.end(), bonuses.begin());
|
||||
belongsToTree = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void BonusList::changed() const
|
||||
{
|
||||
if(belongsToTree)
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
|
||||
void BonusList::stackBonuses()
|
||||
{
|
||||
boost::sort(bonuses, [](const std::shared_ptr<Bonus> & b1, const std::shared_ptr<Bonus> & b2) -> bool
|
||||
{
|
||||
if(b1 == b2)
|
||||
return false;
|
||||
#define COMPARE_ATT(ATT) if(b1->ATT != b2->ATT) return b1->ATT < b2->ATT
|
||||
COMPARE_ATT(stacking);
|
||||
COMPARE_ATT(type);
|
||||
COMPARE_ATT(subtype);
|
||||
COMPARE_ATT(valType);
|
||||
#undef COMPARE_ATT
|
||||
return b1->val > b2->val;
|
||||
});
|
||||
// remove non-stacking
|
||||
size_t next = 1;
|
||||
while(next < bonuses.size())
|
||||
{
|
||||
bool remove = false;
|
||||
std::shared_ptr<Bonus> last = bonuses[next-1];
|
||||
std::shared_ptr<Bonus> current = bonuses[next];
|
||||
|
||||
if(current->stacking.empty())
|
||||
remove = current == last;
|
||||
else if(current->stacking == "ALWAYS")
|
||||
remove = false;
|
||||
else
|
||||
remove = current->stacking == last->stacking
|
||||
&& current->type == last->type
|
||||
&& current->subtype == last->subtype
|
||||
&& current->valType == last->valType;
|
||||
|
||||
if(remove)
|
||||
bonuses.erase(bonuses.begin() + next);
|
||||
else
|
||||
next++;
|
||||
}
|
||||
}
|
||||
|
||||
int BonusList::totalValue() const
|
||||
{
|
||||
struct BonusCollection
|
||||
{
|
||||
int base = 0;
|
||||
int percentToBase = 0;
|
||||
int percentToAll = 0;
|
||||
int additive = 0;
|
||||
int percentToSource = 0;
|
||||
int indepMin = std::numeric_limits<int>::max();
|
||||
int indepMax = std::numeric_limits<int>::min();
|
||||
};
|
||||
|
||||
auto percent = [](int64_t base, int64_t percent) -> int {
|
||||
return static_cast<int>(std::clamp<int64_t>((base * (100 + percent)) / 100, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()));
|
||||
};
|
||||
std::array <BonusCollection, vstd::to_underlying(BonusSource::NUM_BONUS_SOURCE)> sources = {};
|
||||
BonusCollection any;
|
||||
bool hasIndepMax = false;
|
||||
bool hasIndepMin = false;
|
||||
|
||||
for(const auto & b : bonuses)
|
||||
{
|
||||
switch(b->valType)
|
||||
{
|
||||
case BonusValueType::BASE_NUMBER:
|
||||
sources[vstd::to_underlying(b->source)].base += b->val;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_ALL:
|
||||
sources[vstd::to_underlying(b->source)].percentToAll += b->val;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_BASE:
|
||||
sources[vstd::to_underlying(b->source)].percentToBase += b->val;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_SOURCE:
|
||||
sources[vstd::to_underlying(b->source)].percentToSource += b->val;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_TARGET_TYPE:
|
||||
sources[vstd::to_underlying(b->targetSourceType)].percentToSource += b->val;
|
||||
break;
|
||||
case BonusValueType::ADDITIVE_VALUE:
|
||||
sources[vstd::to_underlying(b->source)].additive += b->val;
|
||||
break;
|
||||
case BonusValueType::INDEPENDENT_MAX:
|
||||
hasIndepMax = true;
|
||||
vstd::amax(sources[vstd::to_underlying(b->source)].indepMax, b->val);
|
||||
break;
|
||||
case BonusValueType::INDEPENDENT_MIN:
|
||||
hasIndepMin = true;
|
||||
vstd::amin(sources[vstd::to_underlying(b->source)].indepMin, b->val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(const auto & src : sources)
|
||||
{
|
||||
any.base += percent(src.base, src.percentToSource);
|
||||
any.percentToBase += percent(src.percentToBase, src.percentToSource);
|
||||
any.percentToAll += percent(src.percentToAll, src.percentToSource);
|
||||
any.additive += percent(src.additive, src.percentToSource);
|
||||
if(hasIndepMin)
|
||||
vstd::amin(any.indepMin, percent(src.indepMin, src.percentToSource));
|
||||
if(hasIndepMax)
|
||||
vstd::amax(any.indepMax, percent(src.indepMax, src.percentToSource));
|
||||
}
|
||||
any.base = percent(any.base, any.percentToBase);
|
||||
any.base += any.additive;
|
||||
auto valFirst = percent(any.base ,any.percentToAll);
|
||||
|
||||
if(hasIndepMin && hasIndepMax && any.indepMin < any.indepMax)
|
||||
any.indepMax = any.indepMin;
|
||||
|
||||
const int notIndepBonuses = static_cast<int>(std::count_if(bonuses.cbegin(), bonuses.cend(), [](const std::shared_ptr<Bonus>& b)
|
||||
{
|
||||
return b->valType != BonusValueType::INDEPENDENT_MAX && b->valType != BonusValueType::INDEPENDENT_MIN;
|
||||
}));
|
||||
|
||||
if(notIndepBonuses)
|
||||
return std::clamp(valFirst, any.indepMax, any.indepMin);
|
||||
|
||||
return hasIndepMin ? any.indepMin : hasIndepMax ? any.indepMax : 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> BonusList::getFirst(const CSelector &select)
|
||||
{
|
||||
for (auto & b : bonuses)
|
||||
{
|
||||
if(select(b.get()))
|
||||
return b;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<const Bonus> BonusList::getFirst(const CSelector &selector) const
|
||||
{
|
||||
for(const auto & b : bonuses)
|
||||
{
|
||||
if(selector(b.get()))
|
||||
return b;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BonusList::getBonuses(BonusList & out, const CSelector &selector, const CSelector &limit) const
|
||||
{
|
||||
out.reserve(bonuses.size());
|
||||
for(const auto & b : bonuses)
|
||||
{
|
||||
//add matching bonuses that matches limit predicate or have NO_LIMIT if no given predicate
|
||||
auto noFightLimit = b->effectRange == BonusLimitEffect::NO_LIMIT;
|
||||
if(selector(b.get()) && ((!limit && noFightLimit) || ((bool)limit && limit(b.get()))))
|
||||
out.push_back(b);
|
||||
}
|
||||
}
|
||||
|
||||
void BonusList::getAllBonuses(BonusList &out) const
|
||||
{
|
||||
for(const auto & b : bonuses)
|
||||
out.push_back(b);
|
||||
}
|
||||
|
||||
int BonusList::valOfBonuses(const CSelector &select) const
|
||||
{
|
||||
BonusList ret;
|
||||
CSelector limit = nullptr;
|
||||
getBonuses(ret, select, limit);
|
||||
return ret.totalValue();
|
||||
}
|
||||
|
||||
JsonNode BonusList::toJsonNode() const
|
||||
{
|
||||
JsonNode node(JsonNode::JsonType::DATA_VECTOR);
|
||||
for(const std::shared_ptr<Bonus> & b : bonuses)
|
||||
node.Vector().push_back(b->toJsonNode());
|
||||
return node;
|
||||
}
|
||||
|
||||
void BonusList::push_back(const std::shared_ptr<Bonus> & x)
|
||||
{
|
||||
bonuses.push_back(x);
|
||||
changed();
|
||||
}
|
||||
|
||||
BonusList::TInternalContainer::iterator BonusList::erase(const int position)
|
||||
{
|
||||
changed();
|
||||
return bonuses.erase(bonuses.begin() + position);
|
||||
}
|
||||
|
||||
void BonusList::clear()
|
||||
{
|
||||
bonuses.clear();
|
||||
changed();
|
||||
}
|
||||
|
||||
std::vector<BonusList *>::size_type BonusList::operator-=(const std::shared_ptr<Bonus> & i)
|
||||
{
|
||||
auto itr = std::find(bonuses.begin(), bonuses.end(), i);
|
||||
if(itr == bonuses.end())
|
||||
return false;
|
||||
bonuses.erase(itr);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
void BonusList::resize(BonusList::TInternalContainer::size_type sz, const std::shared_ptr<Bonus> & c)
|
||||
{
|
||||
bonuses.resize(sz, c);
|
||||
changed();
|
||||
}
|
||||
|
||||
void BonusList::reserve(TInternalContainer::size_type sz)
|
||||
{
|
||||
bonuses.reserve(sz);
|
||||
}
|
||||
|
||||
void BonusList::insert(BonusList::TInternalContainer::iterator position, BonusList::TInternalContainer::size_type n, const std::shared_ptr<Bonus> & x)
|
||||
{
|
||||
bonuses.insert(position, n, x);
|
||||
changed();
|
||||
}
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const BonusList &bonusList)
|
||||
{
|
||||
for (ui32 i = 0; i < bonusList.size(); i++)
|
||||
{
|
||||
const auto & b = bonusList[i];
|
||||
out << "Bonus " << i << "\n" << *b << std::endl;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
114
lib/bonuses/BonusList.h
Normal file
114
lib/bonuses/BonusList.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* BonusList.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 "Bonus.h"
|
||||
#include "BonusSelector.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class DLL_LINKAGE BonusList
|
||||
{
|
||||
public:
|
||||
using TInternalContainer = std::vector<std::shared_ptr<Bonus>>;
|
||||
|
||||
private:
|
||||
TInternalContainer bonuses;
|
||||
bool belongsToTree;
|
||||
void changed() const;
|
||||
|
||||
public:
|
||||
using const_reference = TInternalContainer::const_reference;
|
||||
using value_type = TInternalContainer::value_type;
|
||||
|
||||
using const_iterator = TInternalContainer::const_iterator;
|
||||
using iterator = TInternalContainer::iterator;
|
||||
|
||||
BonusList(bool BelongsToTree = false);
|
||||
BonusList(const BonusList &bonusList);
|
||||
BonusList(BonusList && other) noexcept;
|
||||
BonusList& operator=(const BonusList &bonusList);
|
||||
|
||||
// wrapper functions of the STL vector container
|
||||
TInternalContainer::size_type size() const { return bonuses.size(); }
|
||||
void push_back(const std::shared_ptr<Bonus> & x);
|
||||
TInternalContainer::iterator erase (const int position);
|
||||
void clear();
|
||||
bool empty() const { return bonuses.empty(); }
|
||||
void resize(TInternalContainer::size_type sz, const std::shared_ptr<Bonus> & c = nullptr);
|
||||
void reserve(TInternalContainer::size_type sz);
|
||||
TInternalContainer::size_type capacity() const { return bonuses.capacity(); }
|
||||
STRONG_INLINE std::shared_ptr<Bonus> &operator[] (TInternalContainer::size_type n) { return bonuses[n]; }
|
||||
STRONG_INLINE const std::shared_ptr<Bonus> &operator[] (TInternalContainer::size_type n) const { return bonuses[n]; }
|
||||
std::shared_ptr<Bonus> &back() { return bonuses.back(); }
|
||||
std::shared_ptr<Bonus> &front() { return bonuses.front(); }
|
||||
const std::shared_ptr<Bonus> &back() const { return bonuses.back(); }
|
||||
const std::shared_ptr<Bonus> &front() const { return bonuses.front(); }
|
||||
|
||||
// There should be no non-const access to provide solid,robust bonus caching
|
||||
TInternalContainer::const_iterator begin() const { return bonuses.begin(); }
|
||||
TInternalContainer::const_iterator end() const { return bonuses.end(); }
|
||||
TInternalContainer::size_type operator-=(const std::shared_ptr<Bonus> & i);
|
||||
|
||||
// BonusList functions
|
||||
void stackBonuses();
|
||||
int totalValue() const;
|
||||
void getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit = nullptr) const;
|
||||
void getAllBonuses(BonusList &out) const;
|
||||
|
||||
//special find functions
|
||||
std::shared_ptr<Bonus> getFirst(const CSelector &select);
|
||||
std::shared_ptr<const Bonus> getFirst(const CSelector &select) const;
|
||||
int valOfBonuses(const CSelector &select) const;
|
||||
|
||||
// conversion / output
|
||||
JsonNode toJsonNode() const;
|
||||
|
||||
// remove_if implementation for STL vector types
|
||||
template <class Predicate>
|
||||
void remove_if(Predicate pred)
|
||||
{
|
||||
BonusList newList;
|
||||
for(const auto & b : bonuses)
|
||||
{
|
||||
if (!pred(b.get()))
|
||||
newList.push_back(b);
|
||||
}
|
||||
bonuses.clear();
|
||||
bonuses.resize(newList.size());
|
||||
std::copy(newList.begin(), newList.end(), bonuses.begin());
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
void insert(const int position, InputIterator first, InputIterator last);
|
||||
void insert(TInternalContainer::iterator position, TInternalContainer::size_type n, const std::shared_ptr<Bonus> & x);
|
||||
|
||||
template <typename Handler>
|
||||
void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<TInternalContainer&>(bonuses);
|
||||
}
|
||||
|
||||
// C++ for range support
|
||||
auto begin () -> decltype (bonuses.begin())
|
||||
{
|
||||
return bonuses.begin();
|
||||
}
|
||||
|
||||
auto end () -> decltype (bonuses.end())
|
||||
{
|
||||
return bonuses.end();
|
||||
}
|
||||
};
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const BonusList &bonusList);
|
||||
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
260
lib/bonuses/BonusParams.cpp
Normal file
260
lib/bonuses/BonusParams.cpp
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* BonusParams.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 "BonusParams.h"
|
||||
#include "BonusSelector.h"
|
||||
|
||||
#include "../ResourceSet.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr, int deprecatedSubtype):
|
||||
isConverted(true)
|
||||
{
|
||||
if(deprecatedTypeStr == "SECONDARY_SKILL_PREMY" || deprecatedTypeStr == "SPECIAL_SECONDARY_SKILL")
|
||||
{
|
||||
if(deprecatedSubtype == SecondarySkill::PATHFINDING || deprecatedSubtypeStr == "skill.pathfinding")
|
||||
type = BonusType::ROUGH_TERRAIN_DISCOUNT;
|
||||
else if(deprecatedSubtype == SecondarySkill::DIPLOMACY || deprecatedSubtypeStr == "skill.diplomacy")
|
||||
type = BonusType::WANDERING_CREATURES_JOIN_BONUS;
|
||||
else if(deprecatedSubtype == SecondarySkill::WISDOM || deprecatedSubtypeStr == "skill.wisdom")
|
||||
type = BonusType::MAX_LEARNABLE_SPELL_LEVEL;
|
||||
else if(deprecatedSubtype == SecondarySkill::MYSTICISM || deprecatedSubtypeStr == "skill.mysticism")
|
||||
type = BonusType::MANA_REGENERATION;
|
||||
else if(deprecatedSubtype == SecondarySkill::NECROMANCY || deprecatedSubtypeStr == "skill.necromancy")
|
||||
type = BonusType::UNDEAD_RAISE_PERCENTAGE;
|
||||
else if(deprecatedSubtype == SecondarySkill::LEARNING || deprecatedSubtypeStr == "skill.learning")
|
||||
type = BonusType::HERO_EXPERIENCE_GAIN_PERCENT;
|
||||
else if(deprecatedSubtype == SecondarySkill::RESISTANCE || deprecatedSubtypeStr == "skill.resistance")
|
||||
type = BonusType::MAGIC_RESISTANCE;
|
||||
else if(deprecatedSubtype == SecondarySkill::EAGLE_EYE || deprecatedSubtypeStr == "skill.eagleEye")
|
||||
type = BonusType::LEARN_BATTLE_SPELL_CHANCE;
|
||||
else if(deprecatedSubtype == SecondarySkill::SCOUTING || deprecatedSubtypeStr == "skill.scouting")
|
||||
type = BonusType::SIGHT_RADIUS;
|
||||
else if(deprecatedSubtype == SecondarySkill::INTELLIGENCE || deprecatedSubtypeStr == "skill.intelligence")
|
||||
{
|
||||
type = BonusType::MANA_PER_KNOWLEDGE;
|
||||
valueType = BonusValueType::PERCENT_TO_BASE;
|
||||
valueTypeRelevant = true;
|
||||
}
|
||||
else if(deprecatedSubtype == SecondarySkill::SORCERY || deprecatedSubtypeStr == "skill.sorcery")
|
||||
type = BonusType::SPELL_DAMAGE;
|
||||
else if(deprecatedSubtype == SecondarySkill::SCHOLAR || deprecatedSubtypeStr == "skill.scholar")
|
||||
type = BonusType::LEARN_MEETING_SPELL_LIMIT;
|
||||
else if(deprecatedSubtype == SecondarySkill::ARCHERY|| deprecatedSubtypeStr == "skill.archery")
|
||||
{
|
||||
subtype = 1;
|
||||
subtypeRelevant = true;
|
||||
type = BonusType::PERCENTAGE_DAMAGE_BOOST;
|
||||
}
|
||||
else if(deprecatedSubtype == SecondarySkill::OFFENCE || deprecatedSubtypeStr == "skill.offence")
|
||||
{
|
||||
subtype = 0;
|
||||
subtypeRelevant = true;
|
||||
type = BonusType::PERCENTAGE_DAMAGE_BOOST;
|
||||
}
|
||||
else if(deprecatedSubtype == SecondarySkill::ARMORER || deprecatedSubtypeStr == "skill.armorer")
|
||||
{
|
||||
subtype = -1;
|
||||
subtypeRelevant = true;
|
||||
type = BonusType::GENERAL_DAMAGE_REDUCTION;
|
||||
}
|
||||
else if(deprecatedSubtype == SecondarySkill::NAVIGATION || deprecatedSubtypeStr == "skill.navigation")
|
||||
{
|
||||
subtype = 0;
|
||||
subtypeRelevant = true;
|
||||
valueType = BonusValueType::PERCENT_TO_BASE;
|
||||
valueTypeRelevant = true;
|
||||
type = BonusType::MOVEMENT;
|
||||
}
|
||||
else if(deprecatedSubtype == SecondarySkill::LOGISTICS || deprecatedSubtypeStr == "skill.logistics")
|
||||
{
|
||||
subtype = 1;
|
||||
subtypeRelevant = true;
|
||||
valueType = BonusValueType::PERCENT_TO_BASE;
|
||||
valueTypeRelevant = true;
|
||||
type = BonusType::MOVEMENT;
|
||||
}
|
||||
else if(deprecatedSubtype == SecondarySkill::ESTATES || deprecatedSubtypeStr == "skill.estates")
|
||||
{
|
||||
type = BonusType::GENERATE_RESOURCE;
|
||||
subtype = GameResID(EGameResID::GOLD);
|
||||
subtypeRelevant = true;
|
||||
}
|
||||
else if(deprecatedSubtype == SecondarySkill::AIR_MAGIC || deprecatedSubtypeStr == "skill.airMagic")
|
||||
{
|
||||
type = BonusType::MAGIC_SCHOOL_SKILL;
|
||||
subtypeRelevant = true;
|
||||
subtype = 4;
|
||||
}
|
||||
else if(deprecatedSubtype == SecondarySkill::WATER_MAGIC || deprecatedSubtypeStr == "skill.waterMagic")
|
||||
{
|
||||
type = BonusType::MAGIC_SCHOOL_SKILL;
|
||||
subtypeRelevant = true;
|
||||
subtype = 1;
|
||||
}
|
||||
else if(deprecatedSubtype == SecondarySkill::FIRE_MAGIC || deprecatedSubtypeStr == "skill.fireMagic")
|
||||
{
|
||||
type = BonusType::MAGIC_SCHOOL_SKILL;
|
||||
subtypeRelevant = true;
|
||||
subtype = 2;
|
||||
}
|
||||
else if(deprecatedSubtype == SecondarySkill::EARTH_MAGIC || deprecatedSubtypeStr == "skill.earthMagic")
|
||||
{
|
||||
type = BonusType::MAGIC_SCHOOL_SKILL;
|
||||
subtypeRelevant = true;
|
||||
subtype = 8;
|
||||
}
|
||||
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
|
||||
{
|
||||
type = BonusType::BONUS_DAMAGE_CHANCE;
|
||||
subtypeRelevant = true;
|
||||
subtypeStr = "core:creature.ballista";
|
||||
}
|
||||
else if (deprecatedSubtype == SecondarySkill::FIRST_AID || deprecatedSubtypeStr == "skill.firstAid")
|
||||
{
|
||||
type = BonusType::SPECIFIC_SPELL_POWER;
|
||||
subtypeRelevant = true;
|
||||
subtypeStr = "core:spell.firstAid";
|
||||
}
|
||||
else if (deprecatedSubtype == SecondarySkill::BALLISTICS || deprecatedSubtypeStr == "skill.ballistics")
|
||||
{
|
||||
type = BonusType::CATAPULT_EXTRA_SHOTS;
|
||||
subtypeRelevant = true;
|
||||
subtypeStr = "core:spell.catapultShot";
|
||||
}
|
||||
else
|
||||
isConverted = false;
|
||||
}
|
||||
else if (deprecatedTypeStr == "SECONDARY_SKILL_VAL2")
|
||||
{
|
||||
if(deprecatedSubtype == SecondarySkill::EAGLE_EYE || deprecatedSubtypeStr == "skill.eagleEye")
|
||||
type = BonusType::LEARN_BATTLE_SPELL_LEVEL_LIMIT;
|
||||
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
|
||||
{
|
||||
type = BonusType::HERO_GRANTS_ATTACKS;
|
||||
subtypeRelevant = true;
|
||||
subtypeStr = "core:creature.ballista";
|
||||
}
|
||||
else
|
||||
isConverted = false;
|
||||
}
|
||||
else if (deprecatedTypeStr == "SEA_MOVEMENT")
|
||||
{
|
||||
subtype = 0;
|
||||
subtypeRelevant = true;
|
||||
valueType = BonusValueType::ADDITIVE_VALUE;
|
||||
valueTypeRelevant = true;
|
||||
type = BonusType::MOVEMENT;
|
||||
}
|
||||
else if (deprecatedTypeStr == "LAND_MOVEMENT")
|
||||
{
|
||||
subtype = 1;
|
||||
subtypeRelevant = true;
|
||||
valueType = BonusValueType::ADDITIVE_VALUE;
|
||||
valueTypeRelevant = true;
|
||||
type = BonusType::MOVEMENT;
|
||||
}
|
||||
else if (deprecatedTypeStr == "MAXED_SPELL")
|
||||
{
|
||||
type = BonusType::SPELL;
|
||||
subtypeStr = deprecatedSubtypeStr;
|
||||
subtypeRelevant = true;
|
||||
valueType = BonusValueType::INDEPENDENT_MAX;
|
||||
valueTypeRelevant = true;
|
||||
val = 3;
|
||||
valRelevant = true;
|
||||
}
|
||||
else if (deprecatedTypeStr == "FULL_HP_REGENERATION")
|
||||
{
|
||||
type = BonusType::HP_REGENERATION;
|
||||
val = 100000; //very high value to always chose stack health
|
||||
valRelevant = true;
|
||||
}
|
||||
else if (deprecatedTypeStr == "KING1")
|
||||
{
|
||||
type = BonusType::KING;
|
||||
val = 0;
|
||||
valRelevant = true;
|
||||
}
|
||||
else if (deprecatedTypeStr == "KING2")
|
||||
{
|
||||
type = BonusType::KING;
|
||||
val = 2;
|
||||
valRelevant = true;
|
||||
}
|
||||
else if (deprecatedTypeStr == "KING3")
|
||||
{
|
||||
type = BonusType::KING;
|
||||
val = 3;
|
||||
valRelevant = true;
|
||||
}
|
||||
else if (deprecatedTypeStr == "SIGHT_RADIOUS")
|
||||
type = BonusType::SIGHT_RADIUS;
|
||||
else if (deprecatedTypeStr == "SELF_MORALE")
|
||||
{
|
||||
type = BonusType::MORALE;
|
||||
val = 1;
|
||||
valRelevant = true;
|
||||
valueType = BonusValueType::INDEPENDENT_MAX;
|
||||
valueTypeRelevant = true;
|
||||
}
|
||||
else if (deprecatedTypeStr == "SELF_LUCK")
|
||||
{
|
||||
type = BonusType::LUCK;
|
||||
val = 1;
|
||||
valRelevant = true;
|
||||
valueType = BonusValueType::INDEPENDENT_MAX;
|
||||
valueTypeRelevant = true;
|
||||
}
|
||||
else
|
||||
isConverted = false;
|
||||
}
|
||||
|
||||
const JsonNode & BonusParams::toJson()
|
||||
{
|
||||
assert(isConverted);
|
||||
if(ret.isNull())
|
||||
{
|
||||
ret["type"].String() = vstd::findKey(bonusNameMap, type);
|
||||
if(subtypeRelevant && !subtypeStr.empty())
|
||||
ret["subtype"].String() = subtypeStr;
|
||||
else if(subtypeRelevant)
|
||||
ret["subtype"].Integer() = subtype;
|
||||
if(valueTypeRelevant)
|
||||
ret["valueType"].String() = vstd::findKey(bonusValueMap, valueType);
|
||||
if(valRelevant)
|
||||
ret["val"].Float() = val;
|
||||
if(targetTypeRelevant)
|
||||
ret["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetType);
|
||||
jsonCreated = true;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
CSelector BonusParams::toSelector()
|
||||
{
|
||||
assert(isConverted);
|
||||
if(subtypeRelevant && !subtypeStr.empty())
|
||||
JsonUtils::resolveIdentifier(subtype, toJson(), "subtype");
|
||||
|
||||
auto ret = Selector::type()(type);
|
||||
if(subtypeRelevant)
|
||||
ret = ret.And(Selector::subtype()(subtype));
|
||||
if(valueTypeRelevant)
|
||||
ret = ret.And(Selector::valueType(valueType));
|
||||
if(targetTypeRelevant)
|
||||
ret = ret.And(Selector::targetSourceType()(targetType));
|
||||
return ret;
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
41
lib/bonuses/BonusParams.h
Normal file
41
lib/bonuses/BonusParams.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* BonusParams.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 "Bonus.h"
|
||||
|
||||
#include "../GameConstants.h"
|
||||
#include "../JsonNode.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
struct DLL_LINKAGE BonusParams {
|
||||
bool isConverted;
|
||||
BonusType type = BonusType::NONE;
|
||||
TBonusSubtype subtype = -1;
|
||||
std::string subtypeStr;
|
||||
bool subtypeRelevant = false;
|
||||
BonusValueType valueType = BonusValueType::BASE_NUMBER;
|
||||
bool valueTypeRelevant = false;
|
||||
si32 val = 0;
|
||||
bool valRelevant = false;
|
||||
BonusSource targetType = BonusSource::SECONDARY_SKILL;
|
||||
bool targetTypeRelevant = false;
|
||||
|
||||
BonusParams(bool isConverted = true) : isConverted(isConverted) {};
|
||||
BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr = "", int deprecatedSubtype = 0);
|
||||
const JsonNode & toJson();
|
||||
CSelector toSelector();
|
||||
private:
|
||||
JsonNode ret;
|
||||
bool jsonCreated = false;
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
89
lib/bonuses/BonusSelector.cpp
Normal file
89
lib/bonuses/BonusSelector.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* BonusSelector.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 "BonusSelector.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
namespace Selector
|
||||
{
|
||||
DLL_LINKAGE CSelectFieldEqual<BonusType> & type()
|
||||
{
|
||||
static CSelectFieldEqual<BonusType> stype(&Bonus::type);
|
||||
return stype;
|
||||
}
|
||||
|
||||
DLL_LINKAGE CSelectFieldEqual<TBonusSubtype> & subtype()
|
||||
{
|
||||
static CSelectFieldEqual<TBonusSubtype> ssubtype(&Bonus::subtype);
|
||||
return ssubtype;
|
||||
}
|
||||
|
||||
DLL_LINKAGE CSelectFieldEqual<CAddInfo> & info()
|
||||
{
|
||||
static CSelectFieldEqual<CAddInfo> sinfo(&Bonus::additionalInfo);
|
||||
return sinfo;
|
||||
}
|
||||
|
||||
DLL_LINKAGE CSelectFieldEqual<BonusSource> & sourceType()
|
||||
{
|
||||
static CSelectFieldEqual<BonusSource> ssourceType(&Bonus::source);
|
||||
return ssourceType;
|
||||
}
|
||||
|
||||
DLL_LINKAGE CSelectFieldEqual<BonusSource> & targetSourceType()
|
||||
{
|
||||
static CSelectFieldEqual<BonusSource> ssourceType(&Bonus::targetSourceType);
|
||||
return ssourceType;
|
||||
}
|
||||
|
||||
DLL_LINKAGE CSelectFieldEqual<BonusLimitEffect> & effectRange()
|
||||
{
|
||||
static CSelectFieldEqual<BonusLimitEffect> seffectRange(&Bonus::effectRange);
|
||||
return seffectRange;
|
||||
}
|
||||
|
||||
DLL_LINKAGE CWillLastTurns turns;
|
||||
DLL_LINKAGE CWillLastDays days;
|
||||
|
||||
CSelector DLL_LINKAGE typeSubtype(BonusType Type, TBonusSubtype Subtype)
|
||||
{
|
||||
return type()(Type).And(subtype()(Subtype));
|
||||
}
|
||||
|
||||
CSelector DLL_LINKAGE typeSubtypeInfo(BonusType type, TBonusSubtype subtype, const CAddInfo & info)
|
||||
{
|
||||
return CSelectFieldEqual<BonusType>(&Bonus::type)(type)
|
||||
.And(CSelectFieldEqual<TBonusSubtype>(&Bonus::subtype)(subtype))
|
||||
.And(CSelectFieldEqual<CAddInfo>(&Bonus::additionalInfo)(info));
|
||||
}
|
||||
|
||||
CSelector DLL_LINKAGE source(BonusSource source, ui32 sourceID)
|
||||
{
|
||||
return CSelectFieldEqual<BonusSource>(&Bonus::source)(source)
|
||||
.And(CSelectFieldEqual<ui32>(&Bonus::sid)(sourceID));
|
||||
}
|
||||
|
||||
CSelector DLL_LINKAGE sourceTypeSel(BonusSource source)
|
||||
{
|
||||
return CSelectFieldEqual<BonusSource>(&Bonus::source)(source);
|
||||
}
|
||||
|
||||
CSelector DLL_LINKAGE valueType(BonusValueType valType)
|
||||
{
|
||||
return CSelectFieldEqual<BonusValueType>(&Bonus::valType)(valType);
|
||||
}
|
||||
|
||||
DLL_LINKAGE CSelector all([](const Bonus * b){return true;});
|
||||
DLL_LINKAGE CSelector none([](const Bonus * b){return false;});
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
156
lib/bonuses/BonusSelector.h
Normal file
156
lib/bonuses/BonusSelector.h
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* BonusSelector.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 "Bonus.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CSelector : std::function<bool(const Bonus*)>
|
||||
{
|
||||
using TBase = std::function<bool(const Bonus*)>;
|
||||
public:
|
||||
CSelector() = default;
|
||||
template<typename T>
|
||||
CSelector(const T &t, //SFINAE trick -> include this c-tor in overload resolution only if parameter is class
|
||||
//(includes functors, lambdas) or function. Without that VC is going mad about ambiguities.
|
||||
typename std::enable_if < boost::mpl::or_ < std::is_class<T>, std::is_function<T >> ::value>::type *dummy = nullptr)
|
||||
: TBase(t)
|
||||
{}
|
||||
|
||||
CSelector(std::nullptr_t)
|
||||
{}
|
||||
|
||||
CSelector And(CSelector rhs) const
|
||||
{
|
||||
//lambda may likely outlive "this" (it can be even a temporary) => we copy the OBJECT (not pointer)
|
||||
auto thisCopy = *this;
|
||||
return [thisCopy, rhs](const Bonus *b) mutable { return thisCopy(b) && rhs(b); };
|
||||
}
|
||||
CSelector Or(CSelector rhs) const
|
||||
{
|
||||
auto thisCopy = *this;
|
||||
return [thisCopy, rhs](const Bonus *b) mutable { return thisCopy(b) || rhs(b); };
|
||||
}
|
||||
|
||||
CSelector Not() const
|
||||
{
|
||||
auto thisCopy = *this;
|
||||
return [thisCopy](const Bonus *b) mutable { return !thisCopy(b); };
|
||||
}
|
||||
|
||||
bool operator()(const Bonus *b) const
|
||||
{
|
||||
return TBase::operator()(b);
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return !!static_cast<const TBase&>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class CSelectFieldEqual
|
||||
{
|
||||
T Bonus::*ptr;
|
||||
|
||||
public:
|
||||
CSelectFieldEqual(T Bonus::*Ptr)
|
||||
: ptr(Ptr)
|
||||
{
|
||||
}
|
||||
|
||||
CSelector operator()(const T &valueToCompareAgainst) const
|
||||
{
|
||||
auto ptr2 = ptr; //We need a COPY because we don't want to reference this (might be outlived by lambda)
|
||||
return [ptr2, valueToCompareAgainst](const Bonus *bonus)
|
||||
{
|
||||
return bonus->*ptr2 == valueToCompareAgainst;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CWillLastTurns
|
||||
{
|
||||
public:
|
||||
int turnsRequested;
|
||||
|
||||
bool operator()(const Bonus *bonus) const
|
||||
{
|
||||
return turnsRequested <= 0 //every present effect will last zero (or "less") turns
|
||||
|| !Bonus::NTurns(bonus) //so do every not expriing after N-turns effect
|
||||
|| bonus->turnsRemain > turnsRequested;
|
||||
}
|
||||
CWillLastTurns& operator()(const int &setVal)
|
||||
{
|
||||
turnsRequested = setVal;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CWillLastDays
|
||||
{
|
||||
public:
|
||||
int daysRequested;
|
||||
|
||||
bool operator()(const Bonus *bonus) const
|
||||
{
|
||||
if(daysRequested <= 0 || Bonus::Permanent(bonus) || Bonus::OneBattle(bonus))
|
||||
return true;
|
||||
else if(Bonus::OneDay(bonus))
|
||||
return false;
|
||||
else if(Bonus::NDays(bonus) || Bonus::OneWeek(bonus))
|
||||
{
|
||||
return bonus->turnsRemain > daysRequested;
|
||||
}
|
||||
|
||||
return false; // TODO: ONE_WEEK need support for turnsRemain, but for now we'll exclude all unhandled durations
|
||||
}
|
||||
CWillLastDays& operator()(const int &setVal)
|
||||
{
|
||||
daysRequested = setVal;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace Selector
|
||||
{
|
||||
extern DLL_LINKAGE CSelectFieldEqual<BonusType> & type();
|
||||
extern DLL_LINKAGE CSelectFieldEqual<TBonusSubtype> & subtype();
|
||||
extern DLL_LINKAGE CSelectFieldEqual<CAddInfo> & info();
|
||||
extern DLL_LINKAGE CSelectFieldEqual<BonusSource> & sourceType();
|
||||
extern DLL_LINKAGE CSelectFieldEqual<BonusSource> & targetSourceType();
|
||||
extern DLL_LINKAGE CSelectFieldEqual<BonusLimitEffect> & effectRange();
|
||||
extern DLL_LINKAGE CWillLastTurns turns;
|
||||
extern DLL_LINKAGE CWillLastDays days;
|
||||
|
||||
CSelector DLL_LINKAGE typeSubtype(BonusType Type, TBonusSubtype Subtype);
|
||||
CSelector DLL_LINKAGE typeSubtypeInfo(BonusType type, TBonusSubtype subtype, const CAddInfo & info);
|
||||
CSelector DLL_LINKAGE source(BonusSource source, ui32 sourceID);
|
||||
CSelector DLL_LINKAGE sourceTypeSel(BonusSource source);
|
||||
CSelector DLL_LINKAGE valueType(BonusValueType valType);
|
||||
|
||||
/**
|
||||
* Selects all bonuses
|
||||
* Usage example: Selector::all.And(<functor>).And(<functor>)...)
|
||||
*/
|
||||
extern DLL_LINKAGE CSelector all;
|
||||
|
||||
/**
|
||||
* Selects nothing
|
||||
* Usage example: Selector::none.Or(<functor>).Or(<functor>)...)
|
||||
*/
|
||||
extern DLL_LINKAGE CSelector none;
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
210
lib/bonuses/CBonusProxy.cpp
Normal file
210
lib/bonuses/CBonusProxy.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* CBonusProxy.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 "BonusList.h"
|
||||
#include "CBonusProxy.h"
|
||||
#include "IBonusBearer.h"
|
||||
|
||||
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(const IBonusBearer * Target, CSelector Selector):
|
||||
target(Target),
|
||||
selector(std::move(Selector)),
|
||||
cachedLast(0),
|
||||
hasBonus(false)
|
||||
{
|
||||
}
|
||||
|
||||
//This constructor should be placed here to avoid side effects
|
||||
CCheckProxy::CCheckProxy(const CCheckProxy & other) = default;
|
||||
|
||||
bool CCheckProxy::getHasBonus() const
|
||||
{
|
||||
const auto treeVersion = target->getTreeVersion();
|
||||
|
||||
if(treeVersion != cachedLast)
|
||||
{
|
||||
hasBonus = target->hasBonus(selector);
|
||||
cachedLast = treeVersion;
|
||||
}
|
||||
|
||||
return hasBonus;
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
90
lib/bonuses/CBonusProxy.h
Normal file
90
lib/bonuses/CBonusProxy.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* CBonusProxy.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 "Bonus.h"
|
||||
#include "BonusSelector.h"
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
CCheckProxy(const IBonusBearer * Target, CSelector Selector);
|
||||
CCheckProxy(const CCheckProxy & other);
|
||||
CCheckProxy& operator= (const CCheckProxy & other) = default;
|
||||
|
||||
bool getHasBonus() const;
|
||||
|
||||
private:
|
||||
const IBonusBearer * target;
|
||||
CSelector selector;
|
||||
|
||||
mutable int64_t cachedLast;
|
||||
mutable bool hasBonus;
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
689
lib/bonuses/CBonusSystemNode.cpp
Normal file
689
lib/bonuses/CBonusSystemNode.cpp
Normal file
@ -0,0 +1,689 @@
|
||||
/*
|
||||
* CBonusSystemNode.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 "CBonusSystemNode.h"
|
||||
#include "Limiters.h"
|
||||
#include "Updaters.h"
|
||||
#include "Propagators.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
std::atomic<int64_t> CBonusSystemNode::treeChanged(1);
|
||||
constexpr bool CBonusSystemNode::cachingEnabled = true;
|
||||
|
||||
#define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents)
|
||||
#define FOREACH_RED_CHILD(pname) TNodes lchildren; getRedChildren(lchildren); for(CBonusSystemNode *pname : lchildren)
|
||||
|
||||
PlayerColor CBonusSystemNode::retrieveNodeOwner(const CBonusSystemNode * node)
|
||||
{
|
||||
return node ? node->getOwner() : PlayerColor::CANNOT_DETERMINE;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector)
|
||||
{
|
||||
auto ret = bonuses.getFirst(selector);
|
||||
if(ret)
|
||||
return ret;
|
||||
|
||||
FOREACH_PARENT(pname)
|
||||
{
|
||||
ret = pname->getBonusLocalFirst(selector);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<const Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector) const
|
||||
{
|
||||
return (const_cast<CBonusSystemNode*>(this))->getBonusLocalFirst(selector);
|
||||
}
|
||||
|
||||
void CBonusSystemNode::getParents(TCNodes & out) const /*retrieves list of parent nodes (nodes to inherit bonuses from) */
|
||||
{
|
||||
for(const auto & elem : parents)
|
||||
{
|
||||
const CBonusSystemNode *parent = elem;
|
||||
out.insert(parent);
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSystemNode::getParents(TNodes &out)
|
||||
{
|
||||
for (auto & elem : parents)
|
||||
{
|
||||
const CBonusSystemNode *parent = elem;
|
||||
out.insert(const_cast<CBonusSystemNode*>(parent));
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSystemNode::getAllParents(TCNodes & out) const //retrieves list of parent nodes (nodes to inherit bonuses from)
|
||||
{
|
||||
for(auto * parent : parents)
|
||||
{
|
||||
out.insert(parent);
|
||||
parent->getAllParents(out);
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSystemNode::getAllBonusesRec(BonusList &out, const CSelector & selector) const
|
||||
{
|
||||
//out has been reserved sufficient capacity at getAllBonuses() call
|
||||
|
||||
BonusList beforeUpdate;
|
||||
TCNodes lparents;
|
||||
getAllParents(lparents);
|
||||
|
||||
if(!lparents.empty())
|
||||
{
|
||||
//estimate on how many bonuses are missing yet - must be positive
|
||||
beforeUpdate.reserve(std::max(out.capacity() - out.size(), bonuses.size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
beforeUpdate.reserve(bonuses.size()); //at most all local bonuses
|
||||
}
|
||||
|
||||
for(const auto * parent : lparents)
|
||||
{
|
||||
parent->getAllBonusesRec(beforeUpdate, selector);
|
||||
}
|
||||
bonuses.getAllBonuses(beforeUpdate);
|
||||
|
||||
for(const auto & b : beforeUpdate)
|
||||
{
|
||||
//We should not run updaters on non-selected bonuses
|
||||
auto updated = selector(b.get()) && b->updater
|
||||
? getUpdatedBonus(b, b->updater)
|
||||
: b;
|
||||
|
||||
//do not add bonus with updater
|
||||
bool bonusExists = false;
|
||||
for(const auto & bonus : out)
|
||||
{
|
||||
if (bonus == updated)
|
||||
bonusExists = true;
|
||||
if (bonus->updater && bonus->updater == updated->updater)
|
||||
bonusExists = true;
|
||||
}
|
||||
|
||||
if (!bonusExists)
|
||||
out.push_back(updated);
|
||||
}
|
||||
}
|
||||
|
||||
TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root, const std::string &cachingStr) const
|
||||
{
|
||||
bool limitOnUs = (!root || root == this); //caching won't work when we want to limit bonuses against an external node
|
||||
if (CBonusSystemNode::cachingEnabled && limitOnUs)
|
||||
{
|
||||
// Exclusive access for one thread
|
||||
boost::lock_guard<boost::mutex> lock(sync);
|
||||
|
||||
// If the bonus system tree changes(state of a single node or the relations to each other) then
|
||||
// cache all bonus objects. Selector objects doesn't matter.
|
||||
if (cachedLast != treeChanged)
|
||||
{
|
||||
BonusList allBonuses;
|
||||
allBonuses.reserve(cachedBonuses.capacity()); //we assume we'll get about the same number of bonuses
|
||||
|
||||
cachedBonuses.clear();
|
||||
cachedRequests.clear();
|
||||
|
||||
getAllBonusesRec(allBonuses, Selector::all);
|
||||
limitBonuses(allBonuses, cachedBonuses);
|
||||
cachedBonuses.stackBonuses();
|
||||
|
||||
cachedLast = treeChanged;
|
||||
}
|
||||
|
||||
// If a bonus system request comes with a caching string then look up in the map if there are any
|
||||
// pre-calculated bonus results. Limiters can't be cached so they have to be calculated.
|
||||
if(!cachingStr.empty())
|
||||
{
|
||||
auto it = cachedRequests.find(cachingStr);
|
||||
if(it != cachedRequests.end())
|
||||
{
|
||||
//Cached list contains bonuses for our query with applied limiters
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
//We still don't have the bonuses (didn't returned them from cache)
|
||||
//Perform bonus selection
|
||||
auto ret = std::make_shared<BonusList>();
|
||||
cachedBonuses.getBonuses(*ret, selector, limit);
|
||||
|
||||
// Save the results in the cache
|
||||
if(!cachingStr.empty())
|
||||
cachedRequests[cachingStr] = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return getAllBonusesWithoutCaching(selector, limit, root);
|
||||
}
|
||||
}
|
||||
|
||||
TConstBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root) const
|
||||
{
|
||||
auto ret = std::make_shared<BonusList>();
|
||||
|
||||
// Get bonus results without caching enabled.
|
||||
BonusList beforeLimiting;
|
||||
BonusList afterLimiting;
|
||||
getAllBonusesRec(beforeLimiting, selector);
|
||||
|
||||
if(!root || root == this)
|
||||
{
|
||||
limitBonuses(beforeLimiting, afterLimiting);
|
||||
}
|
||||
else if(root)
|
||||
{
|
||||
//We want to limit our query against an external node. We get all its bonuses,
|
||||
// add the ones we're considering and see if they're cut out by limiters
|
||||
BonusList rootBonuses;
|
||||
BonusList limitedRootBonuses;
|
||||
getAllBonusesRec(rootBonuses, selector);
|
||||
|
||||
for(const auto & b : beforeLimiting)
|
||||
rootBonuses.push_back(b);
|
||||
|
||||
root->limitBonuses(rootBonuses, limitedRootBonuses);
|
||||
|
||||
for(const auto & b : beforeLimiting)
|
||||
if(vstd::contains(limitedRootBonuses, b))
|
||||
afterLimiting.push_back(b);
|
||||
|
||||
}
|
||||
afterLimiting.getBonuses(*ret, selector, limit);
|
||||
ret->stackBonuses();
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> CBonusSystemNode::getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const
|
||||
{
|
||||
assert(updater);
|
||||
return updater->createUpdatedBonus(b, * this);
|
||||
}
|
||||
|
||||
CBonusSystemNode::CBonusSystemNode()
|
||||
:CBonusSystemNode(false)
|
||||
{
|
||||
}
|
||||
|
||||
CBonusSystemNode::CBonusSystemNode(bool isHypotetic):
|
||||
bonuses(true),
|
||||
exportedBonuses(true),
|
||||
nodeType(UNKNOWN),
|
||||
cachedLast(0),
|
||||
isHypotheticNode(isHypotetic)
|
||||
{
|
||||
}
|
||||
|
||||
CBonusSystemNode::CBonusSystemNode(ENodeTypes NodeType):
|
||||
bonuses(true),
|
||||
exportedBonuses(true),
|
||||
nodeType(NodeType),
|
||||
cachedLast(0),
|
||||
isHypotheticNode(false)
|
||||
{
|
||||
}
|
||||
|
||||
CBonusSystemNode::CBonusSystemNode(CBonusSystemNode && other) noexcept:
|
||||
bonuses(std::move(other.bonuses)),
|
||||
exportedBonuses(std::move(other.exportedBonuses)),
|
||||
nodeType(other.nodeType),
|
||||
description(other.description),
|
||||
cachedLast(0),
|
||||
isHypotheticNode(other.isHypotheticNode)
|
||||
{
|
||||
std::swap(parents, other.parents);
|
||||
std::swap(children, other.children);
|
||||
|
||||
//fixing bonus tree without recalculation
|
||||
|
||||
if(!isHypothetic())
|
||||
{
|
||||
for(CBonusSystemNode * n : parents)
|
||||
{
|
||||
n->children -= &other;
|
||||
n->children.push_back(this);
|
||||
}
|
||||
}
|
||||
|
||||
for(CBonusSystemNode * n : children)
|
||||
{
|
||||
n->parents -= &other;
|
||||
n->parents.push_back(this);
|
||||
}
|
||||
|
||||
//cache ignored
|
||||
|
||||
//cachedBonuses
|
||||
//cachedRequests
|
||||
}
|
||||
|
||||
CBonusSystemNode::~CBonusSystemNode()
|
||||
{
|
||||
detachFromAll();
|
||||
|
||||
if(!children.empty())
|
||||
{
|
||||
while(!children.empty())
|
||||
children.front()->detachFrom(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSystemNode::attachTo(CBonusSystemNode & parent)
|
||||
{
|
||||
assert(!vstd::contains(parents, &parent));
|
||||
parents.push_back(&parent);
|
||||
|
||||
if(!isHypothetic())
|
||||
{
|
||||
if(parent.actsAsBonusSourceOnly())
|
||||
parent.newRedDescendant(*this);
|
||||
else
|
||||
newRedDescendant(parent);
|
||||
|
||||
parent.newChildAttached(*this);
|
||||
}
|
||||
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
|
||||
void CBonusSystemNode::detachFrom(CBonusSystemNode & parent)
|
||||
{
|
||||
assert(vstd::contains(parents, &parent));
|
||||
|
||||
if(!isHypothetic())
|
||||
{
|
||||
if(parent.actsAsBonusSourceOnly())
|
||||
parent.removedRedDescendant(*this);
|
||||
else
|
||||
removedRedDescendant(parent);
|
||||
}
|
||||
|
||||
if (vstd::contains(parents, &parent))
|
||||
{
|
||||
parents -= &parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
logBonus->error("Error on Detach. Node %s (nodeType=%d) has not parent %s (nodeType=%d)"
|
||||
, nodeShortInfo(), nodeType, parent.nodeShortInfo(), parent.nodeType);
|
||||
}
|
||||
|
||||
if(!isHypothetic())
|
||||
{
|
||||
parent.childDetached(*this);
|
||||
}
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
|
||||
void CBonusSystemNode::removeBonusesRecursive(const CSelector & s)
|
||||
{
|
||||
removeBonuses(s);
|
||||
for(CBonusSystemNode * child : children)
|
||||
child->removeBonusesRecursive(s);
|
||||
}
|
||||
|
||||
void CBonusSystemNode::reduceBonusDurations(const CSelector &s)
|
||||
{
|
||||
BonusList bl;
|
||||
exportedBonuses.getBonuses(bl, s, Selector::all);
|
||||
for(const auto & b : bl)
|
||||
{
|
||||
b->turnsRemain--;
|
||||
if(b->turnsRemain <= 0)
|
||||
removeBonus(b);
|
||||
}
|
||||
|
||||
for(CBonusSystemNode *child : children)
|
||||
child->reduceBonusDurations(s);
|
||||
}
|
||||
|
||||
void CBonusSystemNode::addNewBonus(const std::shared_ptr<Bonus>& b)
|
||||
{
|
||||
//turnsRemain shouldn't be zero for following durations
|
||||
if(Bonus::NTurns(b.get()) || Bonus::NDays(b.get()) || Bonus::OneWeek(b.get()))
|
||||
{
|
||||
assert(b->turnsRemain);
|
||||
}
|
||||
|
||||
assert(!vstd::contains(exportedBonuses, b));
|
||||
exportedBonuses.push_back(b);
|
||||
exportBonus(b);
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
|
||||
void CBonusSystemNode::accumulateBonus(const std::shared_ptr<Bonus>& b)
|
||||
{
|
||||
auto bonus = exportedBonuses.getFirst(Selector::typeSubtype(b->type, b->subtype)); //only local bonuses are interesting //TODO: what about value type?
|
||||
if(bonus)
|
||||
bonus->val += b->val;
|
||||
else
|
||||
addNewBonus(std::make_shared<Bonus>(*b)); //duplicate needed, original may get destroyed
|
||||
}
|
||||
|
||||
void CBonusSystemNode::removeBonus(const std::shared_ptr<Bonus>& b)
|
||||
{
|
||||
exportedBonuses -= b;
|
||||
if(b->propagator)
|
||||
unpropagateBonus(b);
|
||||
else
|
||||
bonuses -= b;
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
|
||||
void CBonusSystemNode::removeBonuses(const CSelector & selector)
|
||||
{
|
||||
BonusList toRemove;
|
||||
exportedBonuses.getBonuses(toRemove, selector, Selector::all);
|
||||
for(const auto & bonus : toRemove)
|
||||
removeBonus(bonus);
|
||||
}
|
||||
|
||||
bool CBonusSystemNode::actsAsBonusSourceOnly() const
|
||||
{
|
||||
switch(nodeType)
|
||||
{
|
||||
case CREATURE:
|
||||
case ARTIFACT:
|
||||
case ARTIFACT_INSTANCE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSystemNode::propagateBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & source)
|
||||
{
|
||||
if(b->propagator->shouldBeAttached(this))
|
||||
{
|
||||
auto propagated = b->propagationUpdater
|
||||
? source.getUpdatedBonus(b, b->propagationUpdater)
|
||||
: b;
|
||||
bonuses.push_back(propagated);
|
||||
logBonus->trace("#$# %s #propagated to# %s", propagated->Description(), nodeName());
|
||||
}
|
||||
|
||||
FOREACH_RED_CHILD(child)
|
||||
child->propagateBonus(b, source);
|
||||
}
|
||||
|
||||
void CBonusSystemNode::unpropagateBonus(const std::shared_ptr<Bonus> & b)
|
||||
{
|
||||
if(b->propagator->shouldBeAttached(this))
|
||||
{
|
||||
bonuses -= b;
|
||||
logBonus->trace("#$# %s #is no longer propagated to# %s", b->Description(), nodeName());
|
||||
}
|
||||
|
||||
FOREACH_RED_CHILD(child)
|
||||
child->unpropagateBonus(b);
|
||||
}
|
||||
|
||||
void CBonusSystemNode::newChildAttached(CBonusSystemNode & child)
|
||||
{
|
||||
assert(!vstd::contains(children, &child));
|
||||
children.push_back(&child);
|
||||
}
|
||||
|
||||
void CBonusSystemNode::childDetached(CBonusSystemNode & child)
|
||||
{
|
||||
if(vstd::contains(children, &child))
|
||||
children -= &child;
|
||||
else
|
||||
{
|
||||
logBonus->error("Error on Detach. Node %s (nodeType=%d) is not a child of %s (nodeType=%d)"
|
||||
, child.nodeShortInfo(), child.nodeType, nodeShortInfo(), nodeType);
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSystemNode::detachFromAll()
|
||||
{
|
||||
while(!parents.empty())
|
||||
detachFrom(*parents.front());
|
||||
}
|
||||
|
||||
bool CBonusSystemNode::isIndependentNode() const
|
||||
{
|
||||
return parents.empty() && children.empty();
|
||||
}
|
||||
|
||||
std::string CBonusSystemNode::nodeName() const
|
||||
{
|
||||
return !description.empty()
|
||||
? description
|
||||
: std::string("Bonus system node of type ") + typeid(*this).name();
|
||||
}
|
||||
|
||||
std::string CBonusSystemNode::nodeShortInfo() const
|
||||
{
|
||||
std::ostringstream str;
|
||||
str << "'" << typeid(* this).name() << "'";
|
||||
description.length() > 0
|
||||
? str << " (" << description << ")"
|
||||
: str << " (no description)";
|
||||
return str.str();
|
||||
}
|
||||
|
||||
void CBonusSystemNode::deserializationFix()
|
||||
{
|
||||
exportBonuses();
|
||||
|
||||
}
|
||||
|
||||
void CBonusSystemNode::getRedParents(TNodes & out)
|
||||
{
|
||||
FOREACH_PARENT(pname)
|
||||
{
|
||||
if(pname->actsAsBonusSourceOnly())
|
||||
{
|
||||
out.insert(pname);
|
||||
}
|
||||
}
|
||||
|
||||
if(!actsAsBonusSourceOnly())
|
||||
{
|
||||
for(CBonusSystemNode *child : children)
|
||||
{
|
||||
out.insert(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSystemNode::getRedChildren(TNodes &out)
|
||||
{
|
||||
FOREACH_PARENT(pname)
|
||||
{
|
||||
if(!pname->actsAsBonusSourceOnly())
|
||||
{
|
||||
out.insert(pname);
|
||||
}
|
||||
}
|
||||
|
||||
if(actsAsBonusSourceOnly())
|
||||
{
|
||||
for(CBonusSystemNode *child : children)
|
||||
{
|
||||
out.insert(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSystemNode::newRedDescendant(CBonusSystemNode & descendant)
|
||||
{
|
||||
for(const auto & b : exportedBonuses)
|
||||
{
|
||||
if(b->propagator)
|
||||
descendant.propagateBonus(b, *this);
|
||||
}
|
||||
TNodes redParents;
|
||||
getRedAncestors(redParents); //get all red parents recursively
|
||||
|
||||
for(auto * parent : redParents)
|
||||
{
|
||||
for(const auto & b : parent->exportedBonuses)
|
||||
{
|
||||
if(b->propagator)
|
||||
descendant.propagateBonus(b, *this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSystemNode::removedRedDescendant(CBonusSystemNode & descendant)
|
||||
{
|
||||
for(const auto & b : exportedBonuses)
|
||||
if(b->propagator)
|
||||
descendant.unpropagateBonus(b);
|
||||
|
||||
TNodes redParents;
|
||||
getRedAncestors(redParents); //get all red parents recursively
|
||||
|
||||
for(auto * parent : redParents)
|
||||
{
|
||||
for(const auto & b : parent->exportedBonuses)
|
||||
if(b->propagator)
|
||||
descendant.unpropagateBonus(b);
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSystemNode::getRedAncestors(TNodes &out)
|
||||
{
|
||||
getRedParents(out);
|
||||
|
||||
TNodes redParents;
|
||||
getRedParents(redParents);
|
||||
|
||||
for(CBonusSystemNode * parent : redParents)
|
||||
parent->getRedAncestors(out);
|
||||
}
|
||||
|
||||
void CBonusSystemNode::exportBonus(const std::shared_ptr<Bonus> & b)
|
||||
{
|
||||
if(b->propagator)
|
||||
propagateBonus(b, *this);
|
||||
else
|
||||
bonuses.push_back(b);
|
||||
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
|
||||
void CBonusSystemNode::exportBonuses()
|
||||
{
|
||||
for(const auto & b : exportedBonuses)
|
||||
exportBonus(b);
|
||||
}
|
||||
|
||||
CBonusSystemNode::ENodeTypes CBonusSystemNode::getNodeType() const
|
||||
{
|
||||
return nodeType;
|
||||
}
|
||||
|
||||
const BonusList& CBonusSystemNode::getBonusList() const
|
||||
{
|
||||
return bonuses;
|
||||
}
|
||||
|
||||
const TNodesVector& CBonusSystemNode::getParentNodes() const
|
||||
{
|
||||
return parents;
|
||||
}
|
||||
|
||||
const TNodesVector& CBonusSystemNode::getChildrenNodes() const
|
||||
{
|
||||
return children;
|
||||
}
|
||||
|
||||
void CBonusSystemNode::setNodeType(CBonusSystemNode::ENodeTypes type)
|
||||
{
|
||||
nodeType = type;
|
||||
}
|
||||
|
||||
BonusList & CBonusSystemNode::getExportedBonusList()
|
||||
{
|
||||
return exportedBonuses;
|
||||
}
|
||||
|
||||
const BonusList & CBonusSystemNode::getExportedBonusList() const
|
||||
{
|
||||
return exportedBonuses;
|
||||
}
|
||||
|
||||
const std::string& CBonusSystemNode::getDescription() const
|
||||
{
|
||||
return description;
|
||||
}
|
||||
|
||||
void CBonusSystemNode::setDescription(const std::string &description)
|
||||
{
|
||||
this->description = description;
|
||||
}
|
||||
|
||||
void CBonusSystemNode::limitBonuses(const BonusList &allBonuses, BonusList &out) const
|
||||
{
|
||||
assert(&allBonuses != &out); //todo should it work in-place?
|
||||
|
||||
BonusList undecided = allBonuses;
|
||||
BonusList & accepted = out;
|
||||
|
||||
while(true)
|
||||
{
|
||||
int undecidedCount = static_cast<int>(undecided.size());
|
||||
for(int i = 0; i < undecided.size(); i++)
|
||||
{
|
||||
auto b = undecided[i];
|
||||
BonusLimitationContext context = {*b, *this, out, undecided};
|
||||
auto decision = b->limiter ? b->limiter->limit(context) : ILimiter::EDecision::ACCEPT; //bonuses without limiters will be accepted by default
|
||||
if(decision == ILimiter::EDecision::DISCARD)
|
||||
{
|
||||
undecided.erase(i);
|
||||
i--; continue;
|
||||
}
|
||||
else if(decision == ILimiter::EDecision::ACCEPT)
|
||||
{
|
||||
accepted.push_back(b);
|
||||
undecided.erase(i);
|
||||
i--; continue;
|
||||
}
|
||||
else
|
||||
assert(decision == ILimiter::EDecision::NOT_SURE);
|
||||
}
|
||||
|
||||
if(undecided.size() == undecidedCount) //we haven't moved a single bonus -> limiters reached a stable state
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TBonusListPtr CBonusSystemNode::limitBonuses(const BonusList &allBonuses) const
|
||||
{
|
||||
auto ret = std::make_shared<BonusList>();
|
||||
limitBonuses(allBonuses, *ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CBonusSystemNode::treeHasChanged()
|
||||
{
|
||||
treeChanged++;
|
||||
}
|
||||
|
||||
int64_t CBonusSystemNode::getTreeVersion() const
|
||||
{
|
||||
return treeChanged;
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user