mirror of
https://github.com/correl/mage.git
synced 2024-11-15 03:00:16 +00:00
Added ConditionalPreventionEffect to support prevention effects with conditions (#5738)
This commit is contained in:
parent
f25f7a0f68
commit
367a1fd189
7 changed files with 340 additions and 18 deletions
|
@ -7,6 +7,7 @@ import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
|
|||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.MyTurnCondition;
|
||||
import mage.abilities.decorator.ConditionalContinuousEffect;
|
||||
import mage.abilities.decorator.ConditionalPreventionEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileTargetEffect;
|
||||
import mage.abilities.effects.common.PreventAllDamageToSourceEffect;
|
||||
|
@ -61,7 +62,7 @@ public final class GideonBlackblade extends CardImpl {
|
|||
)));
|
||||
|
||||
// Prevent all damage that would be dealt to Gideon Blackblade during your turn.
|
||||
this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
|
||||
this.addAbility(new SimpleStaticAbility(new ConditionalPreventionEffect(
|
||||
new PreventAllDamageToSourceEffect(Duration.WhileOnBattlefield),
|
||||
MyTurnCondition.instance, "Prevent all damage that would be dealt to {this} during your turn."
|
||||
)));
|
||||
|
@ -96,7 +97,7 @@ class GideonBlackbladeToken extends TokenImpl {
|
|||
subtype.add(SubType.SOLDIER);
|
||||
power = new MageInt(4);
|
||||
toughness = new MageInt(4);
|
||||
addAbility(new SimpleStaticAbility(new PreventAllDamageToSourceEffect(Duration.WhileOnBattlefield)));
|
||||
this.addAbility(IndestructibleAbility.getInstance());
|
||||
}
|
||||
|
||||
private GideonBlackbladeToken(final GideonBlackbladeToken token) {
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
package org.mage.test.cards.continuous;
|
||||
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.MyTurnCondition;
|
||||
import mage.abilities.condition.common.NotMyTurnCondition;
|
||||
import mage.abilities.decorator.ConditionalPreventionEffect;
|
||||
import mage.abilities.effects.common.PreventAllDamageToAllEffect;
|
||||
import mage.abilities.effects.common.PreventAllDamageToPlayersEffect;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class ConditionalPreventionTest extends CardTestPlayerBase {
|
||||
|
||||
// conditional effects go to layered, but there are prevention effects list too
|
||||
|
||||
@Test
|
||||
public void test_NotPreventDamage() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 0);
|
||||
assertHandCount(playerA, "Lightning Bolt", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PreventDamageNormal() {
|
||||
addCustomCardWithAbility("prevent", playerA, new SimpleStaticAbility(new PreventAllDamageToAllEffect(Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT)));
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 1);
|
||||
assertHandCount(playerA, "Lightning Bolt", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PreventDamageConditionalActive() {
|
||||
addCustomCardWithAbility("prevent", playerA, new SimpleStaticAbility(
|
||||
new ConditionalPreventionEffect(
|
||||
new PreventAllDamageToAllEffect(Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT),
|
||||
MyTurnCondition.instance,
|
||||
""
|
||||
)
|
||||
));
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 1);
|
||||
assertHandCount(playerA, "Lightning Bolt", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PreventDamageConditionalNotActive() {
|
||||
addCustomCardWithAbility("prevent", playerA, new SimpleStaticAbility(
|
||||
new ConditionalPreventionEffect(
|
||||
new PreventAllDamageToAllEffect(Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT),
|
||||
NotMyTurnCondition.instance,
|
||||
""
|
||||
)
|
||||
));
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 0);
|
||||
assertHandCount(playerA, "Lightning Bolt", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PreventDamageConditionalNotActiveWithOtherEffect() {
|
||||
addCustomCardWithAbility("prevent", playerA, new SimpleStaticAbility(
|
||||
new ConditionalPreventionEffect(
|
||||
new PreventAllDamageToAllEffect(Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT),
|
||||
new PreventAllDamageToPlayersEffect(Duration.WhileOnBattlefield, false),
|
||||
NotMyTurnCondition.instance,
|
||||
""
|
||||
)
|
||||
));
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); // will prevent
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA); // will not prevent
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 0); // not prevented, dies
|
||||
assertLife(playerA, 20); // prevented, no damage
|
||||
assertHandCount(playerA, "Lightning Bolt", 0);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package org.mage.test.cards.continuous;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class GideonBlackbladeTest extends CardTestPlayerBase {
|
||||
|
||||
// Gideon Blackblade L4
|
||||
// As long as it's your turn, Gideon Blackblade is a 4/4 Human Soldier creature with indestructible that's still a planeswalker.
|
||||
// Prevent all damage that would be dealt to Gideon Blackblade during your turn.
|
||||
|
||||
@Test
|
||||
public void test_PreventDamageToGideonOnYourTurn() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Gideon Blackblade");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 2);
|
||||
|
||||
checkPT("turn 1 before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gideon Blackblade", 4, 4);
|
||||
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", "Gideon Blackblade");
|
||||
checkPT("turn 1 after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Gideon Blackblade", 4, 4);
|
||||
checkPermanentCounters("turn 1 after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Gideon Blackblade", CounterType.LOYALTY, 4);
|
||||
|
||||
checkPT("turn 2 before", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Gideon Blackblade", 0, 0);
|
||||
castSpell(2, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", "Gideon Blackblade");
|
||||
checkPT("turn 2 after", 2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Gideon Blackblade", 0, 0);
|
||||
checkPermanentCounters("turn 2 after", 2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Gideon Blackblade", CounterType.LOYALTY, 4 - 3);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
}
|
|
@ -1,9 +1,5 @@
|
|||
package mage.abilities.decorator;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.condition.Condition;
|
||||
|
@ -11,11 +7,14 @@ import mage.abilities.condition.FixedCondition;
|
|||
import mage.abilities.condition.LockedInCondition;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.DependencyType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Adds condition to {@link ContinuousEffect}. Acts as decorator.
|
||||
|
@ -48,6 +47,17 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
|
|||
this.otherwiseEffect = otherwiseEffect;
|
||||
this.baseCondition = condition;
|
||||
this.staticText = text;
|
||||
|
||||
// checks for compatibility
|
||||
if (effect != null && !effect.getEffectType().equals(EffectType.CONTINUOUS)) {
|
||||
Assert.fail("ConditionalContinuousEffect supports only " + EffectType.CONTINUOUS.toString() + " but found " + effect.getEffectType().toString());
|
||||
}
|
||||
if (otherwiseEffect != null && !otherwiseEffect.getEffectType().equals(EffectType.CONTINUOUS)) {
|
||||
Assert.fail("ConditionalContinuousEffect supports only " + EffectType.CONTINUOUS.toString() + " but found " + effect.getEffectType().toString());
|
||||
}
|
||||
if (effect != null && otherwiseEffect != null && !effect.getEffectType().equals(otherwiseEffect.getEffectType())) {
|
||||
Assert.fail("ConditionalContinuousEffect must be same but found " + effect.getEffectType().toString() + " and " + otherwiseEffect.getEffectType().toString());
|
||||
}
|
||||
}
|
||||
|
||||
public ConditionalContinuousEffect(final ConditionalContinuousEffect effect) {
|
||||
|
@ -68,6 +78,7 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
|
|||
|
||||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
if (baseCondition instanceof LockedInCondition) {
|
||||
condition = new FixedCondition(((LockedInCondition) baseCondition).getBaseCondition().apply(game, source));
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
package mage.abilities.decorator;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.condition.FixedCondition;
|
||||
import mage.abilities.condition.LockedInCondition;
|
||||
import mage.abilities.effects.PreventionEffect;
|
||||
import mage.abilities.effects.PreventionEffectImpl;
|
||||
import mage.constants.Duration;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class ConditionalPreventionEffect extends PreventionEffectImpl {
|
||||
|
||||
protected PreventionEffect effect;
|
||||
protected PreventionEffect otherwiseEffect;
|
||||
protected Condition baseCondition;
|
||||
protected Condition condition;
|
||||
protected boolean conditionState;
|
||||
protected boolean initDone = false;
|
||||
|
||||
public ConditionalPreventionEffect(PreventionEffect effect, Condition condition, String text) {
|
||||
this(effect, null, condition, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only use this if both effects have the same layers
|
||||
*
|
||||
* @param effect
|
||||
* @param otherwiseEffect
|
||||
* @param condition
|
||||
* @param text
|
||||
*/
|
||||
public ConditionalPreventionEffect(PreventionEffect effect, PreventionEffect otherwiseEffect, Condition condition, String text) {
|
||||
super(effect.getDuration());
|
||||
this.effect = effect;
|
||||
this.otherwiseEffect = otherwiseEffect;
|
||||
this.baseCondition = condition;
|
||||
this.staticText = text;
|
||||
}
|
||||
|
||||
public ConditionalPreventionEffect(final ConditionalPreventionEffect effect) {
|
||||
super(effect);
|
||||
this.effect = (PreventionEffect) effect.effect.copy();
|
||||
if (effect.otherwiseEffect != null) {
|
||||
this.otherwiseEffect = (PreventionEffect) effect.otherwiseEffect.copy();
|
||||
}
|
||||
this.condition = effect.condition; // TODO: checks conditional copy -- it's can be usefull for memory leaks fix?
|
||||
this.conditionState = effect.conditionState;
|
||||
this.baseCondition = effect.baseCondition;
|
||||
this.initDone = effect.initDone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDiscarded() {
|
||||
return this.discarded || effect.isDiscarded() || (otherwiseEffect != null && otherwiseEffect.isDiscarded());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
if (baseCondition instanceof LockedInCondition) {
|
||||
condition = new FixedCondition(((LockedInCondition) baseCondition).getBaseCondition().apply(game, source));
|
||||
} else {
|
||||
condition = baseCondition;
|
||||
}
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.init(source, game);
|
||||
if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.init(source, game);
|
||||
}
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
return effect.replaceEvent(event, source, game);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
return otherwiseEffect.replaceEvent(event, source, game);
|
||||
}
|
||||
|
||||
if (!conditionState && effect.getDuration() == Duration.OneUse) {
|
||||
used = true;
|
||||
}
|
||||
if (!conditionState && effect.getDuration() == Duration.Custom) {
|
||||
this.discard();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return effect.checksEventType(event, game)
|
||||
|| (otherwiseEffect != null && otherwiseEffect.checksEventType(event, game));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (!initDone) { // if simpleStaticAbility, init won't be called
|
||||
init(source, game);
|
||||
}
|
||||
conditionState = condition.apply(game, source);
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
return effect.applies(event, source, game);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
return otherwiseEffect.applies(event, source, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if ((staticText == null || staticText.isEmpty()) && this.effect != null) { // usefull for conditional night/day card abilities
|
||||
return effect.getText(mode);
|
||||
}
|
||||
return staticText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionalPreventionEffect copy() {
|
||||
return new ConditionalPreventionEffect(this);
|
||||
}
|
||||
|
||||
}
|
|
@ -311,14 +311,6 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
|
|||
}
|
||||
}
|
||||
return dependentToEffects;
|
||||
/*
|
||||
return allEffectsInLayer.stream()
|
||||
.filter(effect -> effect.getDependencyTypes().contains(dependendToTypes))
|
||||
.map(Effect::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
}
|
||||
return new HashSet<>();*/
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -369,6 +369,7 @@ public class ContinuousEffects implements Serializable {
|
|||
replaceEffects.put(effect, applicableAbilities);
|
||||
}
|
||||
}
|
||||
|
||||
for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext(); ) {
|
||||
PreventionEffect effect = iterator.next();
|
||||
if (!effect.checksEventType(event, game)) {
|
||||
|
@ -394,6 +395,7 @@ public class ContinuousEffects implements Serializable {
|
|||
replaceEffects.put(effect, applicableAbilities);
|
||||
}
|
||||
}
|
||||
|
||||
return replaceEffects;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue