diff --git a/config/defaultMods.json b/config/defaultMods.json index 2827ff274..3bd5acbac 100644 --- a/config/defaultMods.json +++ b/config/defaultMods.json @@ -88,9 +88,11 @@ { "type" : "MOVEMENT", //Enable army movement bonus "subtype" : 1, - "val" : 0, "valueType" : "BASE_NUMBER", - "updater" : "ARMY_MOVEMENT" + "updater" : { + "type" : "ARMY_MOVEMENT", + "parameters" : [ 20, 3, 10, 700] + } }, { "type" : "MOVEMENT", //Basic sea movement diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 39136a355..23d91332e 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -2763,13 +2763,32 @@ JsonNode TimesHeroLevelUpdater::toJsonNode() const return JsonUtils::stringNode("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 ArmyMovementUpdater::createUpdatedBonus(const std::shared_ptr & b, const CBonusSystemNode & context) const { if(b->type == Bonus::MOVEMENT && context.getNodeType() == CBonusSystemNode::HERO) { + auto speed = static_cast(context).getLowestCreatureSpeed(); + si32 armySpeed = speed * base / divider; + auto counted = armySpeed * multiplier; auto newBonus = std::make_shared(*b); newBonus->source = Bonus::ARMY; - newBonus->val = static_cast(context).getArmyMovementBonus(); + newBonus->val = vstd::amin(counted, max); return newBonus; } if(b->type != Bonus::MOVEMENT) @@ -2784,7 +2803,15 @@ std::string ArmyMovementUpdater::toString() const JsonNode ArmyMovementUpdater::toJsonNode() const { - return JsonUtils::stringNode("ARMY_MOVEMENT"); + JsonNode root(JsonNode::JsonType::DATA_STRUCT); + + root["type"].String() = "ARMY_MOVEMENT"; + root["parameters"].Vector().push_back(JsonUtils::intNode(base)); + root["parameters"].Vector().push_back(JsonUtils::intNode(divider)); + root["parameters"].Vector().push_back(JsonUtils::intNode(multiplier)); + root["parameters"].Vector().push_back(JsonUtils::intNode(max)); + + return root; } TimesStackLevelUpdater::TimesStackLevelUpdater() diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 0bbb76ea3..c462a0375 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -1320,9 +1320,19 @@ public: class DLL_LINKAGE ArmyMovementUpdater : public IUpdater { public: + si32 base; + si32 divider; + si32 multiplier; + si32 max; + ArmyMovementUpdater(); + ArmyMovementUpdater(int base, int divider, int multiplier, int max); template void serialize(Handler & h, const int version) { h & static_cast(*this); + h & base; + h & divider; + h & multiplier; + h & max; } std::shared_ptr createUpdatedBonus(const std::shared_ptr & b, const CBonusSystemNode & context) const override; diff --git a/lib/JsonNode.cpp b/lib/JsonNode.cpp index 425951ba1..e4f64bbe6 100644 --- a/lib/JsonNode.cpp +++ b/lib/JsonNode.cpp @@ -864,6 +864,24 @@ static TUpdaterPtr parseUpdater(const JsonNode & updaterJson) updater->stepSize = static_cast(param[1].Integer()); return updater; } + else if (updaterJson["type"].String() == "ARMY_MOVEMENT") + { + std::shared_ptr updater = std::make_shared(); + if(updaterJson["parameters"].isVector()) + { + const auto & param = updaterJson["parameters"].Vector(); + if(param.size() < 4) + logMod->warn("Invalid ARMY_MOVEMENT parameters, using default!"); + else + { + updater->base = static_cast(param.at(0).Integer()); + updater->divider = static_cast(param.at(1).Integer()); + updater->multiplier = static_cast(param.at(2).Integer()); + updater->max = static_cast(param.at(3).Integer()); + } + return updater; + } + } else logMod->warn("Unknown updater type \"%s\"", updaterJson["type"].String()); break; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 1ebe3328b..31575493a 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -188,19 +188,17 @@ int CGHeroInstance::maxMovePoints(bool onLand) const return maxMovePointsCached(onLand, &ti); } -int CGHeroInstance::getArmyMovementBonus() const +int CGHeroInstance::getLowestCreatureSpeed() const { - return armyMovementVal; + return lowestCreatureSpeed; } void CGHeroInstance::updateArmyMovementBonus(bool onLand, const TurnInfo * ti) const { - int armySpeed = lowestSpeed(this) * 20 / 3; - - auto base = armySpeed * 10; // separate *10 is intentional to receive same rounding as in h3 - if(armyMovementVal != vstd::abetween(base, 200, 700)) // army modifier speed is limited by these values + auto realLowestSpeed = lowestSpeed(this); + if(lowestCreatureSpeed != realLowestSpeed) { - armyMovementVal = base; + lowestCreatureSpeed = realLowestSpeed; ti->updateHeroBonuses(Bonus::MOVEMENT, Selector::subtype()(!!onLand).And(Selector::sourceTypeSel(Bonus::ARMY))); } } @@ -208,7 +206,7 @@ void CGHeroInstance::updateArmyMovementBonus(bool onLand, const TurnInfo * ti) c int CGHeroInstance::maxMovePointsCached(bool onLand, const TurnInfo * ti) const { updateArmyMovementBonus(onLand, ti); - return ti->valOfBonuses(Bonus::MOVEMENT, !!onLand);; + return ti->valOfBonuses(Bonus::MOVEMENT, !!onLand); } CGHeroInstance::CGHeroInstance(): @@ -222,7 +220,7 @@ CGHeroInstance::CGHeroInstance(): level(1), exp(UNINITIALIZED_EXPERIENCE), sex(std::numeric_limits::max()), - armyMovementVal(0) + lowestCreatureSpeed(0) { setNodeType(HERO); ID = Obj::HERO; diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 779b74072..97bbac0d9 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -49,7 +49,7 @@ class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, private: std::set spells; //known spells (spell IDs) - mutable int armyMovementVal; + mutable int lowestCreatureSpeed; public: @@ -171,7 +171,7 @@ public: ui32 getTileCost(const TerrainTile & dest, const TerrainTile & from, const TurnInfo * ti) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling TerrainId getNativeTerrain() const; - ui32 getLowestCreatureSpeed() const; + int getLowestCreatureSpeed() const; si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day si32 getManaNewTurn() const; //calculate how much mana this hero is going to have the next day int getCurrentLuck(int stack=-1, bool town=false) const; @@ -213,7 +213,6 @@ public: int maxMovePointsCached(bool onLand, const TurnInfo * ti) const; //update army movement bonus void updateArmyMovementBonus(bool onLand, const TurnInfo * ti) const; - int getArmyMovementBonus() const; int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false, const TurnInfo * ti = nullptr) const;