mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-29 23:07:48 +02:00
Better support for Adela specialty (+new modding functionality for it)
Fixes Adela specialty that was apparently broken back in #1518 and replaced with logic that was clearly not tested - it was neither functional, nor it was following H3 behavior. - `HAS_ANOTHER_BONUS_LIMITER` now accepts `null` in place of bonus type, for cases when limiting is needed by bonus source or bonus subtype. This allows Adela Bless specialty to always work, irregardless of which bonuses are provided by Bless. - Implemented `DIVIDE_STACK_LEVEL` updater that functions same as `TIMES_STACK_LEVEL`, but it divides bonus value, instead of multiplying it (to make Adela specialty weaker for high-tier units, as in H3) - Implemented `TIMES_HERO_LEVEL_DIVIDE_STACK_LEVEL` updater that combines two existing updaters, to implement `val * heroLevel / unitLevel` formula needed for Adela specialty - Removed deprecated `ARMY_MOVEMENT` updater. Its functionality has already been removed in 1.6.X releases, and it was remaining only as a placeholder - Updated modding documentation to account for these changes & to remove some TODO's Fixed regression from #777 that could led to either duplicated bonuses or to multiple application of updaters. It introduced double-recursion - node parents were gathered recursively, and then bonuses were also collected recursively within each parent. This created situation where updater could be applied different number of times. For example, hero bonus that is propagated to unit in combat could be selected directly, or via hero->combat unit chain, or via hero->garrison unit->combat unit chains, leading to different calls to updaters if updater handles garrison unit node type
This commit is contained in:
@@ -108,72 +108,53 @@ JsonNode TimesHeroLevelUpdater::toJsonNode() const
|
||||
return JsonNode("TIMES_HERO_LEVEL");
|
||||
}
|
||||
|
||||
ArmyMovementUpdater::ArmyMovementUpdater():
|
||||
base(20),
|
||||
divider(3),
|
||||
multiplier(10),
|
||||
max(700)
|
||||
{
|
||||
}
|
||||
|
||||
ArmyMovementUpdater::ArmyMovementUpdater(int base, int divider, int multiplier, int max):
|
||||
base(base),
|
||||
divider(divider),
|
||||
multiplier(multiplier),
|
||||
max(max)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> ArmyMovementUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context)
|
||||
std::shared_ptr<Bonus> TimesHeroLevelDivideStackLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context)
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::HERO)
|
||||
{
|
||||
auto newBonus = TimesHeroLevelUpdater::createUpdatedBonus(b, context);
|
||||
newBonus->updater = divideStackLevel;
|
||||
return newBonus;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
std::string ArmyMovementUpdater::toString() const
|
||||
std::string TimesHeroLevelDivideStackLevelUpdater::toString() const
|
||||
{
|
||||
return "ArmyMovementUpdater";
|
||||
return "TimesHeroLevelDivideStackLevelUpdater";
|
||||
}
|
||||
|
||||
JsonNode ArmyMovementUpdater::toJsonNode() const
|
||||
JsonNode TimesHeroLevelDivideStackLevelUpdater::toJsonNode() const
|
||||
{
|
||||
JsonNode root;
|
||||
|
||||
root["type"].String() = "ARMY_MOVEMENT";
|
||||
root["parameters"].Vector().emplace_back(base);
|
||||
root["parameters"].Vector().emplace_back(divider);
|
||||
root["parameters"].Vector().emplace_back(multiplier);
|
||||
root["parameters"].Vector().emplace_back(max);
|
||||
|
||||
return root;
|
||||
return JsonNode("TIMES_HERO_LEVEL_DIVIDE_STACK_LEVEL");
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> TimesStackLevelUpdater::apply(const std::shared_ptr<Bonus> & b, int level) const
|
||||
{
|
||||
auto newBonus = std::make_shared<Bonus>(*b);
|
||||
newBonus->val *= level;
|
||||
newBonus->updater = nullptr; // prevent double-apply
|
||||
return newBonus;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> TimesStackLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context)
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::STACK_INSTANCE || context.getNodeType() == CBonusSystemNode::COMMANDER)
|
||||
{
|
||||
int level = dynamic_cast<const CStackInstance &>(context).getLevel();
|
||||
auto newBonus = std::make_shared<Bonus>(*b);
|
||||
newBonus->val *= level;
|
||||
return newBonus;
|
||||
return apply(b, level);
|
||||
}
|
||||
else if(context.getNodeType() == CBonusSystemNode::STACK_BATTLE)
|
||||
|
||||
if(context.getNodeType() == CBonusSystemNode::STACK_BATTLE)
|
||||
{
|
||||
const auto & stack = dynamic_cast<const CStack &>(context);
|
||||
//update if stack doesn't have an instance (summons, war machines)
|
||||
if(stack.base == nullptr)
|
||||
{
|
||||
int level = stack.unitType()->getLevel();
|
||||
auto newBonus = std::make_shared<Bonus>(*b);
|
||||
newBonus->val *= level;
|
||||
return newBonus;
|
||||
}
|
||||
return apply(b, stack.unitType()->getLevel());
|
||||
|
||||
// If these are not handled here, the final outcome may potentially be incorrect.
|
||||
else
|
||||
{
|
||||
int level = dynamic_cast<const CStackInstance*>(stack.base)->getLevel();
|
||||
auto newBonus = std::make_shared<Bonus>(*b);
|
||||
newBonus->val *= level;
|
||||
return newBonus;
|
||||
}
|
||||
int level = dynamic_cast<const CStackInstance*>(stack.base)->getLevel();
|
||||
return apply(b, level);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
@@ -188,6 +169,46 @@ JsonNode TimesStackLevelUpdater::toJsonNode() const
|
||||
return JsonNode("TIMES_STACK_LEVEL");
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> DivideStackLevelUpdater::apply(const std::shared_ptr<Bonus> & b, int level) const
|
||||
{
|
||||
auto newBonus = std::make_shared<Bonus>(*b);
|
||||
newBonus->val /= level;
|
||||
newBonus->updater = nullptr; // prevent double-apply
|
||||
return newBonus;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> DivideStackLevelUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context)
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::STACK_INSTANCE || context.getNodeType() == CBonusSystemNode::COMMANDER)
|
||||
{
|
||||
int level = dynamic_cast<const CStackInstance &>(context).getLevel();
|
||||
return apply(b, level);
|
||||
}
|
||||
|
||||
if(context.getNodeType() == CBonusSystemNode::STACK_BATTLE)
|
||||
{
|
||||
const auto & stack = dynamic_cast<const CStack &>(context);
|
||||
//update if stack doesn't have an instance (summons, war machines)
|
||||
if(stack.base == nullptr)
|
||||
return apply(b, stack.unitType()->getLevel());
|
||||
|
||||
// If these are not handled here, the final outcome may potentially be incorrect.
|
||||
int level = dynamic_cast<const CStackInstance*>(stack.base)->getLevel();
|
||||
return apply(b, level);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
std::string DivideStackLevelUpdater::toString() const
|
||||
{
|
||||
return "DivideStackLevelUpdater";
|
||||
}
|
||||
|
||||
JsonNode DivideStackLevelUpdater::toJsonNode() const
|
||||
{
|
||||
return JsonNode("DIVIDE_STACK_LEVEL");
|
||||
}
|
||||
|
||||
std::string OwnerUpdater::toString() const
|
||||
{
|
||||
return "OwnerUpdater";
|
||||
|
||||
Reference in New Issue
Block a user