diff --git a/config/heroes/conflux.json b/config/heroes/conflux.json index 6985fefc8..74b97a968 100644 --- a/config/heroes/conflux.json +++ b/config/heroes/conflux.json @@ -325,6 +325,11 @@ "class" : "elementalist", "female": true, "spellbook": [ "stoneSkin" ], + "texts" : { + "specialty" : { + "description" : "{Stone Skin}\r\n\r\nCasts Stone Skin with increased effect, based on the level of the target unit. (The bonus is greater when used on weaker units)" + } + }, "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -375,9 +380,9 @@ "specialty" : { "bonuses" : { "disruptingRay" : { - "addInfo" : 0, + "addInfo" : -2, "subtype" : "spell.disruptingRay", - "type" : "SPECIAL_PECULIAR_ENCHANT" + "type" : "SPECIAL_ADD_VALUE_ENCHANT" } } } diff --git a/config/heroes/rampart.json b/config/heroes/rampart.json index 80a8bbde6..7055cc798 100644 --- a/config/heroes/rampart.json +++ b/config/heroes/rampart.json @@ -265,8 +265,9 @@ "specialty" : { "bonuses" : { "fortune" : { + "addInfo" : 3, "subtype" : "spell.fortune", - "type" : "MAXED_SPELL" + "type" : "SPECIAL_FIXED_VALUE_ENCHANT" } } } diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 1b6c8f4a0..3d6e03f24 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -1468,6 +1468,8 @@ JsonNode subtypeToJson(Bonus::BonusType type, int subtype) case Bonus::SPECIAL_BLESS_DAMAGE: case Bonus::MAXED_SPELL: case Bonus::SPECIAL_PECULIAR_ENCHANT: + case Bonus::SPECIAL_ADD_VALUE_ENCHANT: + case Bonus::SPECIAL_FIXED_VALUE_ENCHANT: return JsonUtils::stringNode("spell." + (*VLC->spellh)[SpellID::ESpellID(subtype)]->identifier); case Bonus::IMPROVED_NECROMANCY: case Bonus::SPECIAL_UPGRADE: @@ -1556,6 +1558,8 @@ std::string Bonus::nameForBonus() const case Bonus::SPECIAL_BLESS_DAMAGE: case Bonus::MAXED_SPELL: case Bonus::SPECIAL_PECULIAR_ENCHANT: + case Bonus::SPECIAL_ADD_VALUE_ENCHANT: + case Bonus::SPECIAL_FIXED_VALUE_ENCHANT: return (*VLC->spellh)[SpellID::ESpellID(subtype)]->identifier; case Bonus::SPECIAL_UPGRADE: return CreatureID::encode(subtype) + "2" + CreatureID::encode(additionalInfo[0]); diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 501b3d939..6f0c37aaa 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -321,6 +321,8 @@ public: 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.*/\ /* end of list */ diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index 1f2ca9f61..b12b993d9 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -791,7 +791,15 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo & info) } if(isAffected) + { attackDefenceDifference += SpellID(SpellID::SLAYER).toSpell()->getPower(spLevel); + if(info.attacker->hasBonusOfType(Bonus::SPECIAL_PECULIAR_ENCHANT, SpellID::SLAYER)) + { + ui8 attackerTier = info.attacker->unitType()->level; + ui8 specialtyBonus = std::max(5 - attackerTier, 0); + attackDefenceDifference += specialtyBonus; + } + } } //bonus from attack/defense skills diff --git a/lib/spells/effects/Timed.cpp b/lib/spells/effects/Timed.cpp index 8dd5a03d2..a82186de7 100644 --- a/lib/spells/effects/Timed.cpp +++ b/lib/spells/effects/Timed.cpp @@ -151,10 +151,16 @@ void Timed::prepareEffects(SetStackEffect & sse, const Mechanics * m, const Effe std::vector converted; convertBonus(m, duration, converted); - std::shared_ptr bonus = nullptr; + std::shared_ptr peculiarBonus = nullptr; + std::shared_ptr addedValueBonus = nullptr; + std::shared_ptr fixedValueBonus = nullptr; auto casterHero = dynamic_cast(m->caster); if(casterHero) - bonus = casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, m->getSpellIndex())); + { + peculiarBonus = casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, m->getSpellIndex())); + addedValueBonus = casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_ADD_VALUE_ENCHANT, m->getSpellIndex())); + fixedValueBonus = casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_FIXED_VALUE_ENCHANT, m->getSpellIndex())); + } //TODO: does hero specialty should affects his stack casting spells? for(auto & t : target) @@ -175,16 +181,17 @@ void Timed::prepareEffects(SetStackEffect & sse, const Mechanics * m, const Effe if(describe) describeEffect(sse.battleLog, m, converted, affected); - si32 power = 0; + const auto tier = std::max(affected->creatureLevel(), 1); //don't divide by 0 for certain creatures (commanders, war machines) //Apply hero specials - peculiar enchants - const auto tier = std::max(affected->creatureLevel(), 1); //don't divide by 0 for certain creatures (commanders, war machines) - if(bonus) + if(peculiarBonus) { - switch(bonus->additionalInfo[0]) + + si32 power = 0; + switch (peculiarBonus->additionalInfo[0]) { case 0: //normal - switch(tier) + switch (tier) { case 1: case 2: @@ -199,23 +206,36 @@ void Timed::prepareEffects(SetStackEffect & sse, const Mechanics * m, const Effe power = 1; break; } - for(const Bonus & b : converted) - { - Bonus specialBonus(b); - specialBonus.val = power; //it doesn't necessarily make sense for some spells, use it wisely - specialBonus.turnsRemain = duration; - - //additional premy to given effect - buffer.push_back(specialBonus); - } break; - case 1: //only Coronius as yet + case 1: + //Coronius style specialty bonus. + //Please note that actual Coronius isnt here, because Slayer is a spell that doesnt affect monster stats and is used only in calculateDmgRange power = std::max(5 - tier, 0); - Bonus specialBonus(Bonus::N_TURNS, Bonus::PRIMARY_SKILL, Bonus::SPELL_EFFECT, power, m->getSpellIndex(), PrimarySkill::ATTACK); - specialBonus.turnsRemain = duration; - buffer.push_back(specialBonus); break; } + if(m->isNegativeSpell()) + { + //negative spells like weakness are defined in json with negative numbers, so we need do same here + power = -1 * power; + } + for(Bonus& b : buffer) + { + b.val += power; + } + } + if(addedValueBonus) + { + for(Bonus& b : buffer) + { + b.val += addedValueBonus->additionalInfo[0]; + } + } + if(fixedValueBonus) + { + for(Bonus& b : buffer) + { + b.val = fixedValueBonus->additionalInfo[0]; + } } if(casterHero && casterHero->hasBonusOfType(Bonus::SPECIAL_BLESS_DAMAGE, m->getSpellIndex())) //TODO: better handling of bonus percentages {