diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp
index 28b4f717e..9badb95ca 100644
--- a/lib/HeroBonus.cpp
+++ b/lib/HeroBonus.cpp
@@ -76,6 +76,7 @@ const std::map<std::string, TLimiterPtr> bonusLimiterMap =
 	{"IS_UNDEAD", std::make_shared<HasAnotherBonusLimiter>(Bonus::UNDEAD)},
 	{"CREATURE_NATIVE_TERRAIN", std::make_shared<CreatureTerrainLimiter>()},
 	{"CREATURE_FACTION", std::make_shared<CreatureFactionLimiter>()},
+	{"CREATURE_LEVEL", std::make_shared<CreatureLevelLimiter>()},
 	{"OPPOSITE_SIDE", std::make_shared<OppositeSideLimiter>()},
 	{"UNIT_ON_HEXES", std::make_shared<UnitOnHexLimiter>()}
 };
@@ -2401,6 +2402,37 @@ JsonNode CreatureFactionLimiter::toJsonNode() const
 	return root;
 }
 
+CreatureLevelLimiter::CreatureLevelLimiter(uint32_t minLevel, uint32_t maxLevel) :
+	minLevel(minLevel),
+	maxLevel(maxLevel)
+{
+}
+
+ILimiter::EDecision CreatureLevelLimiter::limit(const BonusLimitationContext &context) const
+{
+	const auto *c = retrieveCreature(&context.node);
+	auto accept = c && (c->getLevel() < maxLevel && c->getLevel() >= minLevel);
+	return accept ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD; //drop bonus for non-creatures or non-native residents
+}
+
+std::string CreatureLevelLimiter::toString() const
+{
+	boost::format fmt("CreatureLevelLimiter(minLevel=%d,maxLevel=%d)");
+	fmt % minLevel % maxLevel;
+	return fmt.str();
+}
+
+JsonNode CreatureLevelLimiter::toJsonNode() const
+{
+	JsonNode root(JsonNode::JsonType::DATA_STRUCT);
+
+	root["type"].String() = "CREATURE_LEVEL_LIMITER";
+	root["parameters"].Vector().push_back(JsonUtils::intNode(minLevel));
+	root["parameters"].Vector().push_back(JsonUtils::intNode(maxLevel));
+
+	return root;
+}
+
 CreatureAlignmentLimiter::CreatureAlignmentLimiter(EAlignment Alignment)
 	: alignment(Alignment)
 {
diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h
index b1ecc435b..8e8d55ff5 100644
--- a/lib/HeroBonus.h
+++ b/lib/HeroBonus.h
@@ -12,6 +12,7 @@
 #include "GameConstants.h"
 #include "JsonNode.h"
 #include "battle/BattleHex.h"
+#include <limits>
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -1106,6 +1107,26 @@ public:
 	}
 };
 
+class DLL_LINKAGE CreatureLevelLimiter : public ILimiter //applies only to creatures of given faction
+{
+public:
+	uint32_t minLevel;
+	uint32_t maxLevel;
+	//accept all levels by default, accept creatures of minLevel <= creature->getLevel() < maxLevel
+	CreatureLevelLimiter(uint32_t minLevel = std::numeric_limits<uint32_t>::min(), uint32_t maxLevel = std::numeric_limits<uint32_t>::max());
+
+	EDecision limit(const BonusLimitationContext &context) const override;
+	std::string toString() const override;
+	JsonNode toJsonNode() const override;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<ILimiter&>(*this);
+		h & minLevel;
+		h & maxLevel;
+	}
+};
+
 class DLL_LINKAGE CreatureFactionLimiter : public ILimiter //applies only to creatures of given faction
 {
 public:
diff --git a/lib/JsonNode.cpp b/lib/JsonNode.cpp
index 37f80199f..fc2bdce52 100644
--- a/lib/JsonNode.cpp
+++ b/lib/JsonNode.cpp
@@ -758,6 +758,17 @@ std::shared_ptr<ILimiter> JsonUtils::parseLimiter(const JsonNode & limiter)
 				});
 				return factionLimiter;
 			}
+			else if(limiterType == "CREATURE_LEVEL_LIMITER")
+			{
+				auto levelLimiter = std::make_shared<CreatureLevelLimiter>();
+				if(!parameters.empty()) //If parameters is empty, level limiter works as CREATURES_ONLY limiter
+				{
+					levelLimiter->minLevel = parameters[0].Integer();
+					if(parameters[1].isNumber())
+						levelLimiter->maxLevel = parameters[1].Integer();
+				}
+				return levelLimiter;
+			}
 			else if(limiterType == "CREATURE_TERRAIN_LIMITER")
 			{
 				std::shared_ptr<CreatureTerrainLimiter> terrainLimiter = std::make_shared<CreatureTerrainLimiter>();
diff --git a/lib/mapObjects/JsonRandom.h b/lib/mapObjects/JsonRandom.h
index ee0dd6299..3b1d016eb 100644
--- a/lib/mapObjects/JsonRandom.h
+++ b/lib/mapObjects/JsonRandom.h
@@ -15,7 +15,7 @@
 VCMI_LIB_NAMESPACE_BEGIN
 
 class JsonNode;
-typedef std::vector<JsonNode> JsonVector;
+using JsonVector = std::vector<JsonNode>;
 class CRandomGenerator;
 
 struct Bonus;
diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h
index 2add5fc3f..8e670cd00 100644
--- a/lib/registerTypes/RegisterTypes.h
+++ b/lib/registerTypes/RegisterTypes.h
@@ -180,6 +180,7 @@ void registerTypesMapObjects2(Serializer &s)
 	s.template registerType<ILimiter, HasAnotherBonusLimiter>();
 	s.template registerType<ILimiter, CreatureTerrainLimiter>();
 	s.template registerType<ILimiter, CreatureFactionLimiter>();
+	s.template registerType<ILimiter, CreatureLevelLimiter>();
 	s.template registerType<ILimiter, CreatureAlignmentLimiter>();
 	s.template registerType<ILimiter, RankRangeLimiter>();
 	s.template registerType<ILimiter, StackOwnerLimiter>();