Merge origin/master

This commit is contained in:
LevelX2 2015-06-13 00:33:58 +02:00
commit d28a956754
22 changed files with 430 additions and 104 deletions

View file

@ -51,7 +51,7 @@ public class JoinTableDialog extends MageDialog {
public JoinTableDialog() {
initComponents();
newPlayerPanel.showLevel(false);
txtPassword.setText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD, ""));
txtPassword.setText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD_JOIN, ""));
}
public void showDialog(UUID roomId, UUID tableId, boolean isTournament, boolean isLimited) {
@ -148,7 +148,7 @@ public class JoinTableDialog extends MageDialog {
private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed
Session session = MageFrame.getSession();
try {
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD, txtPassword.getText());
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD_JOIN, txtPassword.getText());
if (isTournament) {
joined = session.joinTournamentTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), "Human", 1, DeckImporterUtil.importDeck(this.newPlayerPanel.getDeckFile()), this.txtPassword.getText());
} else {

View file

@ -164,6 +164,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
// pref setting for new table dialog
public static final String KEY_NEW_TABLE_NAME = "newTableName";
public static final String KEY_NEW_TABLE_PASSWORD = "newTablePassword";
public static final String KEY_NEW_TABLE_PASSWORD_JOIN = "newTablePasswordJoin";
public static final String KEY_NEW_TABLE_DECK_TYPE = "newTableDeckType";
public static final String KEY_NEW_TABLE_TIME_LIMIT = "newTableTimeLimit";
public static final String KEY_NEW_TABLE_GAME_TYPE = "newTableGameType";

View file

@ -29,6 +29,7 @@
package mage.sets.conflux;
import java.util.UUID;
import mage.MageObject;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
@ -36,6 +37,7 @@ import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.SpecialAction;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.RemoveDelayedTriggeredAbilityEffect;
@ -78,7 +80,8 @@ class QuenchableFireEffect extends OneShotEffect {
public QuenchableFireEffect() {
super(Outcome.Damage);
staticText = "{this} deals an additional 3 damage to that player at the beginning of your next upkeep step unless he or she pays {U} before that step";
staticText = "{this} deals an additional 3 damage to that player at the beginning of your next upkeep step unless he or she pays {U} before that step."
+ "<br><i>Use the Special button to pay the {U} with a special action before the beginning of your next upkeep step.</i>";
}
public QuenchableFireEffect(final QuenchableFireEffect effect) {
@ -92,22 +95,34 @@ class QuenchableFireEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
//create delayed triggered ability
QuenchableFireDelayedTriggeredAbility delayedAbility = new QuenchableFireDelayedTriggeredAbility();
delayedAbility.setSourceId(source.getSourceId());
delayedAbility.setControllerId(source.getControllerId());
delayedAbility.setSourceObject(source.getSourceObject(game), game);
delayedAbility.getTargets().addAll(source.getTargets());
game.addDelayedTriggeredAbility(delayedAbility);
MageObject sourceObject = source.getSourceObject(game);
if (sourceObject != null) {
//create special action
QuenchableFireSpecialAction newAction = new QuenchableFireSpecialAction();
//create special action
QuenchableFireSpecialAction newAction = new QuenchableFireSpecialAction(delayedAbility.getId());
delayedAbility.setSpecialActionId(newAction.getId());
newAction.setSourceId(source.getSourceId());
newAction.setControllerId(source.getFirstTarget());
newAction.getTargets().addAll(source.getTargets());
game.getState().getSpecialActions().add(newAction);
return true;
//create delayed triggered ability
QuenchableFireDelayedTriggeredAbility delayedAbility = new QuenchableFireDelayedTriggeredAbility();
delayedAbility.setSourceId(source.getSourceId());
delayedAbility.setControllerId(source.getControllerId());
delayedAbility.setSourceObject(sourceObject, game);
delayedAbility.getTargets().addAll(source.getTargets());
delayedAbility.setSpecialActionId(newAction.getId());
UUID delayedAbilityId = game.addDelayedTriggeredAbility(delayedAbility);
// update special action
newAction.addCost(new ManaCostsImpl("{U}"));
Effect effect = new RemoveDelayedTriggeredAbilityEffect(delayedAbilityId);
newAction.addEffect(effect);
effect.setText(sourceObject.getIdName() + " - Pay {U} to remove the triggered ability that deals 3 damage to you at the beginning of your next upkeep step");
newAction.addEffect(new RemoveSpecialActionEffect(newAction.getId()));
newAction.setSourceId(source.getSourceId());
newAction.setControllerId(source.getFirstTarget());
newAction.getTargets().addAll(source.getTargets());
game.getState().getSpecialActions().add(newAction);
return true;
}
return false;
}
}
@ -151,11 +166,8 @@ class QuenchableFireDelayedTriggeredAbility extends DelayedTriggeredAbility {
class QuenchableFireSpecialAction extends SpecialAction {
public QuenchableFireSpecialAction(UUID effectId) {
public QuenchableFireSpecialAction() {
super();
this.addCost(new ManaCostsImpl("{U}"));
this.addEffect(new RemoveDelayedTriggeredAbilityEffect(effectId));
this.addEffect(new RemoveSpecialActionEffect(this.getId()));
}
public QuenchableFireSpecialAction(final QuenchableFireSpecialAction ability) {

View file

@ -42,6 +42,7 @@ import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.effects.common.DontUntapInControllersUntapStepEnchantedEffect;
import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect;
import mage.abilities.effects.common.UntapEnchantedEffect;
import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
@ -96,7 +97,7 @@ public class DanceOfTheDead extends CardImpl {
// Enchanted creature gets +1/+1 and doesn't untap during its controller's untap step.
ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield));
Effect effect = new DontUntapInControllersUntapStepSourceEffect();
Effect effect = new DontUntapInControllersUntapStepEnchantedEffect();
effect.setText("and doesn't untap during its controller's untap step");
ability.addEffect(effect);
this.addAbility(ability);
@ -144,7 +145,7 @@ class DanceOfTheDeadReAttachEffect extends OneShotEffect {
}
// put card into play
controller.putOntoBattlefieldWithInfo(cardInGraveyard, game, Zone.GRAVEYARD, source.getSourceId());
controller.putOntoBattlefieldWithInfo(cardInGraveyard, game, Zone.GRAVEYARD, source.getSourceId(), true);
Permanent enchantedCreature = game.getPermanent(cardInGraveyard.getId());
FilterCreaturePermanent filter = new FilterCreaturePermanent("enchant creature put onto the battlefield with Dance of the Dead");

View file

@ -110,7 +110,9 @@ class PhantasmalImageCopyEffect extends OneShotEffect {
if (!permanent.getSubtype().contains("Illusion")) {
permanent.getSubtype().add("Illusion");
}
permanent.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), game);
// Add directly because the created permanent is only used to copy from, so there is no need to add the ability to e.g. TriggeredAbilities
permanent.getAbilities().add(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()));
//permanent.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), game);
return true;
}
});

