Преглед изворни кода

Merge pull request #3379 from IvanSavenko/fix_genie_spellcasting

Fix genie spellcasting
Ivan Savenko пре 1 година
родитељ
комит
2b495a9679

+ 3 - 2
AI/BattleAI/BattleEvaluator.cpp

@@ -70,8 +70,9 @@ std::vector<BattleHex> BattleEvaluator::getBrokenWallMoatHexes() const
 std::optional<PossibleSpellcast> BattleEvaluator::findBestCreatureSpell(const CStack *stack)
 {
 	//TODO: faerie dragon type spell should be selected by server
-	SpellID creatureSpellToCast = cb->getBattle(battleID)->battleGetRandomStackSpell(CRandomGenerator::getDefault(), stack, CBattleInfoCallback::RANDOM_AIMED);
-	if(stack->hasBonusOfType(BonusType::SPELLCASTER) && stack->canCast() && creatureSpellToCast != SpellID::NONE)
+	SpellID creatureSpellToCast = cb->getBattle(battleID)->getRandomCastedSpell(CRandomGenerator::getDefault(), stack);
+
+	if(stack->canCast() && creatureSpellToCast != SpellID::NONE)
 	{
 		const CSpell * spell = creatureSpellToCast.toSpell();
 

+ 3 - 3
client/battle/BattleActionsController.cpp

@@ -616,8 +616,8 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
 		case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
 			if(targetStack && targetStackOwned && targetStack != owner.stacksController->getActiveStack() && targetStack->alive()) //only positive spells for other allied creatures
 			{
-				int spellID = owner.getBattle()->battleGetRandomStackSpell(CRandomGenerator::getDefault(), targetStack, CBattleInfoCallback::RANDOM_GENIE);
-				return spellID > -1;
+				SpellID spellID = owner.getBattle()->getRandomBeneficialSpell(CRandomGenerator::getDefault(), owner.stacksController->getActiveStack(), targetStack);
+				return spellID != SpellID::NONE;
 			}
 			return false;
 
@@ -887,7 +887,7 @@ void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterS
 	{
 		// faerie dragon can cast only one, randomly selected spell until their next move
 		//TODO: faerie dragon type spell should be selected by server
-		const auto * spellToCast = owner.getBattle()->battleGetRandomStackSpell(CRandomGenerator::getDefault(), casterStack, CBattleInfoCallback::RANDOM_AIMED).toSpell();
+		const auto * spellToCast = owner.getBattle()->getRandomCastedSpell(CRandomGenerator::getDefault(), casterStack).toSpell();
 
 		if (spellToCast)
 			creatureSpells.push_back(spellToCast);

+ 4 - 1
config/spells/timed.json

@@ -1153,7 +1153,10 @@
 		},
 		"targetCondition" : {
 			"noneOf" : {
-				"bonus.SIEGE_WEAPON" : "absolute"
+				"bonus.MIND_IMMUNITY" : "absolute",
+				"bonus.NON_LIVING" : "absolute",
+				"bonus.SIEGE_WEAPON" : "absolute",
+				"bonus.UNDEAD" : "absolute"
 			}
 		}
 	},

+ 15 - 21
lib/battle/CBattleInfoCallback.cpp

@@ -324,22 +324,6 @@ std::set<BattleHex> CBattleInfoCallback::battleGetAttackedHexes(const battle::Un
 	return attackedHexes;
 }
 
-SpellID CBattleInfoCallback::battleGetRandomStackSpell(CRandomGenerator & rand, const CStack * stack, ERandomSpell mode) const
-{
-	switch (mode)
-	{
-	case RANDOM_GENIE:
-		return getRandomBeneficialSpell(rand, stack); //target
-		break;
-	case RANDOM_AIMED:
-		return getRandomCastedSpell(rand, stack); //caster
-		break;
-	default:
-		logGlobal->error("Incorrect mode of battleGetRandomSpell (%d)", static_cast<int>(mode));
-		return SpellID::NONE;
-	}
-}
-
 const CStack* CBattleInfoCallback::battleGetStackByPos(BattleHex pos, bool onlyAlive) const
 {
 	RETURN_IF_NOT_BATTLE(nullptr);
@@ -1610,7 +1594,7 @@ std::set<const battle::Unit *> CBattleInfoCallback::battleAdjacentUnits(const ba
 	return ret;
 }
 
-SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, const CStack * subject) const
+SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, const battle::Unit * caster, const battle::Unit * subject) const
 {
 	RETURN_IF_NOT_BATTLE(SpellID::NONE);
 	//This is complete list. No spells from mods.
@@ -1658,9 +1642,19 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c
 		std::stringstream cachingStr;
 		cachingStr << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT) << "id_" << spellID.num;
 
