2
0
Эх сурвалжийг харах

Rewrite Beneficial spell selection

AlexVinS 11 жил өмнө
parent
commit
64dccfec80
1 өөрчлөгдсөн 105 нэмэгдсэн , 72 устгасан
  1. 105 72
      lib/CBattleCallback.cpp

+ 105 - 72
lib/CBattleCallback.cpp

@@ -1891,93 +1891,126 @@ std::set<const CStack*> CBattleInfoCallback:: batteAdjacentCreatures(const CStac
 SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) const
 {
 	RETURN_IF_NOT_BATTLE(SpellID::NONE);
-	std::vector<SpellID> possibleSpells;
-
-	for(const CSpell *spell : VLC->spellh->objects)
+	//This is complete list. No spells from mods.
+	//todo: this should be Spellbook of caster Stack
+	static const std::set<SpellID> allPossibleSpells = 
+	{
+		SpellID::AIR_SHIELD,
+		SpellID::ANTI_MAGIC,
+		SpellID::BLESS,		
+		SpellID::BLOODLUST,
+		SpellID::COUNTERSTRIKE,
+		SpellID::CURE,
+		SpellID::FIRE_SHIELD,		
+		SpellID::FORTUNE,
+		SpellID::HASTE,
+		SpellID::MAGIC_MIRROR,
+		SpellID::MIRTH,				
+		SpellID::PRAYER,
+		SpellID::PRECISION,
+		SpellID::PROTECTION_FROM_AIR,
+		SpellID::PROTECTION_FROM_EARTH,
+		SpellID::PROTECTION_FROM_FIRE,
+		SpellID::PROTECTION_FROM_WATER,
+		SpellID::SHIELD,
+		SpellID::SLAYER,
+		SpellID::STONE_SKIN
+	};
+	std::vector<SpellID> beneficialSpells;
+	
+	auto getAliveEnemy = [=](const std::function<bool(const CStack * )> & pred)
 	{
-		if (spell->isPositive() && !spell->isRisingSpell()) //only positive and not rising
+		return getStackIf([=](const CStack * stack)
 		{
-			if (subject->hasBonusFrom(Bonus::SPELL_EFFECT, spell->id)
-				|| battleCanCastThisSpellHere(subject->owner, spell, ECastingMode::CREATURE_ACTIVE_CASTING, subject->position) != ESpellCastProblem::OK)
-				continue;
+			return pred(stack) && stack->owner != subject->owner && stack->alive();
+		});
+	};
 
-			switch (spell->id)
-			{
-			case SpellID::SHIELD:
-			case SpellID::FIRE_SHIELD: // not if all enemy units are shooters
-				{
-					auto walker = getStackIf([&](const CStack *stack) //look for enemy, non-shooting stack
-					{
-						return stack->owner != subject->owner && !stack->shots;
-					});
+	for(const SpellID spellID : allPossibleSpells)
+	{
+		if (subject->hasBonusFrom(Bonus::SPELL_EFFECT, spellID)
+			//TODO: this ability has special limitations
+			|| battleCanCastThisSpellHere(subject->owner, spellID.toSpell(), ECastingMode::CREATURE_ACTIVE_CASTING, subject->position) != ESpellCastProblem::OK)
+			continue;
 
-					if (!walker)
-						continue;
-				}
-				break;
-			case SpellID::AIR_SHIELD: //only against active shooters
+		switch (spellID)
+		{
+		case SpellID::SHIELD:
+		case SpellID::FIRE_SHIELD: // not if all enemy units are shooters
+			{				
+				auto walker = getAliveEnemy([&](const CStack * stack) //look for enemy, non-shooting stack
 				{
+					return !stack->shots;
+				});
 
-					auto shooter = getStackIf([&](const CStack *stack) //look for enemy, non-shooting stack
-					{
-						return stack->owner != subject->owner && stack->hasBonusOfType(Bonus::SHOOTER) && stack->shots;
-					});
-					if (!shooter)
-						continue;
-				}
-				break;
-			case SpellID::ANTI_MAGIC:
-			case SpellID::MAGIC_MIRROR:
-				{
-					if (!battleHasHero(subject->attackerOwned)) //only if there is enemy hero
-						continue;
-				}
-				break;
-			case SpellID::CURE: //only damaged units - what about affected by curse?
-				{
-					if (subject->firstHPleft >= subject->MaxHealth())
-						continue;
-				}
-				break;
-			case SpellID::BLOODLUST:
-				{
-					if (subject->shots) //if can shoot - only if enemy uits are adjacent
-						continue;
-				}
-				break;
-			case SpellID::PRECISION:
+				if (!walker)
+					continue;
+			}
+			break;
+		case SpellID::AIR_SHIELD: //only against active shooters
+			{
+				auto shooter = getAliveEnemy([&](const CStack * stack) //look for enemy, non-shooting stack
 				{
-					if (!(subject->hasBonusOfType(Bonus::SHOOTER) && subject->shots))
-						continue;
-				}
-				break;
-			case SpellID::SLAYER://only if monsters are present
+					return stack->hasBonusOfType(Bonus::SHOOTER) && stack->shots;
+				});
+				if (!shooter)
+					continue;
+			}
+			break;
+		case SpellID::ANTI_MAGIC:
+		case SpellID::MAGIC_MIRROR:
+		case SpellID::PROTECTION_FROM_AIR:
+		case SpellID::PROTECTION_FROM_EARTH:
+		case SpellID::PROTECTION_FROM_FIRE:
+		case SpellID::PROTECTION_FROM_WATER:				
+			{
+				const ui8 enemySide = (ui8)subject->attackerOwned;
+				//todo: only if enemy has spellbook
+				if (!battleHasHero(enemySide)) //only if there is enemy hero
+					continue;
+			}
+			break;
+		case SpellID::CURE: //only damaged units
+			{
+				//do not cast on affected by debuffs
+				if (subject->firstHPleft >= subject->MaxHealth())
+					continue;
+			}
+			break;
+		case SpellID::BLOODLUST:
+			{
+				if (subject->shots) //if can shoot - only if enemy uits are adjacent
+					continue;
+			}
+			break;
+		case SpellID::PRECISION:
+			{
+				if (!(subject->hasBonusOfType(Bonus::SHOOTER) && subject->shots))
+					continue;
+			}
+			break;
+		case SpellID::SLAYER://only if monsters are present
+			{
+				auto kingMonster = getAliveEnemy([&](const CStack *stack) -> bool //look for enemy, non-shooting stack
 				{
-					auto kingMonster = getStackIf([&](const CStack *stack) -> bool //look for enemy, non-shooting stack
-					{
-						const auto isKing = Selector::type(Bonus::KING1)
-							.Or(Selector::type(Bonus::KING2))
-							.Or(Selector::type(Bonus::KING3));
+					const auto isKing = Selector::type(Bonus::KING1)
+						.Or(Selector::type(Bonus::KING2))
+						.Or(Selector::type(Bonus::KING3));
 
-						return stack->owner != subject->owner  &&  stack->hasBonus(isKing);
-					});
+					return stack->hasBonus(isKing);
+				});
 
-					if (!kingMonster)
-						continue;
-				}
-				break;
-			case SpellID::TELEPORT: //issue 1928
-			case SpellID::CLONE: //not allowed
-				continue;
-				break;
+				if (!kingMonster)
+					continue;
 			}
-			possibleSpells.push_back(spell->id);
+			break;
 		}
+		beneficialSpells.push_back(spellID);		
 	}
 
-	if(!possibleSpells.empty())
+	if(!beneficialSpells.empty())
 	{
-		return *RandomGeneratorUtil::nextItem(possibleSpells, gs->getRandomGenerator());
+		return *RandomGeneratorUtil::nextItem(beneficialSpells, gs->getRandomGenerator());
 	}
 	else
 	{