View file

@ -36,8 +36,10 @@ import mage.constants.Zone;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.EntersBattlefieldEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CopyPermanentEffect;
import mage.cards.CardImpl;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
@ -54,7 +56,15 @@ import mage.util.functions.ApplyToPermanent;
* @author Loki
*/
public class PhyrexianMetamorph extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("artifact or creature");
static {
filter.add(Predicates.or(
new CardTypePredicate(CardType.ARTIFACT),
new CardTypePredicate(CardType.CREATURE)));
}
public PhyrexianMetamorph (UUID ownerId) {
super(ownerId, 42, "Phyrexian Metamorph", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{UP}");
this.expansionSetCode = "NPH";
@ -63,10 +73,21 @@ public class PhyrexianMetamorph extends CardImpl {
this.power = new MageInt(0);
this.toughness = new MageInt(0);
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new EntersBattlefieldEffect(
new PhyrexianMetamorphEffect(),
"You may have {this} enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types",
true));
ApplyToPermanent phyrexianMetamorphApplier = new ApplyToPermanent() {
@Override
public Boolean apply(Game game, Permanent permanent) {
if (!permanent.getCardType().contains(CardType.ARTIFACT)) {
permanent.getCardType().add(CardType.ARTIFACT);
}
return true;
}
};
// {UP} ( can be paid with either {U} or 2 life.)
// You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types.
Effect effect = new CopyPermanentEffect(filter, phyrexianMetamorphApplier);
effect.setText("You may have {this} enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types");
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new EntersBattlefieldEffect(effect));
this.addAbility(ability);
}
@ -80,56 +101,3 @@ public class PhyrexianMetamorph extends CardImpl {
}
}
class PhyrexianMetamorphEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent("artifact or creature");
static {
filter.add(Predicates.or(
new CardTypePredicate(CardType.ARTIFACT),
new CardTypePredicate(CardType.CREATURE)));
}
public PhyrexianMetamorphEffect() {
super(Outcome.Copy);
}
public PhyrexianMetamorphEffect(final PhyrexianMetamorphEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (player != null && sourcePermanent != null) {
Target target = new TargetPermanent(filter);
target.setNotTarget(true);
if (target.canChoose(source.getControllerId(), game)) {
player.choose(Outcome.Copy, target, source.getSourceId(), game);
Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget());
if (copyFromPermanent != null) {
game.copyPermanent(copyFromPermanent, sourcePermanent, source, new ApplyToPermanent() {
@Override
public Boolean apply(Game game, Permanent permanent) {
if (!permanent.getCardType().contains(CardType.ARTIFACT)) {
permanent.getCardType().add(CardType.ARTIFACT);
}
return true;
}
});
return true;
}
}
}
return false;
}
@Override
public PhyrexianMetamorphEffect copy() {
return new PhyrexianMetamorphEffect(this);
}
}

