forked from External/mage
rework PhantomReplacementEffect used by 7 Phantom cards (#12189)
This commit is contained in:
parent
6193c9aee6
commit
d645facdc0
15 changed files with 366 additions and 62 deletions
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.common.EntersBattlefieldAbility;
|
||||
|
|
@ -13,9 +12,10 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
|
@ -36,7 +36,7 @@ public final class PhantomCentaur extends CardImpl {
|
|||
this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)), "with three +1/+1 counters on it"));
|
||||
|
||||
// If damage would be dealt to Phantom Centaur, prevent that damage. Remove a +1/+1 counter from Phantom Centaur.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PhantomPreventionEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new PhantomPreventionEffect()), PhantomPreventionEffect.createWatcher());
|
||||
}
|
||||
|
||||
private PhantomCentaur(final PhantomCentaur card) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.EntersBattlefieldAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
|
|
@ -12,9 +11,10 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author emerald000
|
||||
*/
|
||||
|
|
@ -35,7 +35,7 @@ public final class PhantomFlock extends CardImpl {
|
|||
this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)), "with three +1/+1 counters on it"));
|
||||
|
||||
// If damage would be dealt to Phantom Flock, prevent that damage. Remove a +1/+1 counter from Phantom Flock.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PhantomPreventionEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new PhantomPreventionEffect()), PhantomPreventionEffect.createWatcher());
|
||||
}
|
||||
|
||||
private PhantomFlock(final PhantomFlock card) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.EntersBattlefieldAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
|
|
@ -17,14 +16,15 @@ import mage.constants.SubType;
|
|||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class PhantomNantuko extends CardImpl {
|
||||
|
||||
public PhantomNantuko(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
|
||||
this.subtype.add(SubType.INSECT);
|
||||
this.subtype.add(SubType.SPIRIT);
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ public final class PhantomNantuko extends CardImpl {
|
|||
// Phantom Nantuko enters the battlefield with two +1/+1 counters on it.
|
||||
this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2), true), "with two +1/+1 counters on it"));
|
||||
// If damage would be dealt to Phantom Nantuko, prevent that damage. Remove a +1/+1 counter from Phantom Nantuko.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PhantomPreventionEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new PhantomPreventionEffect()), PhantomPreventionEffect.createWatcher());
|
||||
// {tap}: Put a +1/+1 counter on Phantom Nantuko.
|
||||
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new TapSourceCost()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.DealsDamageGainLifeSourceTriggeredAbility;
|
||||
import mage.abilities.common.EntersBattlefieldAbility;
|
||||
|
|
@ -13,9 +12,10 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author fireshoes
|
||||
*/
|
||||
|
|
@ -39,7 +39,7 @@ public final class PhantomNishoba extends CardImpl {
|
|||
this.addAbility(new DealsDamageGainLifeSourceTriggeredAbility());
|
||||
|
||||
// If damage would be dealt to Phantom Nishoba, prevent that damage. Remove a +1/+1 counter from Phantom Nishoba.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PhantomPreventionEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new PhantomPreventionEffect()), PhantomPreventionEffect.createWatcher());
|
||||
}
|
||||
|
||||
private PhantomNishoba(final PhantomNishoba card) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.EntersBattlefieldAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
|
|
@ -11,17 +10,17 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class PhantomNomad extends CardImpl {
|
||||
|
||||
public PhantomNomad(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
|
||||
this.subtype.add(SubType.SPIRIT);
|
||||
this.subtype.add(SubType.NOMAD);
|
||||
|
||||
|
|
@ -33,7 +32,7 @@ public final class PhantomNomad extends CardImpl {
|
|||
"with two +1/+1 counters on it"));
|
||||
|
||||
// If damage would be dealt to Phantom Nomad, prevent that damage. Remove a +1/+1 counter from Phantom Nomad.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PhantomPreventionEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new PhantomPreventionEffect()), PhantomPreventionEffect.createWatcher());
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.EntersBattlefieldAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
|
|
@ -11,9 +10,10 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Temba
|
||||
*/
|
||||
|
|
@ -30,7 +30,7 @@ public final class PhantomTiger extends CardImpl {
|
|||
this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), "with two +1/+1 counters on it"));
|
||||
|
||||
// If damage would be dealt to Phantom Tiger, prevent that damage. Remove a +1/+1 counter from Phantom Tiger.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PhantomPreventionEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new PhantomPreventionEffect()), PhantomPreventionEffect.createWatcher());
|
||||
}
|
||||
|
||||
private PhantomTiger(final PhantomTiger card) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.EntersBattlefieldAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
|
|
@ -11,17 +10,17 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class PhantomWurm extends CardImpl {
|
||||
|
||||
public PhantomWurm(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}{G}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}");
|
||||
this.subtype.add(SubType.WURM);
|
||||
this.subtype.add(SubType.SPIRIT);
|
||||
|
||||
|
|
@ -33,7 +32,7 @@ public final class PhantomWurm extends CardImpl {
|
|||
"with four +1/+1 counters on it"));
|
||||
|
||||
// If damage would be dealt to Phantom Wurm, prevent that damage. Remove a +1/+1 counter from Phantom Wurm.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PhantomPreventionEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new PhantomPreventionEffect()), PhantomPreventionEffect.createWatcher());
|
||||
}
|
||||
|
||||
private PhantomWurm(final PhantomWurm card) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,237 @@
|
|||
package org.mage.test.cards.single.tsp;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class PhantomWurmTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.p.PhantomWurm Phantom Wurm} {4}{G}{G}
|
||||
* Creature — Wurm Spirit
|
||||
* Phantom Wurm enters the battlefield with four +1/+1 counters on it.
|
||||
* If damage would be dealt to Phantom Wurm, prevent that damage. Remove a +1/+1 counter from Phantom Wurm.
|
||||
* 2/0
|
||||
*/
|
||||
private static final String wurm = "Phantom Wurm";
|
||||
|
||||
@Test
|
||||
public void test_DoubleBlocked() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, wurm);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Eager Cadet");
|
||||
|
||||
attack(1, playerA, wurm, playerB);
|
||||
block(1, playerB, "Memnite", wurm);
|
||||
block(1, playerB, "Eager Cadet", wurm);
|
||||
|
||||
setChoice(playerA, "X=1"); // damage assignment
|
||||
setChoice(playerA, "X=3"); // damage assignment
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Memnite", 1);
|
||||
assertGraveyardCount(playerB, "Eager Cadet", 1);
|
||||
assertDamageReceived(playerA, wurm, 0); // all is prevented
|
||||
assertCounterCount(wurm, CounterType.P1P1, 4 - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_BlockedByAnotherPhantom() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, wurm);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Phantom Nomad");
|
||||
|
||||
attack(1, playerA, wurm, playerB);
|
||||
block(1, playerB, "Phantom Nomad", wurm);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertDamageReceived(playerA, wurm, 0); // all is prevented
|
||||
assertCounterCount(wurm, CounterType.P1P1, 4 - 1);
|
||||
assertDamageReceived(playerB, "Phantom Nomad", 0); // all is prevented
|
||||
assertCounterCount("Phantom Nomad", CounterType.P1P1, 2 - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_BlockedByAnotherPhantom_ThenBolt() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, wurm);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Phantom Nomad");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain");
|
||||
addCard(Zone.HAND, playerB, "Lightning Bolt");
|
||||
|
||||
attack(1, playerA, wurm, playerB);
|
||||
block(1, playerB, "Phantom Nomad", wurm);
|
||||
|
||||
castSpell(1, PhaseStep.COMBAT_DAMAGE, playerB, "Lightning Bolt", wurm);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertDamageReceived(playerA, wurm, 0); // all is prevented
|
||||
assertCounterCount(wurm, CounterType.P1P1, 4 - 2);
|
||||
assertDamageReceived(playerB, "Phantom Nomad", 0); // all is prevented
|
||||
assertCounterCount("Phantom Nomad", CounterType.P1P1, 2 - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DoubleStrike() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, wurm);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Adorned Pouncer"); // 1/1 double strike
|
||||
|
||||
attack(1, playerA, wurm, playerB);
|
||||
block(1, playerB, "Adorned Pouncer", wurm);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Adorned Pouncer", 1);
|
||||
assertDamageReceived(playerA, wurm, 0); // all is prevented
|
||||
assertCounterCount(wurm, CounterType.P1P1, 4 - 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DoubleBlocked_OneFirstStrikeOneNormal() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, wurm);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Goblin Striker", 1); // 1/1 first strike haste
|
||||
|
||||
attack(1, playerA, wurm, playerB);
|
||||
block(1, playerB, "Memnite", wurm);
|
||||
block(1, playerB, "Goblin Striker", wurm);
|
||||
|
||||
setChoice(playerA, "X=1"); // damage assignment
|
||||
setChoice(playerA, "X=3"); // damage assignment
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Memnite", 1);
|
||||
assertGraveyardCount(playerB, "Goblin Striker", 1);
|
||||
assertDamageReceived(playerA, wurm, 0); // all is prevented
|
||||
assertCounterCount(wurm, CounterType.P1P1, 4 - 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DoubleBlocked_TwoFirstStrike() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, wurm);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Boros Recruit"); // 1/1 first strike
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Goblin Striker", 1); // 1/1 first strike haste
|
||||
|
||||
attack(1, playerA, wurm, playerB);
|
||||
block(1, playerB, "Boros Recruit", wurm);
|
||||
block(1, playerB, "Goblin Striker", wurm);
|
||||
|
||||
setChoice(playerA, "X=1"); // damage assignment
|
||||
setChoice(playerA, "X=3"); // damage assignment
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Boros Recruit", 1);
|
||||
assertGraveyardCount(playerB, "Goblin Striker", 1);
|
||||
assertDamageReceived(playerA, wurm, 0); // all is prevented
|
||||
assertCounterCount(wurm, CounterType.P1P1, 4 - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Blocked_ThenBolt() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, wurm);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain");
|
||||
addCard(Zone.HAND, playerB, "Lightning Bolt");
|
||||
|
||||
attack(1, playerA, wurm, playerB);
|
||||
block(1, playerB, "Memnite", wurm);
|
||||
|
||||
castSpell(1, PhaseStep.COMBAT_DAMAGE, playerB, "Lightning Bolt", wurm);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Memnite", 1);
|
||||
assertDamageReceived(playerA, wurm, 0); // all is prevented
|
||||
assertCounterCount(wurm, CounterType.P1P1, 4 - 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Blocked_FirstStrike_ThenBolt() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, wurm);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Boros Recruit");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain");
|
||||
addCard(Zone.HAND, playerB, "Lightning Bolt");
|
||||
|
||||
attack(1, playerA, wurm, playerB);
|
||||
block(1, playerB, "Boros Recruit", wurm);
|
||||
|
||||
castSpell(1, PhaseStep.FIRST_COMBAT_DAMAGE, playerB, "Lightning Bolt", wurm);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Boros Recruit", 1);
|
||||
assertDamageReceived(playerA, wurm, 0); // all is prevented
|
||||
assertCounterCount(wurm, CounterType.P1P1, 4 - 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Simultanous_NonCombat() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, wurm);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Memnite");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Boros Recruit");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
|
||||
addCard(Zone.HAND, playerA, "Band Together"); // Up to two target creatures you control each deal damage equal to their power to another target creature.
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Band Together");
|
||||
addTarget(playerA, "Memnite^Boros Recruit");
|
||||
addTarget(playerA, wurm);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertDamageReceived(playerA, wurm, 0); // all is prevented
|
||||
assertCounterCount(wurm, CounterType.P1P1, 4 - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Bolt_ThenBolt() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, wurm);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2);
|
||||
addCard(Zone.HAND, playerB, "Lightning Bolt", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", wurm);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", wurm);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertDamageReceived(playerA, wurm, 0); // all is prevented
|
||||
assertCounterCount(wurm, CounterType.P1P1, 4 - 2);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,28 @@
|
|||
package mage.abilities.effects;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.turn.Step;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by IGOUDT on 22-3-2017.
|
||||
* This requires to add the PhantomPreventionWatcher
|
||||
*
|
||||
* @author Susucr
|
||||
*/
|
||||
public class PhantomPreventionEffect extends PreventionEffectImpl {
|
||||
|
||||
// remember turn and phase step to check if counter in this step was already removed
|
||||
private int turn = 0;
|
||||
private Step combatPhaseStep = null;
|
||||
public static PhantomPreventionWatcher createWatcher() {
|
||||
return new PhantomPreventionWatcher();
|
||||
}
|
||||
|
||||
public PhantomPreventionEffect() {
|
||||
super(Duration.WhileOnBattlefield);
|
||||
|
|
@ -25,8 +31,6 @@ public class PhantomPreventionEffect extends PreventionEffectImpl {
|
|||
|
||||
protected PhantomPreventionEffect(final PhantomPreventionEffect effect) {
|
||||
super(effect);
|
||||
this.turn = effect.turn;
|
||||
this.combatPhaseStep = effect.combatPhaseStep;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -37,27 +41,19 @@ public class PhantomPreventionEffect extends PreventionEffectImpl {
|
|||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
preventDamageAction(event, source, game);
|
||||
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
boolean removeCounter = true;
|
||||
// check if in the same combat damage step already a counter was removed
|
||||
if (game.getTurn().getPhase().getStep().getType() == PhaseStep.COMBAT_DAMAGE) {
|
||||
if (game.getTurnNum() == turn
|
||||
&& game.getTurn().getStep().equals(combatPhaseStep)) {
|
||||
removeCounter = false;
|
||||
} else {
|
||||
turn = game.getTurnNum();
|
||||
combatPhaseStep = game.getTurn().getStep();
|
||||
PhantomPreventionWatcher watcher = game.getState().getWatcher(PhantomPreventionWatcher.class);
|
||||
if (permanent != null && watcher != null) {
|
||||
MageObjectReference mor = new MageObjectReference(source.getId(), source.getSourceObjectZoneChangeCounter(), game);
|
||||
if (!watcher.hadMORCounterRemovedThisBatch(mor)) {
|
||||
watcher.addMOR(mor);
|
||||
if (permanent.getCounters(game).containsKey(CounterType.P1P1)) {
|
||||
StringBuilder sb = new StringBuilder(permanent.getName()).append(": ");
|
||||
permanent.removeCounters(CounterType.P1P1.createInstance(), source, game);
|
||||
sb.append("Removed a +1/+1 counter ");
|
||||
game.informPlayers(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
if (removeCounter && permanent.getCounters(game).containsKey(CounterType.P1P1)) {
|
||||
StringBuilder sb = new StringBuilder(permanent.getName()).append(": ");
|
||||
permanent.removeCounters(CounterType.P1P1.createInstance(), source, game);
|
||||
sb.append("Removed a +1/+1 counter ");
|
||||
game.informPlayers(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -72,5 +68,38 @@ public class PhantomPreventionEffect extends PreventionEffectImpl {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PhantomPreventionWatcher extends Watcher {
|
||||
|
||||
// We keep a very short-lived set of which PhantomPreventionEffect caused
|
||||
// +1/+1 to get removed during the current damage batch.
|
||||
private final Set<MageObjectReference> morRemovedCounterThisDamageBatch = new HashSet<>();
|
||||
|
||||
PhantomPreventionWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
// This watcher resets every time a Damage Batch could have fired (even if all damage was prevented)
|
||||
if (event.getType() != GameEvent.EventType.DAMAGED_BATCH_COULD_HAVE_FIRED) {
|
||||
return;
|
||||
}
|
||||
morRemovedCounterThisDamageBatch.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
morRemovedCounterThisDamageBatch.clear();
|
||||
}
|
||||
|
||||
boolean hadMORCounterRemovedThisBatch(MageObjectReference mor) {
|
||||
return morRemovedCounterThisDamageBatch.contains(mor);
|
||||
}
|
||||
|
||||
void addMOR(MageObjectReference mor) {
|
||||
morRemovedCounterThisDamageBatch.add(mor);
|
||||
}
|
||||
}
|
||||
|
|
@ -818,6 +818,16 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
return !simultaneousEvents.isEmpty();
|
||||
}
|
||||
|
||||
// There might be no damage dealt, but we want to fire that damage (in a batch) could have been dealt.
|
||||
public void addBatchDamageCouldHaveBeenFired(boolean combat, Game game) {
|
||||
for (GameEvent event : simultaneousEvents) {
|
||||
if (event instanceof DamagedBatchCouldHaveFiredEvent && event.getFlag() == combat) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
addSimultaneousEvent(new DamagedBatchCouldHaveFiredEvent(combat), game);
|
||||
}
|
||||
|
||||
public void addSimultaneousDamage(DamagedEvent damagedEvent, Game game) {
|
||||
// Combine multiple damage events in the single event (batch)
|
||||
// Note: one event can be stored in multiple batches
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package mage.game.events;
|
||||
|
||||
/**
|
||||
* Does not contain any info on damage events, and can fire even when all damage is prevented.
|
||||
* Fire any time a DAMAGED_BATCH_FOR_ALL could have fired (combat & noncombat).
|
||||
* It is not a batch event (doesn't contain sub events), the name is a little ambiguous.
|
||||
*
|
||||
* @author Susucr
|
||||
*/
|
||||
public class DamagedBatchCouldHaveFiredEvent extends GameEvent {
|
||||
|
||||
public DamagedBatchCouldHaveFiredEvent(boolean combat) {
|
||||
super(EventType.DAMAGED_BATCH_COULD_HAVE_FIRED, null, null, null, 0, combat);
|
||||
}
|
||||
}
|
||||
|
|
@ -129,6 +129,12 @@ public class GameEvent implements Serializable {
|
|||
includes all damage events, both permanent damage and player damage, in single batch event
|
||||
*/
|
||||
DAMAGED_BATCH_FOR_ALL,
|
||||
/* DAMAGED_BATCH_FIRED
|
||||
* Does not contain any info on damage events, and can fire even when all damage is prevented.
|
||||
* Fire any time a DAMAGED_BATCH_FOR_ALL could have fired (combat & noncombat).
|
||||
* It is not a batch event (doesn't contain sub events), the name is a little ambiguous.
|
||||
*/
|
||||
DAMAGED_BATCH_COULD_HAVE_FIRED,
|
||||
|
||||
/* DAMAGE_CAUSES_LIFE_LOSS,
|
||||
targetId the id of the damaged player
|
||||
|
|
|
|||
|
|
@ -1024,6 +1024,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
}
|
||||
DamageEvent event = new DamagePermanentEvent(objectId, attackerId, controllerId, damageAmount, preventable, combat);
|
||||
event.setAppliedEffects(appliedEffects);
|
||||
// Even if no damage was dealt, some watchers would need a reset next time actions are processed.
|
||||
// For instance PhantomPreventionWatcher used by the [[Phantom Wurm]] type of replacement effect.
|
||||
game.getState().addBatchDamageCouldHaveBeenFired(combat, game);
|
||||
if (game.replaceEvent(event)) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
package mage.game.turn;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.game.Game;
|
||||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
|
|
@ -65,6 +65,9 @@ public class CombatDamageStep extends Step {
|
|||
for (CombatGroup group : game.getCombat().getBlockingGroups()) {
|
||||
group.applyDamage(game);
|
||||
}
|
||||
|
||||
// Even if no damage was dealt, some watchers need a reset. For instance PhantomPreventionWatcher.
|
||||
game.getState().addBatchDamageCouldHaveBeenFired(true, game);
|
||||
// Must fire damage batch events now, before SBA (https://github.com/magefree/mage/issues/9129)
|
||||
game.getState().handleSimultaneousEvent(game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2255,7 +2255,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return doDamage(damage, attackerId, source, game, combatDamage, preventable, appliedEffects);
|
||||
}
|
||||
|
||||
private int doDamage(int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable, List<UUID> appliedEffects) {
|
||||
private int doDamage(int damage, UUID attackerId, Ability source, Game game, boolean combat, boolean preventable, List<UUID> appliedEffects) {
|
||||
if (!this.isInGame()) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2263,8 +2263,11 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (damage < 1) {
|
||||
return 0;
|
||||
}
|
||||
DamageEvent event = new DamagePlayerEvent(playerId, attackerId, playerId, damage, preventable, combatDamage);
|
||||
DamageEvent event = new DamagePlayerEvent(playerId, attackerId, playerId, damage, preventable, combat);
|
||||
event.setAppliedEffects(appliedEffects);
|
||||
// Even if no damage was dealt, some watchers would need a reset next time actions are processed.
|
||||
// For instance PhantomPreventionWatcher used by the [[Phantom Wurm]] type of replacement effect.
|
||||
game.getState().addBatchDamageCouldHaveBeenFired(combat, game);
|
||||
if (game.replaceEvent(event)) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2300,20 +2303,20 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
addCounters(CounterType.POISON.createInstance(actualDamage), sourceControllerId, source, game);
|
||||
} else {
|
||||
GameEvent damageToLifeLossEvent = new GameEvent(GameEvent.EventType.DAMAGE_CAUSES_LIFE_LOSS,
|
||||
playerId, source, playerId, actualDamage, combatDamage);
|
||||
playerId, source, playerId, actualDamage, combat);
|
||||
if (!game.replaceEvent(damageToLifeLossEvent)) {
|
||||
this.loseLife(damageToLifeLossEvent.getAmount(), game, source, combatDamage, attackerId);
|
||||
this.loseLife(damageToLifeLossEvent.getAmount(), game, source, combat, attackerId);
|
||||
}
|
||||
}
|
||||
if (sourceAbilities != null && sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) {
|
||||
if (combatDamage) {
|
||||
if (combat) {
|
||||
game.getPermanent(attackerId).markLifelink(actualDamage);
|
||||
} else {
|
||||
Player player = game.getPlayer(sourceControllerId);
|
||||
player.gainLife(actualDamage, game, source);
|
||||
}
|
||||
}
|
||||
if (combatDamage && sourceAbilities != null && sourceAbilities.containsClass(ToxicAbility.class)) {
|
||||
if (combat && sourceAbilities != null && sourceAbilities.containsClass(ToxicAbility.class)) {
|
||||
int countersToAdd = CardUtil
|
||||
.castStream(sourceAbilities.stream(), ToxicAbility.class)
|
||||
.mapToInt(ToxicAbility::getAmount)
|
||||
|
|
@ -2325,7 +2328,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
Player player = game.getPlayer(sourceControllerId);
|
||||
new SquirrelToken().putOntoBattlefield(actualDamage, game, source, player.getId());
|
||||
}
|
||||
DamagedEvent damagedEvent = new DamagedPlayerEvent(playerId, attackerId, playerId, actualDamage, combatDamage);
|
||||
DamagedEvent damagedEvent = new DamagedPlayerEvent(playerId, attackerId, playerId, actualDamage, combat);
|
||||
game.fireEvent(damagedEvent);
|
||||
game.getState().addSimultaneousDamage(damagedEvent, game);
|
||||
return actualDamage;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue