Explorar o código

Merge pull request #3116 from IvanSavenko/bonus_fixes

Bonuses fixes
Ivan Savenko %!s(int64=2) %!d(string=hai) anos
pai
achega
207968ced3

+ 14 - 1
docs/modders/Bonus/Bonus_Types.md

@@ -60,8 +60,14 @@ Allows flying movement for affected heroes
 
 Eliminates terrain penalty on certain terrain types for affected heroes (Nomads ability).
 
+Note: to eliminate all terrain penalties see ROUGH_TERRAIN_DISCOUNT bonus
+
 - subtype: type of terrain
 
+### TERRAIN_NATIVE
+
+Affected units will view any terrain as native
+
 ### PRIMARY_SKILL
 
 Changes selected primary skill for affected heroes and units
@@ -252,6 +258,7 @@ Allows creature upgrade for affected armies
 Changes duration of timed spells casted by affected hero
 
 - val: additional duration, turns
+- subtype: optional, identifier of affected spells, or all if not set
 
 ### SPELL
 
@@ -791,7 +798,7 @@ Affected unit is permanently enchanted with a spell, that is cast again every tu
 
 Affected unit is immune to all spell with level below or equal to value of this bonus
 
-- val: level to which this unit is immune to
+- val: level up to which this unit is immune to
 
 TODO: additional info?
 
@@ -893,6 +900,12 @@ Affected unit will never retaliate to an attack (Blind, Paralyze)
 
 # Others
 
+### NEGATIVE_EFFECTS_IMMUNITY
+
+Affected unit is immune to all negative spells of specified spell school
+
+- subtype: affected spell school
+
 ### BLOCK_MAGIC_ABOVE
 
 Blocks casting spells of the level above specified one in battles affected by this bonus

+ 3 - 4
lib/BasicTypes.cpp

@@ -33,14 +33,13 @@ bool INativeTerrainProvider::isNativeTerrain(TerrainId terrain) const
 
 TerrainId AFactionMember::getNativeTerrain() const
 {
-	constexpr auto any = TerrainId(ETerrainId::ANY_TERRAIN);
-	const std::string cachingStringNoTerrainPenalty = "type_NO_TERRAIN_PENALTY_sANY";
-	static const auto selectorNoTerrainPenalty = Selector::typeSubtype(BonusType::NO_TERRAIN_PENALTY, BonusSubtypeID(any));
+	const std::string cachingStringNoTerrainPenalty = "type_TERRAIN_NATIVE_NONE";
+	static const auto selectorNoTerrainPenalty = Selector::typeSubtype(BonusType::TERRAIN_NATIVE, BonusSubtypeID());
 
 	//this code is used in the CreatureTerrainLimiter::limit to setup battle bonuses
 	//and in the CGHeroInstance::getNativeTerrain() to setup movement bonuses or/and penalties.
 	return getBonusBearer()->hasBonus(selectorNoTerrainPenalty, cachingStringNoTerrainPenalty)
-		? any : VLC->factions()->getById(getFaction())->getNativeTerrain();
+		? TerrainId::ANY_TERRAIN : VLC->factions()->getById(getFaction())->getNativeTerrain();
 }
 
 int32_t AFactionMember::magicResistance() const

+ 2 - 0
lib/JsonNode.cpp

@@ -446,6 +446,7 @@ static void loadBonusSubtype(BonusSubtypeID & subtype, BonusType type, const Jso
 		case BonusType::SPELLS_OF_SCHOOL:
 		case BonusType::SPELL_DAMAGE_REDUCTION:
 		case BonusType::SPELL_SCHOOL_IMMUNITY:
+		case BonusType::NEGATIVE_EFFECTS_IMMUNITY:
 		{
 			VLC->identifiers()->requestIdentifier( "spellSchool", node, [&subtype](int32_t identifier)
 			{
@@ -485,6 +486,7 @@ static void loadBonusSubtype(BonusSubtypeID & subtype, BonusType type, const Jso
 			break;
 		}
 		case BonusType::SPELL_IMMUNITY:
+		case BonusType::SPELL_DURATION:
 		case BonusType::SPECIAL_ADD_VALUE_ENCHANT:
 		case BonusType::SPECIAL_FIXED_VALUE_ENCHANT:
 		case BonusType::SPECIAL_PECULIAR_ENCHANT:

+ 1 - 0
lib/bonuses/BonusEnum.h

@@ -169,6 +169,7 @@ class JsonNode;
 	BONUS_NAME(MAX_LEARNABLE_SPELL_LEVEL) /*This can work as wisdom before. val = max learnable spell level*/\
 	BONUS_NAME(SPELL_SCHOOL_IMMUNITY) /*This bonus will work as spell school immunity for all spells, subtype - spell school: 0 - air, 1 - fire, 2 - water, 3 - earth. Any is not handled for reducing overlap from LEVEL_SPELL_IMMUNITY*/\
 	BONUS_NAME(NEGATIVE_EFFECTS_IMMUNITY) /*This bonus will work as spell school immunity for negative effects from spells of school, subtype - spell school: -1 - any, 0 - air, 1 - fire, 2 - water, 3 - earth*/\
+	BONUS_NAME(TERRAIN_NATIVE)
 	/* end of list */
 
 

+ 5 - 1
lib/mapObjects/CGHeroInstance.cpp

@@ -700,7 +700,11 @@ int32_t CGHeroInstance::getEffectPower(const spells::Spell * spell) const
 
 int32_t CGHeroInstance::getEnchantPower(const spells::Spell * spell) const
 {
-	return getPrimSkillLevel(PrimarySkill::SPELL_POWER) + valOfBonuses(BonusType::SPELL_DURATION);
+	int32_t spellpower = getPrimSkillLevel(PrimarySkill::SPELL_POWER);
+	int32_t durationCommon = valOfBonuses(BonusType::SPELL_DURATION, BonusSubtypeID());
+	int32_t durationSpecific = valOfBonuses(BonusType::SPELL_DURATION, BonusSubtypeID(spell->getId()));
+
+	return spellpower + durationCommon + durationSpecific;
 }
 
 int64_t CGHeroInstance::getEffectValue(const spells::Spell * spell) const

+ 2 - 2
lib/pathfinder/TurnInfo.cpp

@@ -22,8 +22,8 @@ TurnInfo::BonusCache::BonusCache(const TConstBonusListPtr & bl)
 {
 	for(const auto & terrain : VLC->terrainTypeHandler->objects)
 	{
-		noTerrainPenalty.push_back(static_cast<bool>(
-				bl->getFirst(Selector::type()(BonusType::NO_TERRAIN_PENALTY).And(Selector::subtype()(BonusSubtypeID(terrain->getId()))))));
+		auto selector = Selector::typeSubtype(BonusType::NO_TERRAIN_PENALTY, BonusSubtypeID(terrain->getId()));
+		noTerrainPenalty.push_back(static_cast<bool>(bl->getFirst(selector)));
 	}
 
 	freeShipBoarding = static_cast<bool>(bl->getFirst(Selector::type()(BonusType::FREE_SHIP_BOARDING)));