View file

@ -54,9 +54,11 @@ public class WurmcoilEngine extends CardImpl {
this.power = new MageInt(6);
this.toughness = new MageInt(6);
// Deathtouch, lifelink
this.addAbility(DeathtouchAbility.getInstance());
this.addAbility(LifelinkAbility.getInstance());
// When Wurmcoil Engine dies, put a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink onto the battlefield.
Ability ability = new DiesTriggeredAbility(new CreateTokenEffect(new Wurm1Token(expansionSetCode)), false);
ability.addEffect(new CreateTokenEffect(new Wurm2Token(expansionSetCode)));
this.addAbility(ability);

View file

@ -53,6 +53,7 @@ public class Clone extends CardImpl {
this.power = new MageInt(0);
this.toughness = new MageInt(0);
// You may have Clone enter the battlefield as a copy of any creature on the battlefield.
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new EntersBattlefieldEffect(
new CopyPermanentEffect(),
"You may have {this} enter the battlefield as a copy of any creature on the battlefield",

View file

@ -61,7 +61,11 @@ public class Nightmare extends CardImpl {
this.power = new MageInt(0);
this.toughness = new MageInt(0);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Nightmare's power and toughness are each equal to the number of Swamps you control.
this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new PermanentsOnBattlefieldCount(filter), Duration.EndOfGame)));
}

View file

@ -0,0 +1,142 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.urzasdestiny;
import java.util.UUID;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author cbrianhill
*/
public class Repercussion extends CardImpl {
public Repercussion(UUID ownerId) {
super(ownerId, 95, "Repercussion", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}{R}");
this.expansionSetCode = "UDS";
// Whenever a creature is dealt damage, Repercussion deals that much damage to that creature's controller.
this.addAbility(new RepercussionTriggeredAbility(new RepercussionEffect()));
}
public Repercussion(final Repercussion card) {
super(card);
}
@Override
public Repercussion copy() {
return new Repercussion(this);
}
}
class RepercussionTriggeredAbility extends TriggeredAbilityImpl {
static final String PLAYER_DAMAGE_AMOUNT_KEY = "playerDamage";
static final String TRIGGERING_CREATURE_KEY = "triggeringCreature";
public RepercussionTriggeredAbility(Effect effect) {
super(Zone.BATTLEFIELD, effect);
}
public RepercussionTriggeredAbility(final RepercussionTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_CREATURE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
for(Effect effect : getEffects()) {
effect.setValue(PLAYER_DAMAGE_AMOUNT_KEY, event.getAmount());
effect.setValue(TRIGGERING_CREATURE_KEY, new MageObjectReference(event.getTargetId(), game));
}
return true;
}
@Override
public String getRule() {
return "Whenever a creature is dealt damage, {this} deals that much damage to that creature's controller.";
}
@Override
public TriggeredAbility copy() {
return new RepercussionTriggeredAbility(this);
}
}
class RepercussionEffect extends OneShotEffect {
public RepercussionEffect() {
super(Outcome.Damage);
}
public RepercussionEffect(final RepercussionEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Integer playerDamage = (Integer)this.getValue(RepercussionTriggeredAbility.PLAYER_DAMAGE_AMOUNT_KEY);
MageObjectReference mor = (MageObjectReference)this.getValue(RepercussionTriggeredAbility.TRIGGERING_CREATURE_KEY);
if (playerDamage != null && mor != null) {
Permanent creature = mor.getPermanentOrLKIBattlefield(game);
if (creature != null) {
Player player = game.getPlayer(creature.getControllerId());
if (player != null) {
player.damage(playerDamage, source.getSourceId(), game, false, true);
}
}
return true;
}
return false;
}
@Override
public Effect copy() {
return new RepercussionEffect(this);
}
}

View file

@ -40,7 +40,6 @@ import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.common.FilterNonlandPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.target.common.TargetControlledPermanent;

View file

@ -108,9 +108,11 @@ public class CloneTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 6);
addCard(Zone.BATTLEFIELD, playerB, "Forest", 1);
// Target creature you control gets +1/+1 and gains hexproof until end of turn. (It can't be the target of spells or abilities your opponents control.)
addCard(Zone.HAND, playerB, "Ranger's Guile");
addCard(Zone.HAND, playerA, "Clone");
// Return target nonland permanent to its owner's hand.
addCard(Zone.HAND, playerA, "Disperse");
addCard(Zone.BATTLEFIELD, playerB, "Nightmare", 1);

View file

@ -525,5 +525,50 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
assertPermanentCount(playerB, "Butcher Ghoul", 1);
assertPowerToughness(playerB, "Butcher Ghoul", 2, 2);
}
/**
* 12:29: Attacker: Wurmcoil Engine [466] (6/6) blocked by Wurmcoil Engine
* [4ed] (6/6)
* 12:29: yespair gains 6 life
* 12:29: HipSomHap gains 6 life
* 12:29: Wurmcoil Engine [4ed] died
* 12:29: Ability triggers: Wurmcoil Engine [4ed] - When Wurmcoil Engine [4ed] dies, put a a 3/3 colorless
* Wurm artifact creature token with deathtouch onto the battlefield. Put a
* a 3/3 colorless Wurm artifact creature token with lifelink onto the
* battlefield.
* 12:29: Phantasmal Image [466] died
* 12:29: HipSomHap puts a Wurm [7d0] token onto the battlefield
* 12:29: HipSomHap puts a Wurm [186] token onto the battlefield
*
* To the best of my knowledge, the Phantasmal Image [466], which entered
* the battlefield as a Wurmcoil Engine, should grant tokens through the
* Dies-trigger as well, right?
*/
@Test
public void testDiesTriggered2() {
addCard(Zone.BATTLEFIELD, playerB, "Wurmcoil Engine");
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.HAND, playerA, "Phantasmal Image");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phantasmal Image"); // not targeted
setChoice(playerB, "Wurmcoil Engine");
attack(2, playerB, "Wurmcoil Engine");
block(2, playerA, "Wurmcoil Engine", "Wurmcoil Engine");
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertLife(playerB, 26);
assertLife(playerA, 26);
assertGraveyardCount(playerA, "Phantasmal Image", 1);
assertGraveyardCount(playerB, "Wurmcoil Engine", 1);
assertPermanentCount(playerA, "Wurm", 2);
assertPermanentCount(playerB, "Wurm", 2);
}
}

View file

@ -0,0 +1,124 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package org.mage.test.cards.copy;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class PhyrexianMetamorphTest extends CardTestPlayerBase {
@Test
public void testCopyCreature() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
// You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types.
addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{UP}
addCard(Zone.HAND, playerA, "Cloudshift");
//Flying
// Vanishing 3 (This permanent enters the battlefield with three time counters on it. At the beginning of your upkeep, remove a time counter from it. When the last is removed, sacrifice it.)
// When Aven Riftwatcher enters the battlefield or leaves the battlefield, you gain 2 life.
addCard(Zone.BATTLEFIELD, playerB, "Aven Riftwatcher"); // 2/3
// When Ponyback Brigade enters the battlefield or is turned face up, put three 1/1 red Goblin creature tokens onto the battlefield.
addCard(Zone.BATTLEFIELD, playerB, "Ponyback Brigade"); // 2/2
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phyrexian Metamorph");
setChoice(playerA, "Aven Riftwatcher");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cloudshift", "Aven Riftwatcher");
setChoice(playerA, "Ponyback Brigade");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerA, 24);
assertLife(playerB, 20);
assertGraveyardCount(playerA, "Cloudshift", 1);
assertPermanentCount(playerA, "Ponyback Brigade", 1);
assertPermanentCount(playerA, "Goblin", 3);
}
/**
* An opponent cast Phyrexian Metamorph and cloned another opponent's
* Maelstrom Wanderer(his Commander). The first opponent then dealt combat
* damage with Brago, King Eternal and chose to flicker several permanents,
* including the Phyrexian Metamorph/Maelstrom Wanderer, but he was not able
* to choose a new creature to clone when the Phyrexian Metamorph re-entered
* the battlefield.
*/
@Test
public void testFlickerWithBrago() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
// You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types.
addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{UP}
// Flying
// When Brago, King Eternal deals combat damage to a player, exile any number of target nonland permanents you control, then return those cards to the battlefield under their owner's control.
addCard(Zone.BATTLEFIELD, playerA, "Brago, King Eternal"); // 2/4
// Creatures you control have haste.
// Cascade, cascade
addCard(Zone.BATTLEFIELD, playerB, "Maelstrom Wanderer"); // 7/5
// When Ponyback Brigade enters the battlefield or is turned face up, put three 1/1 red Goblin creature tokens onto the battlefield.
addCard(Zone.BATTLEFIELD, playerB, "Ponyback Brigade"); // 2/2
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phyrexian Metamorph");
setChoice(playerA, "Maelstrom Wanderer");
attack(3, playerA, "Brago, King Eternal");
addTarget(playerA, "Maelstrom Wanderer");
setChoice(playerA, "Ponyback Brigade");
setStopAt(3, PhaseStep.END_COMBAT);
execute();
assertLife(playerA, 20);
assertLife(playerB, 18);
assertPermanentCount(playerA, "Ponyback Brigade", 1);
assertPermanentCount(playerA, "Goblin", 3);
}
}

View file

@ -247,7 +247,7 @@ public abstract class AbilityImpl implements Ability {
return false;
}
game.applyEffects();
/* 20130201 - 601.2b
* If the spell is modal the player announces the mode choice (see rule 700.2).
*/

View file

@ -61,4 +61,7 @@ public interface ContinuousEffect extends Effect {
void newId();
@Override
ContinuousEffect copy();
boolean isTemporary();
void setTemporary(boolean temporary);
}

View file