-		if(subject->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(spellID)), Selector::all, cachingStr.str())
-		 //TODO: this ability has special limitations
-		|| !(spellID.toSpell()->canBeCast(this, spells::Mode::CREATURE_ACTIVE, subject)))
+		if(subject->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(spellID)), Selector::all, cachingStr.str()))
+			continue;
+
+		auto spellPtr = spellID.toSpell();
+		spells::Target target;
+		target.emplace_back(subject);
+
+		spells::BattleCast cast(this, caster, spells::Mode::CREATURE_ACTIVE, spellPtr);
+
+		auto m = spellPtr->battleMechanics(&cast);
+		spells::detail::ProblemImpl problem;
+
+		if (!m->canBeCastAt(target, problem))
 			continue;
 
 		switch (spellID.toEnum())
@@ -1703,7 +1697,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c
 		case SpellID::CURE: //only damaged units
 		{
 			//do not cast on affected by debuffs
-			if(!subject->canBeHealed())
+			if(subject->getFirstHPleft() == subject->getMaxHealth())
 				continue;
 		}
 			break;

+ 1 - 7
lib/battle/CBattleInfoCallback.h

@@ -52,11 +52,6 @@ struct DLL_LINKAGE BattleClientInterfaceData
 class DLL_LINKAGE CBattleInfoCallback : public virtual CBattleInfoEssentials
 {
 public:
-	enum ERandomSpell
-	{
-		RANDOM_GENIE, RANDOM_AIMED
-	};
-
 	std::optional<int> battleIsFinished() const override; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw
 
 	std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const override;
@@ -121,8 +116,7 @@ public:
 	int32_t battleGetSpellCost(const spells::Spell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
 	ESpellCastProblem battleCanCastSpell(const spells::Caster * caster, spells::Mode mode) const; //returns true if there are no general issues preventing from casting a spell
 
-	SpellID battleGetRandomStackSpell(CRandomGenerator & rand, const CStack * stack, ERandomSpell mode) const;
-	SpellID getRandomBeneficialSpell(CRandomGenerator & rand, const CStack * subject) const;
+	SpellID getRandomBeneficialSpell(CRandomGenerator & rand, const battle::Unit * caster, const battle::Unit * target) const;
 	SpellID getRandomCastedSpell(CRandomGenerator & rand, const CStack * caster) const; //called at the beginning of turn for Faerie Dragon
 
 	std::vector<PossiblePlayerBattleAction> getClientActionsForStack(const CStack * stack, const BattleClientInterfaceData & data);

+ 39 - 15
server/battles/BattleActionProcessor.cpp

@@ -411,24 +411,48 @@ bool BattleActionProcessor::doUnitSpellAction(const CBattleInfoCallback & battle
 	std::shared_ptr<const Bonus> randSpellcaster = stack->getBonus(Selector::type()(BonusType::RANDOM_SPELLCASTER));
 	std::shared_ptr<const Bonus> spellcaster = stack->getBonus(Selector::typeSubtype(BonusType::SPELLCASTER, BonusSubtypeID(spellID)));
 
-	//TODO special bonus for genies ability
-	if (randSpellcaster && battle.battleGetRandomStackSpell(gameHandler->getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_AIMED) == SpellID::NONE)
-		spellID = battle.battleGetRandomStackSpell(gameHandler->getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_GENIE);
-
-	if (spellID == SpellID::NONE)
+	if (!spellcaster && !randSpellcaster)
+	{
 		gameHandler->complain("That stack can't cast spells!");
-	else
+		return false;
+	}
+
+	if (randSpellcaster)
 	{
-		const CSpell * spell = SpellID(spellID).toSpell();
-		spells::BattleCast parameters(&battle, stack, spells::Mode::CREATURE_ACTIVE, spell);
-		int32_t spellLvl = 0;
-		if(spellcaster)
-			vstd::amax(spellLvl, spellcaster->val);
-		if(randSpellcaster)
-			vstd::amax(spellLvl, randSpellcaster->val);
-		parameters.setSpellLevel(spellLvl);
-		parameters.cast(gameHandler->spellEnv, target);
+		if (target.size() != 1)
+		{
+			gameHandler->complain("Invalid target for random spellcaster!");
+			return false;
+		}
+
+		const battle::Unit * subject = target[0].unitValue;
+		if (target[0].unitValue == nullptr)
+			subject = battle.battleGetStackByPos(target[0].hexValue, true);
+
+		if (subject == nullptr)
+		{
+			gameHandler->complain("Invalid target for random spellcaster!");
+			return false;
+		}
+
+		spellID = battle.getRandomBeneficialSpell(gameHandler->getRandomGenerator(), stack, subject);
+
+		if (spellID == SpellID::NONE)
+		{
+			gameHandler->complain("That stack can't cast spells!");
+			return false;
+		}
 	}
+
+	const CSpell * spell = SpellID(spellID).toSpell();
+	spells::BattleCast parameters(&battle, stack, spells::Mode::CREATURE_ACTIVE, spell);
+	int32_t spellLvl = 0;
+	if(spellcaster)
+		vstd::amax(spellLvl, spellcaster->val);
+	if(randSpellcaster)
+		vstd::amax(spellLvl, randSpellcaster->val);
+	parameters.setSpellLevel(spellLvl);
+	parameters.cast(gameHandler->spellEnv, target);
 	return true;
 }