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

Merge pull request #5897 from Opuszek/fix_magic_mirror_spell

Fix magic_mirror spell
Ivan Savenko 3 сар өмнө
parent
commit
ac06f3d557

+ 2 - 1
config/spells/timed.json

@@ -1311,7 +1311,8 @@
 				"bonus.MIND_IMMUNITY":"normal",
 				"bonus.UNDEAD":"normal",
 				"bonus.NON_LIVING":"normal",
-				"bonus.MECHANICAL":"normal"
+				"bonus.MECHANICAL":"normal",
+				"spell.magicMirror":"normal"
 			}
 		},
 		"flags" : {

+ 53 - 8
lib/spells/BattleSpellMechanics.cpp

@@ -378,6 +378,7 @@ void BattleSpellMechanics::cast(ServerCallback * server, const Target & target)
 	case Mode::ENCHANTER:
 	case Mode::HERO:
 	case Mode::PASSIVE:
+	case Mode::MAGIC_MIRROR:
 		{
 			MetaString line;
 			caster->getCastDescription(owner, affectedUnits, line);
@@ -449,6 +450,15 @@ void BattleSpellMechanics::beforeCast(BattleSpellCast & sc, vstd::RNG & rng, con
 			affectedUnits.push_back(unit);
 	};
 
+	if (!target.empty())
+	{
+		const battle::Unit * targetedUnit = battle()->battleGetUnitByPos(target.front().hexValue, true);
+		if (isReflected(targetedUnit, rng)) {
+			reflect(sc, rng, targetedUnit);
+			return;
+			}
+	}
+
 	//prepare targets
 	effectsToApply = effects->prepare(this, target, spellTarget);
 
@@ -469,18 +479,48 @@ void BattleSpellMechanics::beforeCast(BattleSpellCast & sc, vstd::RNG & rng, con
 		});
 	}
 
-	if(mode == Mode::MAGIC_MIRROR)
-	{
-		if(caster->getHeroCaster() == nullptr)
-		{
-			sc.reflectedCres.insert(caster->getCasterUnitId());
-		}
-	}
-
 	for(const auto * unit : resisted)
 		sc.resistedCres.insert(unit->unitId());
 }
 
+bool BattleSpellMechanics::isReflected(const battle::Unit * unit, vstd::RNG & rng)
+{
+	if (unit == nullptr)
+		return false;
+	bool isDirectSpell = owner->getTargetType() == AimType::CREATURE && !isMassive();
+	bool spellIsReflectable = isDirectSpell && (mode == Mode::HERO || mode == Mode::MAGIC_MIRROR) && isNegativeSpell();
+	bool targetCanReflectSpell = spellIsReflectable && unit->getAllBonuses(Selector::type()(BonusType::MAGIC_MIRROR))->size()>0;
+	return targetCanReflectSpell && rng.nextInt(0, 99) < unit->valOfBonuses(BonusType::MAGIC_MIRROR);
+}
+
+void BattleSpellMechanics::reflect(BattleSpellCast & sc, vstd::RNG & rng, const battle::Unit * unit)
+{
+	auto otherSide = battle()->otherSide(unit->unitSide());
+	auto newTarget = getRandomUnit(rng, otherSide);
+	if (newTarget == nullptr)
+		throw std::runtime_error("Failed to find random unit to reflect spell!");
+	auto reflectedTo = newTarget->getPosition();
+
+	mode = Mode::MAGIC_MIRROR;
+	sc.reflectedCres.insert(unit->unitId());
+	sc.tile = reflectedTo;
+
+	if (!isReceptive(newTarget))
+		sc.resistedCres.insert(newTarget->unitId());    //A spell can be reflected to then resisted by an immune unit. Consistent with the original game.
+
+	beforeCast(sc, rng, { Destination(reflectedTo) });
+}
+
+const battle::Unit * BattleSpellMechanics::getRandomUnit(vstd::RNG & rng, const BattleSide & side)
+{
+	auto targets = battle()->getBattle()->getUnitsIf([&side](const battle::Unit * unit)
+	{
+		return unit->unitSide() == side && unit->isValidTarget(false) &&
+			!unit->hasBonusOfType(BonusType::SIEGE_WEAPON);
+	});
+	return !targets.empty() ? (*RandomGeneratorUtil::nextItem(targets, rng)) : nullptr;
+}
+
 void BattleSpellMechanics::castEval(ServerCallback * server, const Target & target)
 {
 	affectedUnits.clear();
@@ -702,6 +742,11 @@ bool BattleSpellMechanics::isReceptive(const battle::Unit * target) const
 	return targetCondition->isReceptive(this, target);
 }
 
+bool BattleSpellMechanics::isSmart() const
+{
+	return mode != Mode::MAGIC_MIRROR && BaseMechanics::isSmart();
+}
+
 BattleHexArray BattleSpellMechanics::rangeInHexes(const BattleHex & centralHex) const
 {
 	if(isMassive() || !centralHex.isValid())

+ 4 - 0
lib/spells/BattleSpellMechanics.h

@@ -59,6 +59,7 @@ public:
 
 	/// Returns true if spell can be cast on unit
 	bool isReceptive(const battle::Unit * target) const override;
+	bool isSmart() const override;
 
 	/// Returns list of hexes that are affected by spell assuming cast at centralHex
 	BattleHexArray rangeInHexes(const BattleHex & centralHex) const override;
@@ -75,6 +76,9 @@ private:
 	effects::Effects::EffectsToApply effectsToApply;
 
 	void beforeCast(BattleSpellCast & sc, vstd::RNG & rng, const Target & target);
+	bool isReflected(const battle::Unit * unit, vstd::RNG & rng);
+	void reflect(BattleSpellCast & sc, vstd::RNG & rng, const battle::Unit * unit);
+	const battle::Unit * getRandomUnit(vstd::RNG & rng, const BattleSide & side);
 
 	std::set<const battle::Unit *> collectTargets() const;
 

+ 0 - 58
lib/spells/ISpellMechanics.cpp

@@ -151,20 +151,6 @@ BattleCast::BattleCast(const CBattleInfoCallback * cb_, const Caster * caster_,
 {
 }
 
-BattleCast::BattleCast(const BattleCast & orig, const Caster * caster_)
-	: spell(orig.spell),
-	cb(orig.cb),
-	caster(caster_),
-	mode(Mode::MAGIC_MIRROR),
-	magicSkillLevel(orig.magicSkillLevel),
-	effectPower(orig.effectPower),
-	effectDuration(orig.effectDuration),
-	effectValue(orig.effectValue),
-	smart(true),
-	massive(false)
-{
-}
-
 BattleCast::~BattleCast() = default;
 
 const CSpell * BattleCast::getSpell() const
@@ -251,51 +237,7 @@ void BattleCast::cast(ServerCallback * server, Target target)
 
 	auto m = spell->battleMechanics(this);
 
-	const battle::Unit * mainTarget = nullptr;
-
-	if(target.front().unitValue)
-	{
-		mainTarget = target.front().unitValue;
-	}
-	else if(target.front().hexValue.isValid())
-	{
-		mainTarget = cb->battleGetUnitByPos(target.front().hexValue, true);
-	}
-
-	bool tryMagicMirror = (mainTarget != nullptr) && (mode == Mode::HERO || mode == Mode::CREATURE_ACTIVE);//TODO: recheck
-	tryMagicMirror = tryMagicMirror && (mainTarget->unitOwner() != caster->getCasterOwner()) && !spell->isPositive();//TODO: recheck
-
 	m->cast(server, target);
-
-	//Magic Mirror effect
-	if(tryMagicMirror)
-	{
-		const std::string magicMirrorCacheStr = "type_MAGIC_MIRROR";
-		static const auto magicMirrorSelector = Selector::type()(BonusType::MAGIC_MIRROR);
-
-		const int mirrorChance = mainTarget->valOfBonuses(magicMirrorSelector, magicMirrorCacheStr);
-
-		if(server->getRNG()->nextInt(0, 99) < mirrorChance)
-		{
-			auto mirrorTargets = cb->battleGetUnitsIf([this](const battle::Unit * unit)
-			{
-				//Get all caster stacks. Magic mirror can reflect to immune creature (with no effect)
-				return unit->unitOwner() == caster->getCasterOwner() && unit->isValidTarget(true);
-			});
-
-
-			if(!mirrorTargets.empty())
-			{
-				const auto * mirrorDestination = (*RandomGeneratorUtil::nextItem(mirrorTargets, *server->getRNG()));
-
-				Target mirrorTarget;
-				mirrorTarget.emplace_back(mirrorDestination);
-
-				BattleCast mirror(*this, mainTarget);
-				mirror.cast(server, mirrorTarget);
-			}
-		}
-	}
 }
 
 void BattleCast::castEval(ServerCallback * server, Target target)

+ 0 - 3
lib/spells/ISpellMechanics.h

@@ -102,9 +102,6 @@ public:
 	//normal constructor
 	BattleCast(const CBattleInfoCallback * cb_, const Caster * caster_, const Mode mode_, const CSpell * spell_);
 
-	//magic mirror constructor
-	BattleCast(const BattleCast & orig, const Caster * caster_);
-
 	virtual ~BattleCast();
 
 	///IBattleCast