mirror of
https://github.com/correl/mage.git
synced 2025-01-11 19:13:02 +00:00
* Transform abilities - fixed duplicated triggered abilities from transformed permanents;
* Accursed Witch // Infectious Curse - fixed wrong cost modification effect (#6684);
This commit is contained in:
parent
1045f352bc
commit
0824d2901a
6 changed files with 102 additions and 36 deletions
|
@ -1,9 +1,6 @@
|
|||
|
||||
package mage.cards.i;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
|
@ -18,20 +15,23 @@ import mage.constants.*;
|
|||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.TargetPlayer;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author halljared
|
||||
*/
|
||||
public final class InfectiousCurse extends CardImpl {
|
||||
|
||||
public InfectiousCurse(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "");
|
||||
this.subtype.add(SubType.AURA, SubType.CURSE);
|
||||
this.color.setBlack(true);
|
||||
|
||||
|
@ -46,6 +46,7 @@ public final class InfectiousCurse extends CardImpl {
|
|||
|
||||
// Spells you cast that target enchanted player cost {1} less to cast.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new InfectiousCurseCostReductionEffect()));
|
||||
|
||||
// At the beginning of enchanted player's upkeep, that player loses 1 life and you gain 1 life.
|
||||
InfectiousCurseAbility curseAbility = new InfectiousCurseAbility();
|
||||
curseAbility.addEffect(new GainLifeEffect(1));
|
||||
|
@ -121,23 +122,31 @@ class InfectiousCurseCostReductionEffect extends CostModificationEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
if (abilityToModify instanceof SpellAbility) {
|
||||
if (source.isControlledBy(abilityToModify.getControllerId())) {
|
||||
for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
|
||||
Mode mode = abilityToModify.getModes().get(modeId);
|
||||
for (Target target : mode.getTargets()) {
|
||||
for (UUID targetUUID : target.getTargets()) {
|
||||
Permanent enchantment = game.getPermanent(source.getSourceId());
|
||||
UUID attachedTo = enchantment.getAttachedTo();
|
||||
if (targetUUID.equals(attachedTo)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(abilityToModify instanceof SpellAbility)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
if (!source.isControlledBy(abilityToModify.getControllerId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Permanent enchantment = game.getPermanent(source.getSourceId());
|
||||
if (enchantment == null || enchantment.getAttachedTo() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
|
||||
Set<UUID> allTargets;
|
||||
if (spell != null) {
|
||||
// real cast
|
||||
allTargets = CardUtil.getAllSelectedTargets(abilityToModify, game);
|
||||
} else {
|
||||
// playable
|
||||
allTargets = CardUtil.getAllPossibleTargets(abilityToModify, game);
|
||||
}
|
||||
|
||||
// try to reduce all the time (if it possible to target that player)
|
||||
return allTargets.stream().anyMatch(target -> Objects.equals(target, enchantment.getAttachedTo()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
|
||||
|
||||
package org.mage.test.cards.copy;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
|
@ -14,7 +13,6 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
|
|||
* addition to its other types. It gains haste. Sacrifice it at the beginning of
|
||||
* the next end step.
|
||||
*
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class FeldonOfTheThirdPathTest extends CardTestPlayerBase {
|
||||
|
@ -22,27 +20,41 @@ public class FeldonOfTheThirdPathTest extends CardTestPlayerBase {
|
|||
/**
|
||||
* Checking that enters the battlefield abilities of the copied creature
|
||||
* card works.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testETBEffect() {
|
||||
// When Highway Robber enters the battlefield, target opponent loses 2 life and you gain 2 life.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Highway Robber", 1);
|
||||
// {2}{R}, {T}: Create a token that's a copy of target creature card in your graveyard, except it's an artifact
|
||||
// in addition to its other types. It gains haste. Sacrifice it at the beginning of the next end step.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Feldon of the Third Path", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
//
|
||||
// When Highway Robber enters the battlefield, target opponent loses 2 life and you gain 2 life.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Highway Robber", 1);
|
||||
//
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
|
||||
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA,
|
||||
"{2}{R}, {T}: Create a token that's a copy of target creature card in your graveyard, except it's an artifact in addition to its other types. It gains haste. Sacrifice it at the beginning of the next end step.",
|
||||
"Highway Robber");
|
||||
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}, {T}: Create a token", "Highway Robber");
|
||||
addTarget(playerA, playerB); // opponent to loses 2 life
|
||||
waitStackResolved(2, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkPermanentCount("must have token", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Highway Robber", 1);
|
||||
checkPermanentCount("must have card", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Feldon of the Third Path", 1);
|
||||
|
||||
// destroy token
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Highway Robber");
|
||||
waitStackResolved(2, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkPermanentCount("must haven't token", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Highway Robber", 0);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Highway Robber", 1);
|
||||
assertPermanentCount(playerA, "Feldon of the Third Path", 1);
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 22); // +2 from Robber
|
||||
assertLife(playerB, 18); // -2 from Robber
|
||||
|
||||
// possible bug: triggers from destroyed permanents keeps in game state (e.g. 2 triggers in game state)
|
||||
Assert.assertEquals("game state must have only 1 trigger from original card", 1, currentGame.getState().getTriggers().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -59,8 +71,11 @@ public class FeldonOfTheThirdPathTest extends CardTestPlayerBase {
|
|||
"{2}{R}, {T}: Create a token that's a copy of target creature card in your graveyard, except it's an artifact in addition to its other types. It gains haste. Sacrifice it at the beginning of the next end step.",
|
||||
"Sepulchral Primordial");
|
||||
addTarget(playerA, "Silvercoat Lion"); // target for ETB Sepulchral Primordial
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Feldon of the Third Path", 1);
|
||||
assertPermanentCount(playerA, "Sepulchral Primordial", 1);
|
||||
|
|
|
@ -343,4 +343,43 @@ public class CostModificationTest extends CardTestPlayerBase {
|
|||
|
||||
assertGraveyardCount(playerA, "Engulfing Flames", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ThatTargetEnchantedPlayer_InfectiousCurse() {
|
||||
// When Accursed Witch dies, return it to the battlefield transformed under your control attached to target opponent.
|
||||
//
|
||||
// transformed: Infectious Curse
|
||||
// Enchant player
|
||||
// Spells you cast that target enchanted player cost {1} less to cast.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Accursed Witch", 1); // 4/2
|
||||
//
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
//
|
||||
// {1}{R} SORCERY
|
||||
// Grapeshot deals 1 damage to any target.
|
||||
addCard(Zone.HAND, playerA, "Grapeshot");
|
||||
|
||||
checkPlayableAbility("no mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", false);
|
||||
|
||||
// transform
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Accursed Witch");
|
||||
addTarget(playerA, playerB); // attach curse to
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkPermanentCount("transformed", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Infectious Curse", 1);
|
||||
checkPlayableAbility("reduced, but no mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", false);
|
||||
|
||||
// cast
|
||||
// possible bug: transform command can generate duplicated triggers (player get choose trigger dialog but must not)
|
||||
checkPlayableAbility("reduced, with mana", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", true);
|
||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Grapeshot", playerB);
|
||||
waitStackResolved(3, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkLife("a", 3, PhaseStep.PRECOMBAT_MAIN, playerA, 20 + 1); // +1 from curse on turn 2
|
||||
checkLife("b", 3, PhaseStep.PRECOMBAT_MAIN, playerB, 20 - 1 - 1); // -1 from grapeshot, -1 from curse on turn 2
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(6, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ public class ApplyCountersEffect extends ContinuousEffectImpl {
|
|||
if (layer == Layer.AbilityAddingRemovingEffects_6) {
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) {
|
||||
for (AbilityCounter counter : permanent.getCounters(game).getAbilityCounters()) {
|
||||
permanent.addAbility(counter.getAbility(), source == null ? null : source.getSourceId(), game);
|
||||
permanent.addAbility(counter.getAbility(), source == null ? permanent.getId() : source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,9 @@ public class TransformAbility extends SimpleStaticAbility {
|
|||
permanent.setExpansionSetCode(sourceCard.getExpansionSetCode());
|
||||
permanent.getAbilities().clear();
|
||||
for (Ability ability : sourceCard.getAbilities()) {
|
||||
permanent.addAbility(ability, source == null ? null : source.getSourceId(), game);
|
||||
// source == null -- call from init card (e.g. own abilities)
|
||||
// source != null -- from apply effect
|
||||
permanent.addAbility(ability, source == null ? permanent.getId() : source.getSourceId(), game);
|
||||
}
|
||||
permanent.getPower().modifyBaseValue(sourceCard.getPower().getValue());
|
||||
permanent.getToughness().modifyBaseValue(sourceCard.getToughness().getValue());
|
||||
|
|
|
@ -58,6 +58,7 @@ public class PermanentToken extends PermanentImpl {
|
|||
this.abilities.addAll(token.getAbilities());
|
||||
} else {
|
||||
// first time -> create ContinuousEffects only once
|
||||
// so sourceId must be null (keep triggered abilities forever?)
|
||||
for (Ability ability : token.getAbilities()) {
|
||||
this.addAbility(ability, null, game);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue