diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 07a3f7b41..01ad4ae27 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -96,11 +96,14 @@ set(lib_SRCS
serializer/JsonSerializeFormat.cpp
serializer/JsonSerializer.cpp
+ spells/AbilityCaster.cpp
spells/AdventureSpellMechanics.cpp
spells/BattleSpellMechanics.cpp
+ spells/BonusCaster.cpp
spells/CSpellHandler.cpp
spells/ISpellMechanics.cpp
spells/Problem.cpp
+ spells/ProxyCaster.cpp
spells/TargetCondition.cpp
spells/ViewSpellInt.cpp
@@ -258,13 +261,16 @@ set(lib_HEADERS
serializer/JsonSerializeFormat.h
serializer/JsonSerializer.h
+ spells/AbilityCaster.h
spells/AdventureSpellMechanics.h
spells/BattleSpellMechanics.h
+ spells/BonusCaster.h
spells/CSpellHandler.h
spells/ISpellMechanics.h
spells/Magic.h
spells/SpellMechanics.h
spells/Problem.h
+ spells/ProxyCaster.h
spells/TargetCondition.h
spells/ViewSpellInt.h
diff --git a/lib/VCMI_lib.cbp b/lib/VCMI_lib.cbp
index 1d024e648..7f7d44a61 100644
--- a/lib/VCMI_lib.cbp
+++ b/lib/VCMI_lib.cbp
@@ -381,10 +381,14 @@
+
+
+
+
@@ -392,6 +396,8 @@
+
+
diff --git a/lib/battle/CUnitState.cpp b/lib/battle/CUnitState.cpp
index 14198d108..fabf9482c 100644
--- a/lib/battle/CUnitState.cpp
+++ b/lib/battle/CUnitState.cpp
@@ -606,7 +606,7 @@ void CUnitState::getCasterName(MetaString & text) const
addNameReplacement(text, true);
}
-void CUnitState::getCastDescription(const spells::Spell * spell, MetaString & text) const
+void CUnitState::getCastDescription(const spells::Spell * spell, const std::vector & attacked, MetaString & text) const
{
text.addTxt(MetaString::GENERAL_TXT, 565);//The %s casts %s
//todo: use text 566 for single creature
@@ -614,11 +614,6 @@ void CUnitState::getCastDescription(const spells::Spell * spell, MetaString & te
text.addReplacement(MetaString::SPELL_NAME, spell->getIndex());
}
-void CUnitState::getCastDescription(const spells::Spell * spell, const std::vector & attacked, MetaString & text) const
-{
- getCastDescription(spell, text);
-}
-
bool CUnitState::ableToRetaliate() const
{
return alive()
diff --git a/lib/battle/CUnitState.h b/lib/battle/CUnitState.h
index 2054c50ce..0ae2bdcf4 100644
--- a/lib/battle/CUnitState.h
+++ b/lib/battle/CUnitState.h
@@ -226,7 +226,6 @@ public:
const PlayerColor getOwner() const override;
void getCasterName(MetaString & text) const override;
- void getCastDescription(const spells::Spell * spell, MetaString & text) const override;
void getCastDescription(const spells::Spell * spell, const std::vector & attacked, MetaString & text) const override;
bool ableToRetaliate() const override;
diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp
index 606b7f8ed..f7a57ccf7 100644
--- a/lib/mapObjects/CGHeroInstance.cpp
+++ b/lib/mapObjects/CGHeroInstance.cpp
@@ -699,13 +699,6 @@ void CGHeroInstance::getCasterName(MetaString & text) const
text.addReplacement(name);
}
-void CGHeroInstance::getCastDescription(const spells::Spell * spell, MetaString & text) const
-{
- text.addTxt(MetaString::GENERAL_TXT, 196);
- getCasterName(text);
- text.addReplacement(MetaString::SPELL_NAME, spell->getIndex());
-}
-
void CGHeroInstance::getCastDescription(const spells::Spell * spell, const std::vector & attacked, MetaString & text) const
{
const bool singleTarget = attacked.size() == 1;
diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h
index b7037c890..7ccf140d2 100644
--- a/lib/mapObjects/CGHeroInstance.h
+++ b/lib/mapObjects/CGHeroInstance.h
@@ -247,7 +247,6 @@ public:
const PlayerColor getOwner() const override;
void getCasterName(MetaString & text) const override;
- void getCastDescription(const spells::Spell * spell, MetaString & text) const override;
void getCastDescription(const spells::Spell * spell, const std::vector & attacked, MetaString & text) const override;
void spendMana(const spells::PacketSender * server, const int spellCost) const override;
diff --git a/lib/spells/AbilityCaster.cpp b/lib/spells/AbilityCaster.cpp
new file mode 100644
index 000000000..3127cf3c5
--- /dev/null
+++ b/lib/spells/AbilityCaster.cpp
@@ -0,0 +1,58 @@
+/*
+ * AbilityCaster.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 "AbilityCaster.h"
+
+#include "../battle/Unit.h"
+
+namespace spells
+{
+
+AbilityCaster::AbilityCaster(const battle::Unit * actualCaster_, int baseSpellLevel_)
+ : ProxyCaster(actualCaster_),
+ actualCaster(actualCaster_),
+ baseSpellLevel(baseSpellLevel_)
+{
+}
+
+AbilityCaster::~AbilityCaster() = default;
+
+ui8 AbilityCaster::getSpellSchoolLevel(const Spell * spell, int * outSelectedSchool) const
+{
+ int skill = baseSpellLevel;
+
+ if(spell->getLevel() > 0)
+ {
+ vstd::amax(skill, actualCaster->valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 0));
+ }
+
+ vstd::amax(skill, 0);
+ vstd::amin(skill, 3);
+
+ return static_cast(skill); //todo: unify spell school level type
+}
+
+int AbilityCaster::getEffectLevel(const Spell * spell) const
+{
+ return getSpellSchoolLevel(spell);
+}
+
+void AbilityCaster::getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const
+{
+ //do nothing
+}
+
+void AbilityCaster::spendMana(const PacketSender * server, const int spellCost) const
+{
+ //do nothing
+}
+
+} // namespace spells
diff --git a/lib/spells/AbilityCaster.h b/lib/spells/AbilityCaster.h
new file mode 100644
index 000000000..c3dd90b95
--- /dev/null
+++ b/lib/spells/AbilityCaster.h
@@ -0,0 +1,34 @@
+/*
+ * AbilityCaster.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 "ProxyCaster.h"
+
+namespace spells
+{
+
+class DLL_LINKAGE AbilityCaster : public ProxyCaster
+{
+public:
+ AbilityCaster(const battle::Unit * actualCaster_, int baseSpellLevel_);
+ virtual ~AbilityCaster();
+
+ ui8 getSpellSchoolLevel(const Spell * spell, int * outSelectedSchool = nullptr) const override;
+ int getEffectLevel(const Spell * spell) const override;
+ void getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const override;
+ void spendMana(const PacketSender * server, const int spellCost) const override;
+
+private:
+ const battle::Unit * actualCaster;
+ int baseSpellLevel;
+};
+
+} // namespace spells
diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp
index bf731e1db..f6e08650f 100644
--- a/lib/spells/BattleSpellMechanics.cpp
+++ b/lib/spells/BattleSpellMechanics.cpp
@@ -262,7 +262,8 @@ void BattleSpellMechanics::cast(const PacketSender * server, vstd::RNG & rng, co
{
MetaString line;
caster->getCastDescription(owner, affectedUnits, line);
- sc.battleLog.push_back(line);
+ if(!line.message.empty())
+ sc.battleLog.push_back(line);
}
break;
diff --git a/lib/spells/BonusCaster.cpp b/lib/spells/BonusCaster.cpp
new file mode 100644
index 000000000..05ca2db2a
--- /dev/null
+++ b/lib/spells/BonusCaster.cpp
@@ -0,0 +1,57 @@
+/*
+ * BonusCaster.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 "BonusCaster.h"
+
+#include "../NetPacksBase.h"
+#include "../HeroBonus.h"
+#include "../battle/Unit.h"
+
+namespace spells
+{
+
+BonusCaster::BonusCaster(const Caster * actualCaster_, std::shared_ptr bonus_)
+ : ProxyCaster(actualCaster_),
+ actualCaster(actualCaster_),
+ bonus(bonus_)
+{
+
+}
+
+BonusCaster::~BonusCaster() = default;
+
+void BonusCaster::getCasterName(MetaString & text) const
+{
+ if(!bonus->description.empty())
+ text.addReplacement(bonus->description);
+ else
+ actualCaster->getCasterName(text);
+}
+
+void BonusCaster::getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const
+{
+ const bool singleTarget = attacked.size() == 1;
+ const int textIndex = singleTarget ? 195 : 196;
+
+ text.addTxt(MetaString::GENERAL_TXT, textIndex);
+ getCasterName(text);
+ text.addReplacement(MetaString::SPELL_NAME, spell->getIndex());
+ if(singleTarget)
+ attacked.at(0)->addNameReplacement(text, true);
+}
+
+void BonusCaster::spendMana(const PacketSender * server, const int spellCost) const
+{
+ logGlobal->error("Unexpected call to BonusCaster::spendMana");
+}
+
+
+} // namespace spells
diff --git a/lib/spells/BonusCaster.h b/lib/spells/BonusCaster.h
new file mode 100644
index 000000000..79997bcc3
--- /dev/null
+++ b/lib/spells/BonusCaster.h
@@ -0,0 +1,36 @@
+/*
+ * BonusCaster.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 "ProxyCaster.h"
+
+struct Bonus;
+
+namespace spells
+{
+
+class DLL_LINKAGE BonusCaster : public ProxyCaster
+{
+public:
+ BonusCaster(const Caster * actualCaster_, std::shared_ptr bonus_);
+ virtual ~BonusCaster();
+
+ void getCasterName(MetaString & text) const override;
+ void getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const override;
+ void spendMana(const PacketSender * server, const int spellCost) const override;
+
+private:
+ const Caster * actualCaster;
+ std::shared_ptr bonus;
+};
+
+} // namespace spells
+
diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp
index fd2dbc327..a16c58e13 100644
--- a/lib/spells/CSpellHandler.cpp
+++ b/lib/spells/CSpellHandler.cpp
@@ -250,6 +250,11 @@ int32_t CSpell::getIndex() const
return id.toEnum();
}
+int32_t CSpell::getLevel() const
+{
+ return level;
+}
+
bool CSpell::isCombatSpell() const
{
return combatSpell;
diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h
index e31c61275..44e4fd4a4 100644
--- a/lib/spells/CSpellHandler.h
+++ b/lib/spells/CSpellHandler.h
@@ -291,6 +291,7 @@ public:
void forEachSchool(const std::function & cb) const override;
int32_t getIndex() const override;
+ int32_t getLevel() const override;
/**
* Returns resource name of icon for SPELL_IMMUNITY bonus
diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp
index aae895a6d..5a24bab3f 100644
--- a/lib/spells/ISpellMechanics.cpp
+++ b/lib/spells/ISpellMechanics.cpp
@@ -158,8 +158,7 @@ BattleCast::BattleCast(const CBattleInfoCallback * cb, const Caster * caster_, c
cb(cb),
caster(caster_),
mode(mode_),
- spellLvl(),
- effectLevel(),
+ magicSkillLevel(),
effectPower(),
effectDuration(),
effectValue(),
@@ -174,8 +173,7 @@ BattleCast::BattleCast(const BattleCast & orig, const Caster * caster_)
cb(orig.cb),
caster(caster_),
mode(Mode::MAGIC_MIRROR),
- spellLvl(orig.spellLvl),
- effectLevel(orig.effectLevel),
+ magicSkillLevel(orig.magicSkillLevel),
effectPower(orig.effectPower),
effectDuration(orig.effectDuration),
effectValue(orig.effectValue),
@@ -206,20 +204,9 @@ const CBattleInfoCallback * BattleCast::getBattle() const
return cb;
}
-BattleCast::OptionalValue BattleCast::getEffectLevel() const
+BattleCast::OptionalValue BattleCast::getSpellLevel() const
{
- if(effectLevel)
- return effectLevel;
- else
- return spellLvl;
-}
-
-BattleCast::OptionalValue BattleCast::getRangeLevel() const
-{
- if(rangeLevel)
- return rangeLevel;
- else
- return spellLvl;
+ return magicSkillLevel;
}
BattleCast::OptionalValue BattleCast::getEffectPower() const
@@ -249,17 +236,7 @@ boost::logic::tribool BattleCast::isMassive() const
void BattleCast::setSpellLevel(BattleCast::Value value)
{
- spellLvl = boost::make_optional(value);
-}
-
-void BattleCast::setEffectLevel(BattleCast::Value value)
-{
- effectLevel = boost::make_optional(value);
-}
-
-void BattleCast::setRangeLevel(BattleCast::Value value)
-{
- rangeLevel = boost::make_optional(value);
+ magicSkillLevel = boost::make_optional(value);
}
void BattleCast::setEffectPower(BattleCast::Value value)
@@ -484,7 +461,7 @@ BaseMechanics::BaseMechanics(const IBattleCast * event)
casterSide = cb->playerToSide(caster->getOwner()).get();
{
- auto value = event->getRangeLevel();
+ auto value = event->getSpellLevel();
if(value)
rangeLevel = value.get();
else
@@ -492,7 +469,7 @@ BaseMechanics::BaseMechanics(const IBattleCast * event)
vstd::abetween(rangeLevel, 0, 3);
}
{
- auto value = event->getEffectLevel();
+ auto value = event->getSpellLevel();
if(value)
effectLevel = value.get();
else
diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h
index d9ec25432..a5f6ccb16 100644
--- a/lib/spells/ISpellMechanics.h
+++ b/lib/spells/ISpellMechanics.h
@@ -88,8 +88,7 @@ public:
virtual const Caster * getCaster() const = 0;
virtual const CBattleInfoCallback * getBattle() const = 0;
- virtual OptionalValue getEffectLevel() const = 0;
- virtual OptionalValue getRangeLevel() const = 0;
+ virtual OptionalValue getSpellLevel() const = 0;
virtual OptionalValue getEffectPower() const = 0;
virtual OptionalValue getEffectDuration() const = 0;
@@ -123,8 +122,7 @@ public:
const Caster * getCaster() const override;
const CBattleInfoCallback * getBattle() const override;
- OptionalValue getEffectLevel() const override;
- OptionalValue getRangeLevel() const override;
+ OptionalValue getSpellLevel() const override;
OptionalValue getEffectPower() const override;
OptionalValue getEffectDuration() const override;
@@ -135,8 +133,6 @@ public:
boost::logic::tribool isMassive() const override;
void setSpellLevel(Value value);
- void setEffectLevel(Value value);
- void setRangeLevel(Value value);
void setEffectPower(Value value);
void setEffectDuration(Value value);
@@ -162,10 +158,8 @@ public:
private:
///spell school level
- OptionalValue spellLvl;
+ OptionalValue magicSkillLevel;
- OptionalValue rangeLevel;
- OptionalValue effectLevel;
///actual spell-power affecting effect values
OptionalValue effectPower;
///actual spell-power affecting effect duration
diff --git a/lib/spells/Magic.h b/lib/spells/Magic.h
index 09a62214c..4ece8b1ce 100644
--- a/lib/spells/Magic.h
+++ b/lib/spells/Magic.h
@@ -41,8 +41,6 @@ enum class Mode
{
//ACTIVE, //todo: use
HERO, //deprecated
- AFTER_ATTACK,
- BEFORE_ATTACK,
MAGIC_MIRROR,
CREATURE_ACTIVE, //deprecated
ENCHANTER,
@@ -92,6 +90,8 @@ public:
virtual int32_t getIndex() const = 0;
+ virtual int32_t getLevel() const = 0;
+
virtual void forEachSchool(const std::function & cb) const = 0;
};
@@ -129,7 +129,6 @@ public:
virtual void getCasterName(MetaString & text) const = 0;
///full default text
- virtual void getCastDescription(const Spell * spell, MetaString & text) const = 0;
virtual void getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const = 0;
virtual void spendMana(const PacketSender * server, const int spellCost) const = 0;
diff --git a/lib/spells/ProxyCaster.cpp b/lib/spells/ProxyCaster.cpp
new file mode 100644
index 000000000..5746dd643
--- /dev/null
+++ b/lib/spells/ProxyCaster.cpp
@@ -0,0 +1,82 @@
+/*
+ * ProxyCaster.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 "ProxyCaster.h"
+
+#include "../GameConstants.h"
+
+namespace spells
+{
+
+ProxyCaster::ProxyCaster(const Caster * actualCaster_)
+ : actualCaster(actualCaster_)
+{
+
+}
+
+ProxyCaster::~ProxyCaster() = default;
+
+ui8 ProxyCaster::getSpellSchoolLevel(const Spell * spell, int * outSelectedSchool) const
+{
+ return actualCaster->getSpellSchoolLevel(spell, outSelectedSchool);
+}
+
+int ProxyCaster::getEffectLevel(const Spell * spell) const
+{
+ return actualCaster->getEffectLevel(spell);
+}
+
+int64_t ProxyCaster::getSpellBonus(const Spell * spell, int64_t base, const battle::Unit * affectedStack) const
+{
+ return actualCaster->getSpellBonus(spell, base, affectedStack);
+}
+
+int64_t ProxyCaster::getSpecificSpellBonus(const Spell * spell, int64_t base) const
+{
+ return actualCaster->getSpecificSpellBonus(spell, base);
+}
+
+int ProxyCaster::getEffectPower(const Spell * spell) const
+{
+ return actualCaster->getEffectPower(spell);
+}
+
+int ProxyCaster::getEnchantPower(const Spell * spell) const
+{
+ return actualCaster->getEnchantPower(spell);
+}
+
+int64_t ProxyCaster::getEffectValue(const Spell * spell) const
+{
+ return actualCaster->getEffectValue(spell);
+}
+
+const PlayerColor ProxyCaster::getOwner() const
+{
+ return actualCaster->getOwner();
+}
+
+void ProxyCaster::getCasterName(MetaString & text) const
+{
+ return actualCaster->getCasterName(text);
+}
+
+void ProxyCaster::getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const
+{
+ actualCaster->getCastDescription(spell, attacked, text);
+}
+
+void ProxyCaster::spendMana(const PacketSender * server, const int spellCost) const
+{
+ actualCaster->spendMana(server, spellCost);
+}
+
+}
diff --git a/lib/spells/ProxyCaster.h b/lib/spells/ProxyCaster.h
new file mode 100644
index 000000000..febec8191
--- /dev/null
+++ b/lib/spells/ProxyCaster.h
@@ -0,0 +1,40 @@
+/*
+ * ProxyCaster.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 "Magic.h"
+
+namespace spells
+{
+
+class DLL_LINKAGE ProxyCaster : public Caster
+{
+public:
+ ProxyCaster(const Caster * actualCaster_);
+ virtual ~ProxyCaster();
+
+ ui8 getSpellSchoolLevel(const Spell * spell, int * outSelectedSchool = nullptr) const override;
+ int getEffectLevel(const Spell * spell) const override;
+ int64_t getSpellBonus(const Spell * spell, int64_t base, const battle::Unit * affectedStack) const override;
+ int64_t getSpecificSpellBonus(const Spell * spell, int64_t base) const override;
+ int getEffectPower(const Spell * spell) const override;
+ int getEnchantPower(const Spell * spell) const override;
+ int64_t getEffectValue(const Spell * spell) const override;
+ const PlayerColor getOwner() const override;
+ void getCasterName(MetaString & text) const override;
+ void getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const override;
+ void spendMana(const PacketSender * server, const int spellCost) const override;
+
+private:
+ const Caster * actualCaster;
+};
+
+} // namespace spells
diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp
index 9f1c80f91..cd4c8ca1b 100644
--- a/server/CGameHandler.cpp
+++ b/server/CGameHandler.cpp
@@ -18,6 +18,8 @@
#include "../lib/CArtHandler.h"
#include "../lib/CBuildingHandler.h"
#include "../lib/CHeroHandler.h"
+#include "../lib/spells/AbilityCaster.h"
+#include "../lib/spells/BonusCaster.h"
#include "../lib/spells/CSpellHandler.h"
#include "../lib/spells/ISpellMechanics.h"
#include "../lib/spells/Problem.h"
@@ -141,11 +143,6 @@ public:
logGlobal->error("Unexpected call to ObstacleCasterProxy::getCasterName");
}
- void getCastDescription(const Spell * spell, MetaString & text) const override
- {
- logGlobal->error("Unexpected call to ObstacleCasterProxy::getCastDescription");
- }
-
void getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const override
{
logGlobal->error("Unexpected call to ObstacleCasterProxy::getCastDescription");
@@ -163,95 +160,6 @@ private:
const SpellCreatedObstacle * obs;
};
-class BonusCasterProxy : public Caster
-{
-public:
- BonusCasterProxy(const CGHeroInstance * hero_, std::shared_ptr bonus_)
- : hero(hero_),
- bonus(bonus_)
- {
-
- }
-
- ~BonusCasterProxy() = default;
-
- ui8 getSpellSchoolLevel(const Spell * spell, int * outSelectedSchool = nullptr) const override
- {
- return hero->getSpellSchoolLevel(spell, outSelectedSchool);
- }
-
- int getEffectLevel(const Spell * spell) const override
- {
- return hero->getEffectLevel(spell);
- }
-
- int64_t getSpellBonus(const Spell * spell, int64_t base, const battle::Unit * affectedStack) const override
- {
- return hero->getSpellBonus(spell, base, affectedStack);
- }
-
- int64_t getSpecificSpellBonus(const Spell * spell, int64_t base) const override
- {
- return hero->getSpecificSpellBonus(spell, base);
- }
-
- int getEffectPower(const Spell * spell) const override
- {
- return hero->getEffectPower(spell);
- }
-
- int getEnchantPower(const Spell * spell) const override
- {
- return hero->getEnchantPower(spell);
- }
-
- int64_t getEffectValue(const Spell * spell) const override
- {
- return hero->getEffectValue(spell);
- }
-
- const PlayerColor getOwner() const override
- {
- return hero->getOwner();
- }
-
- void getCasterName(MetaString & text) const override
- {
- if(!bonus->description.empty())
- text.addReplacement(bonus->description);
- else
- hero->getCasterName(text);
- }
-
- void getCastDescription(const Spell * spell, MetaString & text) const override
- {
- text.addTxt(MetaString::GENERAL_TXT, 196);
- getCasterName(text);
- text.addReplacement(MetaString::SPELL_NAME, spell->getIndex());
- }
-
- void getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const override
- {
- const bool singleTarget = attacked.size() == 1;
- const int textIndex = singleTarget ? 195 : 196;
-
- text.addTxt(MetaString::GENERAL_TXT, textIndex);
- getCasterName(text);
- text.addReplacement(MetaString::SPELL_NAME, spell->getIndex());
- if(singleTarget)
- attacked.at(0)->addNameReplacement(text, true);
- }
-
- void spendMana(const PacketSender * server, const int spellCost) const override
- {
- logGlobal->error("Unexpected call to BonusCasterProxy::spendMana");
- }
-
-private:
- const CGHeroInstance * hero;
- std::shared_ptr bonus;
-};
-
}//
CondSh battleMadeAction(false);
@@ -5530,10 +5438,8 @@ bool CGameHandler::dig(const CGHeroInstance *h)
return true;
}
-void CGameHandler::attackCasting(bool ranged, Bonus::BonusType attackMode, const CStack * attacker, const CStack * defender)
+void CGameHandler::attackCasting(bool ranged, Bonus::BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender)
{
- spells::Mode mode = (attackMode == Bonus::SPELL_AFTER_ATTACK) ? spells::Mode::AFTER_ATTACK : spells::Mode::BEFORE_ATTACK;
-
if(attacker->hasBonusOfType(attackMode))
{
std::set spellsToCast;
@@ -5563,7 +5469,9 @@ void CGameHandler::attackCasting(bool ranged, Bonus::BonusType attackMode, const
vstd::amin(chance, 100);
const CSpell * spell = SpellID(spellID).toSpell();
- if(!spell->canBeCastAt(gs->curB, mode, attacker, defender->getPosition()))
+ spells::AbilityCaster caster(attacker, spellLevel);
+
+ if(!spell->canBeCastAt(gs->curB, spells::Mode::PASSIVE, &caster, defender->getPosition()))
continue;
//check if spell should be cast (probability handling)
@@ -5573,8 +5481,7 @@ void CGameHandler::attackCasting(bool ranged, Bonus::BonusType attackMode, const
//casting
if(castMe)
{
- spells::BattleCast parameters(gs->curB, attacker, mode, spell);
- parameters.setSpellLevel(spellLevel);
+ spells::BattleCast parameters(gs->curB, &caster, spells::Mode::PASSIVE, spell);
parameters.aimToUnit(defender);
parameters.cast(spellEnv);
}
@@ -5623,8 +5530,9 @@ void CGameHandler::handleAfterAttackCasting(bool ranged, const CStack * attacker
//TODO: death stare was not originally available for multiple-hex attacks, but...
const CSpell * spell = SpellID(SpellID::DEATH_STARE).toSpell();
- spells::BattleCast parameters(gs->curB, attacker, spells::Mode::AFTER_ATTACK, spell);
- parameters.setSpellLevel(0);
+ spells::AbilityCaster caster(attacker, 0);
+
+ spells::BattleCast parameters(gs->curB, &caster, spells::Mode::PASSIVE, spell);
parameters.aimToUnit(defender);
parameters.setEffectValue(staredCreatures);
parameters.cast(spellEnv);
@@ -5646,8 +5554,9 @@ void CGameHandler::handleAfterAttackCasting(bool ranged, const CStack * attacker
{
const CSpell * spell = SpellID(SpellID::ACID_BREATH_DAMAGE).toSpell();
- spells::BattleCast parameters(gs->curB, attacker, spells::Mode::AFTER_ATTACK, spell);
- parameters.setSpellLevel(0);
+ spells::AbilityCaster caster(attacker, 0);
+
+ spells::BattleCast parameters(gs->curB, &caster, spells::Mode::PASSIVE, spell);
parameters.aimToUnit(defender);
parameters.setEffectValue(acidDamage * attacker->getCount());
parameters.cast(spellEnv);
@@ -6066,7 +5975,7 @@ void CGameHandler::runBattle()
for (auto b : *bl)
{
- spells::BonusCasterProxy caster(h, b);
+ spells::BonusCaster caster(h, b);
const CSpell * spell = SpellID(b->subtype).toSpell();
diff --git a/server/CGameHandler.h b/server/CGameHandler.h
index 08d24f7d6..085d7d4d3 100644
--- a/server/CGameHandler.h
+++ b/server/CGameHandler.h
@@ -289,7 +289,7 @@ public:
void newTurn();
void handleAttackBeforeCasting(bool ranged, const CStack * attacker, const CStack * defender);
void handleAfterAttackCasting(bool ranged, const CStack * attacker, const CStack * defender);
- void attackCasting(bool ranged, Bonus::BonusType attackMode, const CStack * attacker, const CStack * defender);
+ void attackCasting(bool ranged, Bonus::BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender);
bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector & slot);
void spawnWanderingMonsters(CreatureID creatureID);
void handleCheatCode(std::string & cheat, PlayerColor player, const CGHeroInstance * hero, const CGTownInstance * town, bool & cheated);
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 5ea5a69d1..25251f7c6 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -30,6 +30,7 @@ set(test_SRCS
map/CMapFormatTest.cpp
map/MapComparer.cpp
+ spells/AbilityCasterTest.cpp
spells/TargetConditionTest.cpp
spells/effects/EffectFixture.cpp
diff --git a/test/Test.cbp b/test/Test.cbp
index 6c47d5ad7..c7121f107 100644
--- a/test/Test.cbp
+++ b/test/Test.cbp
@@ -109,6 +109,7 @@
+
diff --git a/test/game/CGameStateTest.cpp b/test/game/CGameStateTest.cpp
index 5460acd20..b9a0a0e7b 100644
--- a/test/game/CGameStateTest.cpp
+++ b/test/game/CGameStateTest.cpp
@@ -26,6 +26,7 @@
#include "../../lib/spells/CSpellHandler.h"
#include "../../lib/spells/ISpellMechanics.h"
+#include "../../lib/spells/AbilityCaster.h"
class CGameStateTest : public ::testing::Test, public SpellCastEnvironment, public MapListener
{
@@ -224,12 +225,14 @@ TEST_F(CGameStateTest, issue2765)
{
const CSpell * age = SpellID(SpellID::AGE).toSpell();
ASSERT_NE(age, nullptr);
- //here tested ballista, but this applied to all war machines
- spells::BattleCast cast(gameState->curB, att, spells::Mode::AFTER_ATTACK, age);
- cast.aimToUnit(def);
- cast.setSpellLevel(3);
- EXPECT_FALSE(age->canBeCastAt(gameState->curB, spells::Mode::AFTER_ATTACK, att, def->getPosition()));
+ spells::AbilityCaster caster(att, 3);
+
+ //here tested ballista, but this applied to all war machines
+ spells::BattleCast cast(gameState->curB, &caster, spells::Mode::PASSIVE, age);
+ cast.aimToUnit(def);
+
+ EXPECT_FALSE(age->canBeCastAt(gameState->curB, spells::Mode::PASSIVE, &caster, def->getPosition()));
EXPECT_TRUE(cast.castIfPossible(this));//should be possible, but with no effect (change to aimed cast check?)
diff --git a/test/mock/mock_battle_Unit.h b/test/mock/mock_battle_Unit.h
index 968379e73..3b26050c9 100644
--- a/test/mock/mock_battle_Unit.h
+++ b/test/mock/mock_battle_Unit.h
@@ -27,7 +27,6 @@ public:
MOCK_CONST_METHOD1(getEffectValue, int64_t(const spells::Spell *));
MOCK_CONST_METHOD0(getOwner, const PlayerColor());
MOCK_CONST_METHOD1(getCasterName, void(MetaString &));
- MOCK_CONST_METHOD2(getCastDescription, void(const spells::Spell *, MetaString &));
MOCK_CONST_METHOD3(getCastDescription, void(const spells::Spell *, const std::vector &, MetaString &));
MOCK_CONST_METHOD2(spendMana, void(const spells::PacketSender *, const int));
diff --git a/test/mock/mock_spells_Spell.h b/test/mock/mock_spells_Spell.h
index 006d1de62..52892e6ec 100644
--- a/test/mock/mock_spells_Spell.h
+++ b/test/mock/mock_spells_Spell.h
@@ -19,6 +19,7 @@ class SpellMock : public Spell
{
public:
MOCK_CONST_METHOD0(getIndex, int32_t());
+ MOCK_CONST_METHOD0(getLevel, int32_t());
MOCK_CONST_METHOD1(forEachSchool, void(const std::function &));
};
diff --git a/test/spells/AbilityCasterTest.cpp b/test/spells/AbilityCasterTest.cpp
new file mode 100644
index 000000000..f7863f94a
--- /dev/null
+++ b/test/spells/AbilityCasterTest.cpp
@@ -0,0 +1,84 @@
+/*
+ * AbilityCasterTest.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 "mock/mock_battle_Unit.h"
+#include "mock/mock_BonusBearer.h"
+#include "mock/mock_spells_Spell.h"
+
+#include "../../../lib/NetPacksBase.h"
+#include "../../../lib/spells/AbilityCaster.h"
+
+namespace test
+{
+using namespace ::spells;
+using namespace ::testing;
+
+
+class AbilityCasterTest : public Test
+{
+public:
+ std::shared_ptr subject;
+
+ StrictMock actualCaster;
+ BonusBearerMock casterBonuses;
+ StrictMock spellMock;
+
+protected:
+ void SetUp() override
+ {
+ ON_CALL(actualCaster, getAllBonuses(_, _, _, _)).WillByDefault(Invoke(&casterBonuses, &BonusBearerMock::getAllBonuses));
+ ON_CALL(actualCaster, getTreeVersion()).WillByDefault(Invoke(&casterBonuses, &BonusBearerMock::getTreeVersion));
+ }
+
+ void setupSubject(int skillLevel)
+ {
+ subject = std::make_shared(&actualCaster, skillLevel);
+ }
+};
+
+TEST_F(AbilityCasterTest, NonMagicAbilityIgnoresBonuses)
+{
+ EXPECT_CALL(spellMock, getLevel()).WillRepeatedly(Return(0));
+ setupSubject(1);
+
+ EXPECT_EQ(subject->getSpellSchoolLevel(&spellMock), 1);
+}
+
+TEST_F(AbilityCasterTest, MagicAbilityAffectedByGenericBonus)
+{
+ EXPECT_CALL(spellMock, getLevel()).WillRepeatedly(Return(1));
+
+ casterBonuses.addNewBonus(std::make_shared(Bonus::ONE_BATTLE, Bonus::MAGIC_SCHOOL_SKILL, Bonus::OTHER, 2, 0, 0));
+
+ EXPECT_CALL(actualCaster, getAllBonuses(_, _, _, _)).Times(AtLeast(1));
+ EXPECT_CALL(actualCaster, getTreeVersion()).Times(AtLeast(0));
+
+ setupSubject(1);
+
+ EXPECT_EQ(subject->getSpellSchoolLevel(&spellMock), 2);
+}
+
+TEST_F(AbilityCasterTest, MagicAbilityIngoresSchoolBonus)
+{
+ EXPECT_CALL(spellMock, getLevel()).WillRepeatedly(Return(1));
+
+ casterBonuses.addNewBonus(std::make_shared(Bonus::ONE_BATTLE, Bonus::MAGIC_SCHOOL_SKILL, Bonus::OTHER, 2, 0, 1));
+
+ EXPECT_CALL(actualCaster, getAllBonuses(_, _, _, _)).Times(AtLeast(1));
+ EXPECT_CALL(actualCaster, getTreeVersion()).Times(AtLeast(0));
+
+ setupSubject(1);
+
+ EXPECT_EQ(subject->getSpellSchoolLevel(&spellMock), 1);
+}
+
+
+}