mirror of
https://github.com/magefree/mage.git
synced 2025-12-21 02:52:02 -08:00
Improves in some effects:
* ConditionalAsThoughEffect - improved support with target effects (see comments from e6e802033b);
* TargetCardInLibrary - added additional checks on wrong usage (must be used inside effects only, not as ability's target);
* PlayFromNotOwnHandZone - fixed wrong playable mark on battlefield's permanents (AI related too);
This commit is contained in:
parent
aed6a4ac3d
commit
021a2d251c
8 changed files with 173 additions and 3 deletions
|
|
@ -0,0 +1,136 @@
|
||||||
|
package org.mage.test.cards.conditional;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.SimpleActivatedAbility;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
|
||||||
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
|
import mage.abilities.decorator.ConditionalAsThoughEffect;
|
||||||
|
import mage.abilities.effects.common.InfoEffect;
|
||||||
|
import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneAllEffect;
|
||||||
|
import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.target.common.TargetCardInLibrary;
|
||||||
|
import mage.target.common.TargetCardInOpponentsGraveyard;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class ConditionalAsThoughTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_PlayFromNotOwnHandZoneAllEffect() {
|
||||||
|
removeAllCardsFromLibrary(playerA);
|
||||||
|
removeAllCardsFromLibrary(playerB);
|
||||||
|
|
||||||
|
addCustomCardWithAbility("play any library on any creature", playerA, new SimpleStaticAbility(
|
||||||
|
Zone.ALL,
|
||||||
|
new ConditionalAsThoughEffect(
|
||||||
|
new PlayFromNotOwnHandZoneAllEffect(
|
||||||
|
StaticFilters.FILTER_CARD,
|
||||||
|
Zone.LIBRARY,
|
||||||
|
false,
|
||||||
|
TargetController.ANY,
|
||||||
|
Duration.EndOfTurn
|
||||||
|
),
|
||||||
|
new PermanentsOnTheBattlefieldCondition(
|
||||||
|
StaticFilters.FILTER_PERMANENT_CREATURE,
|
||||||
|
ComparisonType.MORE_THAN,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
addCard(Zone.HAND, playerA, "Grizzly Bears");
|
||||||
|
addCard(Zone.LIBRARY, playerA, "Silvercoat Lion");
|
||||||
|
addCard(Zone.LIBRARY, playerB, "Lightning Bolt");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
|
||||||
|
|
||||||
|
// can't play lib's card before good condition
|
||||||
|
checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grizzly Bears", true);
|
||||||
|
checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Silvercoat Lion", false);
|
||||||
|
checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", false);
|
||||||
|
|
||||||
|
// make good condition - now we can play any lib's card
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears");
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
checkPlayableAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grizzly Bears", false);
|
||||||
|
checkPlayableAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Silvercoat Lion", true);
|
||||||
|
checkPlayableAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void test_TargetCardInLibrary_CantUseAsAbilityTarget() {
|
||||||
|
Ability ability = new SimpleActivatedAbility(
|
||||||
|
Zone.ALL,
|
||||||
|
new InfoEffect("test"),
|
||||||
|
new ManaCostsImpl("{R}")
|
||||||
|
);
|
||||||
|
ability.addTarget(new TargetCardInLibrary());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_PlayFromNotOwnHandZoneTargetEffect() {
|
||||||
|
Ability ability = new SimpleActivatedAbility(
|
||||||
|
Zone.ALL,
|
||||||
|
new ConditionalAsThoughEffect(
|
||||||
|
new PlayFromNotOwnHandZoneTargetEffect(
|
||||||
|
Zone.GRAVEYARD,
|
||||||
|
TargetController.ANY,
|
||||||
|
Duration.EndOfTurn
|
||||||
|
),
|
||||||
|
new PermanentsOnTheBattlefieldCondition(
|
||||||
|
StaticFilters.FILTER_PERMANENT_CREATURE,
|
||||||
|
ComparisonType.MORE_THAN,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
).setText("allow target cast"),
|
||||||
|
new ManaCostsImpl("{R}")
|
||||||
|
);
|
||||||
|
ability.addTarget(new TargetCardInOpponentsGraveyard(StaticFilters.FILTER_CARD));
|
||||||
|
addCustomCardWithAbility("play any opponent hand", playerA, ability);
|
||||||
|
|
||||||
|
addCard(Zone.HAND, playerA, "Grizzly Bears");
|
||||||
|
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");
|
||||||
|
addCard(Zone.GRAVEYARD, playerB, "Lightning Bolt");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
|
||||||
|
|
||||||
|
// can't play grave before
|
||||||
|
checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grizzly Bears", true);
|
||||||
|
checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Silvercoat Lion", false);
|
||||||
|
checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", false);
|
||||||
|
|
||||||
|
// activate target effect
|
||||||
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}: Allow");
|
||||||
|
addTarget(playerA, "Lightning Bolt");
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
|
||||||
|
// can't play grave after but without good condition
|
||||||
|
checkPlayableAbility("after bad", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grizzly Bears", true);
|
||||||
|
checkPlayableAbility("after bad", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Silvercoat Lion", false);
|
||||||
|
checkPlayableAbility("after bad", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", false);
|
||||||
|
|
||||||
|
// make good condition - now we can play opponent's grave
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears");
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
checkPlayableAbility("after good", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grizzly Bears", false);
|
||||||
|
checkPlayableAbility("after good", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Silvercoat Lion", false);
|
||||||
|
checkPlayableAbility("after good", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2400,7 +2400,7 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
// card in hand (only own hand supports here)
|
// card in hand (only own hand supports here)
|
||||||
// TODO: add not own hand too, example
|
// cards from non-own hand must be targeted through revealed cards
|
||||||
if (target.getOriginalTarget() instanceof TargetCardInHand
|
if (target.getOriginalTarget() instanceof TargetCardInHand
|
||||||
|| target.getOriginalTarget() instanceof TargetDiscard
|
|| target.getOriginalTarget() instanceof TargetDiscard
|
||||||
|| (target.getOriginalTarget() instanceof TargetCard && target.getOriginalTarget().getZone() == Zone.HAND)) {
|
|| (target.getOriginalTarget() instanceof TargetCard && target.getOriginalTarget().getZone() == Zone.HAND)) {
|
||||||
|
|
@ -2569,6 +2569,13 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// library
|
||||||
|
if (target.getOriginalTarget() instanceof TargetCardInLibrary
|
||||||
|
|| (target.getOriginalTarget() instanceof TargetCard && target.getOriginalTarget().getZone() == Zone.LIBRARY)) {
|
||||||
|
// user don't have access to library, so it must be targeted through list/revealed cards
|
||||||
|
Assert.fail("Library zone is private, you must target through cards list, e.g. revealed: " + target.getOriginalTarget().getClass().getCanonicalName());
|
||||||
|
}
|
||||||
|
|
||||||
// uninplemented TargetCard's zone
|
// uninplemented TargetCard's zone
|
||||||
if (target.getOriginalTarget() instanceof TargetCard && !targetCardZonesChecked.contains(target.getOriginalTarget().getZone())) {
|
if (target.getOriginalTarget() instanceof TargetCard && !targetCardZonesChecked.contains(target.getOriginalTarget().getZone())) {
|
||||||
Assert.fail("Found unimplemented TargetCard's zone or TargetCard's extented class: "
|
Assert.fail("Found unimplemented TargetCard's zone or TargetCard's extented class: "
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ import mage.game.stack.Spell;
|
||||||
import mage.game.stack.StackAbility;
|
import mage.game.stack.StackAbility;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
|
import mage.target.TargetCard;
|
||||||
import mage.target.Targets;
|
import mage.target.Targets;
|
||||||
|
import mage.target.common.TargetCardInLibrary;
|
||||||
import mage.target.targetadjustment.TargetAdjuster;
|
import mage.target.targetadjustment.TargetAdjuster;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
import mage.util.GameLog;
|
import mage.util.GameLog;
|
||||||
|
|
@ -881,6 +883,12 @@ public abstract class AbilityImpl implements Ability {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTarget(Target target) {
|
public void addTarget(Target target) {
|
||||||
|
// verify check
|
||||||
|
if (target instanceof TargetCardInLibrary
|
||||||
|
|| (target instanceof TargetCard && target.getZone().equals(Zone.LIBRARY))) {
|
||||||
|
throw new IllegalArgumentException("Wrong usage of TargetCardInLibrary - you must use it with SearchLibrary only");
|
||||||
|
}
|
||||||
|
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
getTargets().add(target);
|
getTargets().add(target);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,19 @@ public class ConditionalAsThoughEffect extends AsThoughEffectImpl {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean applies(UUID sourceId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||||
|
conditionState = condition.apply(game, source);
|
||||||
|
if (conditionState) {
|
||||||
|
effect.setTargetPointer(this.targetPointer);
|
||||||
|
return effect.applies(sourceId, affectedAbility, source, game, playerId);
|
||||||
|
} else if (otherwiseEffect != null) {
|
||||||
|
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||||
|
return otherwiseEffect.applies(sourceId, affectedAbility, source, game, playerId);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
|
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
|
||||||
conditionState = condition.apply(game, source);
|
conditionState = condition.apply(game, source);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ public interface AsThoughEffect extends ContinuousEffect {
|
||||||
* Apply to ONE affected ability from the object (sourceId)
|
* Apply to ONE affected ability from the object (sourceId)
|
||||||
* <p>
|
* <p>
|
||||||
* Warning, if you don't need ability to check then ignore it (by default it calls full object check)
|
* Warning, if you don't need ability to check then ignore it (by default it calls full object check)
|
||||||
|
* Warning, if you use conditional effect then you must override both applies methods to support different types
|
||||||
*
|
*
|
||||||
* @param sourceId
|
* @param sourceId
|
||||||
* @param affectedAbility ability to check (example: check if spell ability can be cast from non hand)
|
* @param affectedAbility ability to check (example: check if spell ability can be cast from non hand)
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,8 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl {
|
||||||
if (affectedAbility == null) {
|
if (affectedAbility == null) {
|
||||||
// ContinuousEffects.asThough already checks affectedAbility, so that error must never be called here
|
// ContinuousEffects.asThough already checks affectedAbility, so that error must never be called here
|
||||||
// PLAY_FROM_NOT_OWN_HAND_ZONE must applies to affectedAbility only
|
// PLAY_FROM_NOT_OWN_HAND_ZONE must applies to affectedAbility only
|
||||||
|
// If you see it then parent conditional effect must override both applies methods to support different
|
||||||
|
// AsThough effect types in one conditional effect
|
||||||
throw new IllegalArgumentException("ERROR, can't call applies method on empty affectedAbility");
|
throw new IllegalArgumentException("ERROR, can't call applies method on empty affectedAbility");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3794,8 +3794,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
ApprovingObject approvingObject;
|
ApprovingObject approvingObject;
|
||||||
if (isPlaySpell || isPlayLand) {
|
if ((isPlaySpell || isPlayLand) && (fromZone != Zone.BATTLEFIELD)) {
|
||||||
// play hand from non hand zone
|
// play hand from non hand zone (except battlefield - you can't play already played permanents)
|
||||||
approvingObject = game.getContinuousEffects().asThough(object.getId(),
|
approvingObject = game.getContinuousEffects().asThough(object.getId(),
|
||||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, ability, this.getId(), game);
|
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, ability, this.getId(), game);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,9 @@ import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
|
* Can be used with SearchLibrary only. User hasn't access to libs.
|
||||||
|
*
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
public class TargetCardInLibrary extends TargetCard {
|
public class TargetCardInLibrary extends TargetCard {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue