Browse Source

Merge pull request #4623 from Laserlicht/invincible_bonus

INVINCIBLE bonus
Ivan Savenko 1 year ago
parent
commit
cb96b9959e

+ 4 - 1
AI/BattleAI/BattleEvaluator.cpp

@@ -337,7 +337,10 @@ BattleAction BattleEvaluator::moveOrAttack(const CStack * stack, BattleHex hex,
 	}
 	else
 	{
-		return BattleAction::makeMove(stack, hex);
+		if(stack->position == hex)
+			return BattleAction::makeDefend(stack);
+		else
+			return BattleAction::makeMove(stack, hex);
 	}
 }
 

+ 8 - 0
AI/StupidAI/StupidAI.cpp

@@ -296,7 +296,11 @@ BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stac
 	for(auto hex : hexes)
 	{
 		if(vstd::contains(avHexes, hex))
+		{
+			if(stack->position == hex)
+				return BattleAction::makeDefend(stack);
 			return BattleAction::makeMove(stack, hex);
+		}
 
 		if(stack->coversPos(hex))
 		{
@@ -336,7 +340,11 @@ BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stac
 			}
 
 			if(vstd::contains(avHexes, currentDest))
+			{
+				if(stack->position == currentDest)
+					return BattleAction::makeDefend(stack);
 				return BattleAction::makeMove(stack, currentDest);
+			}
 
 			currentDest = reachability.predecessors[currentDest];
 		}

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

@@ -665,5 +665,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" :

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

@@ -1022,4 +1022,8 @@ Internal bonus, do not use
 
 ### DISINTEGRATE
 
-After death of unit no corpse remains
+When a unit affected by this bonus dies, no corpse is left behind
+
+### INVINCIBLE
+
+The unit affected by this bonus cannot be target of attacks or spells

+ 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 or spells */ \
 	/* 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

@@ -425,6 +425,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;