@ -68,7 +68,8 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
protected boolean discarded = false; // for manual effect discard
protected boolean affectedObjectsSet = false;
protected List<MageObjectReference> affectedObjectList = new ArrayList<>();
protected boolean temporary = false;
// until your next turn
protected int startingTurn;
protected UUID startingControllerId;
@ -96,6 +97,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
this.discarded = effect.discarded;
this.affectedObjectsSet = effect.affectedObjectsSet;
this.affectedObjectList.addAll(effect.affectedObjectList);
this.temporary = effect.temporary;
this.startingTurn = effect.startingTurn;
this.startingControllerId = effect.startingControllerId;
}
@ -234,4 +236,18 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
return affectedObjectList;
}
/**
* Returns the status if the effect is temporary added to the ContinuousEffects
* @return
*/
@Override
public boolean isTemporary() {
return temporary;
}
@Override
public void setTemporary(boolean temporary) {
this.temporary = temporary;
}
}

View file

@ -939,6 +939,7 @@ public class ContinuousEffects implements Serializable {
*/
public void addEffect(ContinuousEffect effect, UUID sourceId, Ability source) {
if (!(source instanceof MageSingleton)) { // because MageSingletons may never be removed by removing the temporary effecs they are not added to the temporaryEffects to prevent this
effect.setTemporary(true);
Set abilities = temporaryEffects.get(effect);
if (abilities == null) {
abilities = new HashSet<>();

View file

@ -40,6 +40,7 @@ import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
@ -56,6 +57,7 @@ public class CopyEffect extends ContinuousEffectImpl {
* Object we copy from
*/
private MageObject target;
private UUID sourceId;
private ApplyToPermanent applier;
@ -79,23 +81,21 @@ public class CopyEffect extends ContinuousEffectImpl {
@Override
public void init(Ability source, Game game) {
super.init(source, game);
if (affectedObjectsSet) {
affectedObjectList.add(new MageObjectReference(sourceId, game));
}
affectedObjectList.add(new MageObjectReference(getSourceId(), game));
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent;
if (affectedObjectsSet) {
permanent = affectedObjectList.get(0).getPermanent(game);
} else {
permanent = game.getPermanent(this.sourceId);
}
Permanent permanent = affectedObjectList.get(0).getPermanent(game);
if (permanent == null) {
discard();
return false;
permanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD, source.getSourceObjectZoneChangeCounter());
// As long as the permanent is still in the LKI continue to copy to get triggered abilities to TriggeredAbilites for dies events.
if (permanent == null) {
discard();
return false;
}
}
permanent.setCopy(true);
permanent.setName(target.getName());
permanent.getColor(game).setColor(target.getColor(game));
permanent.getManaCost().clear();
@ -134,9 +134,7 @@ public class CopyEffect extends ContinuousEffectImpl {
} else if (target instanceof PermanentToken || target instanceof Card) {
permanent.setCardNumber(((Card) target).getCardNumber());
permanent.setExpansionSetCode(((Card) target).getExpansionSetCode());
}
permanent.setCopy(true);
}
return true;
}

View file

@ -36,8 +36,8 @@ import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
@ -76,12 +76,16 @@ public class SetPowerToughnessSourceEffect extends ContinuousEffectImpl {
@Override
public boolean apply(Game game, Ability source) {
MageObject mageObject = game.getObject(source.getSourceId());
MageObject mageObject = game.getObject(source.getSourceId());
if (mageObject == null) {
if (duration.equals(Duration.Custom)) {
discard();
}
return false;
} else if (isTemporary()) { // it's somehow w
if (!(mageObject instanceof Permanent)) {
return false;
}
}
if (amount != null) {
int value = amount.calculate(game, source, this);

View file

@ -253,7 +253,7 @@ public interface Game extends MageItem, Serializable {
Card copyCard(Card cardToCopy, Ability source, UUID newController);
void addTriggeredAbility(TriggeredAbility ability);
void addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility);
UUID addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility);
void applyEffects();
boolean checkStateAndTriggered();
void playPriority(UUID activePlayerId, boolean resuming);

View file

@ -1383,11 +1383,12 @@ public abstract class GameImpl implements Game, Serializable {
}
@Override
public void addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility) {
public UUID addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility) {
DelayedTriggeredAbility newAbility = delayedAbility.copy();
newAbility.newId();
newAbility.init(this);
state.addDelayedTriggeredAbility(newAbility);
return newAbility.getId();
}
/**