2
0
Laserlicht 1 жил өмнө
parent
commit
b36c05df1d

+ 3 - 1
Mods/vcmi/config/vcmi/english.json

@@ -662,5 +662,7 @@
 	"core.bonus.WIDE_BREATH.name": "Wide breath",
 	"core.bonus.WIDE_BREATH.description": "Wide breath attack (multiple hexes)",
 	"core.bonus.DISINTEGRATE.name": "Disintegrate",
-	"core.bonus.DISINTEGRATE.description": "No corpse remains after death"
+	"core.bonus.DISINTEGRATE.description": "No corpse remains after death",
+	"core.bonus.INVINCIBLE.name": "Invincible",
+	"core.bonus.INVINCIBLE.description": "Cannot be affected by anything"
 }

+ 9 - 0
config/bonuses.json

@@ -600,6 +600,15 @@
 			"icon":  "zvs/Lib1.res/DISINTEGRATE"
 		}
 
+	},
+
+	"INVINCIBLE":
+	{
+		"graphics":
+		{
+			"icon":  "zvs/Lib1.res/INVINCIBLE"
+		}
+
 	}
 }
 

+ 4 - 0
config/creatures/castle.json

@@ -10,6 +10,10 @@
 			"cavalryChargeImmunity" :
 			{
 				"type" : "CHARGE_IMMUNITY"
+			},
+			"invincible" :
+			{
+				"type" : "INVINCIBLE"
 			}
 		},
 		"graphics" :

+ 4 - 0
docs/modders/Bonus/Bonus_Types.md

@@ -1023,3 +1023,7 @@ Internal bonus, do not use
 ### DISINTEGRATE
 
 After death of unit no corpse remains
+
+### INVINCIBLE
+
+Cannot be target of attacks and spells and cannot be affected by anything

+ 3 - 0
lib/CStack.cpp

@@ -296,6 +296,9 @@ std::vector<BattleHex> CStack::meleeAttackHexes(const battle::Unit * attacker, c
 
 bool CStack::isMeleeAttackPossible(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos, BattleHex defenderPos)
 {
+	if(defender->hasBonusOfType(BonusType::INVINCIBLE))
+		return false;
+		
 	return !meleeAttackHexes(attacker, defender, attackerPos, defenderPos).empty();
 }
 

+ 6 - 0
lib/battle/CBattleInfoCallback.cpp

@@ -685,6 +685,9 @@ bool CBattleInfoCallback::battleCanAttack(const battle::Unit * stack, const batt
 	if (!stack || !target)
 		return false;
 
+	if(target->hasBonusOfType(BonusType::INVINCIBLE))
+		return false;
+
 	if(!battleMatchOwner(stack, target))
 		return false;
 
@@ -730,6 +733,9 @@ bool CBattleInfoCallback::battleCanShoot(const battle::Unit * attacker, BattleHe
 	if(!attacker || !defender)
 		return false;
 
+	if(defender->hasBonusOfType(BonusType::INVINCIBLE))
+		return false;
+
 	if(battleMatchOwner(attacker, defender) && defender->alive())
 	{
 		if(battleCanShoot(attacker))

+ 1 - 0
lib/bonuses/BonusEnum.h

@@ -179,6 +179,7 @@ class JsonNode;
 	BONUS_NAME(RESOURCES_CONSTANT_BOOST) /*Bonus that does not account for propagation and gives extra resources per day. val - resource amount, subtype - resource type*/ \
 	BONUS_NAME(RESOURCES_TOWN_MULTIPLYING_BOOST) /*Bonus that does not account for propagation and gives extra resources per day with amount multiplied by number of owned towns. val - base resource amount to be multiplied times number of owned towns, subtype - resource type*/ \
 	BONUS_NAME(DISINTEGRATE) /* after death no corpse remains */ \
+	BONUS_NAME(INVINCIBLE) /* cannot be target of attacks and spells and cannot be affected by anything */ \
 	/* end of list */
 
 

+ 4 - 1
lib/spells/BattleSpellMechanics.cpp

@@ -228,8 +228,11 @@ bool BattleSpellMechanics::canBeCastAt(const Target & target, Problem & problem)
 			mainTarget = battle()->battleGetUnitByPos(target.front().hexValue, true);
 		}
 
-		if (mainTarget && mainTarget == caster)
+		if(mainTarget && mainTarget == caster)
 			return false; // can't cast on self
+
+		if(mainTarget && mainTarget->hasBonusOfType(BonusType::INVINCIBLE))
+			return false;
 	}
 
 	return effects->applicable(problem, this, target, spellTarget);

+ 4 - 0
lib/spells/CSpellHandler.cpp

@@ -420,6 +420,10 @@ int64_t CSpell::adjustRawDamage(const spells::Caster * caster, const battle::Uni
 			ret *= 100 + bearer->valOfBonuses(BonusType::MORE_DAMAGE_FROM_SPELL, BonusSubtypeID(id));
 			ret /= 100;
 		}
+
+		//invincible
+		if(bearer->hasBonusOfType(BonusType::INVINCIBLE))
+			ret = 0;
 	}
 	ret = caster->getSpellBonus(this, ret, affectedCreature);
 	return ret;