Browse Source

vcmi: allow to configure army movement counter

It is not hardcoded now. MOVEMENT.TXT is still not read,
but ARMY_MOVEMENT updater parameters can be specified in json.
There is a 4 parameters:
1. Base - base value (firstly lowest speed is multiplied by it)
2. Divider - base value is integrally divided by it
3. Multiplier - result value will be multiplied by it
4. Max - maximum allowed movement from army.

Vanilla values is in defaultMods.json

Fixes: https://bugs.vcmi.eu/view.php?id=3209
Konstantin 2 years ago
parent
commit
0adffc824f

+ 4 - 2
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

+ 29 - 2
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<Bonus> ArmyMovementUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
 {
 	if(b->type == Bonus::MOVEMENT && context.getNodeType() == CBonusSystemNode::HERO)
 	{
+		auto speed = static_cast<const CGHeroInstance &>(context).getLowestCreatureSpeed();
+		si32 armySpeed = speed * base / divider;
+		auto counted = armySpeed * multiplier;
 		auto newBonus = std::make_shared<Bonus>(*b);
 		newBonus->source = Bonus::ARMY;
-		newBonus->val = static_cast<const CGHeroInstance &>(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()

+ 10 - 0
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 <typename Handler> void serialize(Handler & h, const int version)
 	{
 		h & static_cast<IUpdater &>(*this);
+		h & base;
+		h & divider;
+		h & multiplier;
+		h & max;
 	}
 
 	std::shared_ptr<Bonus> createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const override;

+ 18 - 0
lib/JsonNode.cpp

@@ -864,6 +864,24 @@ static TUpdaterPtr parseUpdater(const JsonNode & updaterJson)
 				updater->stepSize = static_cast<int>(param[1].Integer());
 			return updater;
 		}
+		else if (updaterJson["type"].String() == "ARMY_MOVEMENT")
+		{
+			std::shared_ptr<ArmyMovementUpdater> updater = std::make_shared<ArmyMovementUpdater>();
+			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<si32>(param.at(0).Integer());
+					updater->divider = static_cast<si32>(param.at(1).Integer());
+					updater->multiplier = static_cast<si32>(param.at(2).Integer());
+					updater->max = static_cast<si32>(param.at(3).Integer());
+				}
+				return updater;
+			}
+		}
 		else
 			logMod->warn("Unknown updater type \"%s\"", updaterJson["type"].String());
 		break;

+ 7 - 9
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<ui8>::max()),
-	armyMovementVal(0)
+	lowestCreatureSpeed(0)
 {
 	setNodeType(HERO);
 	ID = Obj::HERO;

+ 2 - 3
lib/mapObjects/CGHeroInstance.h

@@ -49,7 +49,7 @@ class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator,
 
 private:
 	std::set<SpellID> 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;