mirror of
https://github.com/magefree/mage.git
synced 2025-12-22 11:32:00 -08:00
* Fixed that continuous effects of copied cards with limited duration stop to work as the copied card stops to exist.
This commit is contained in:
parent
7292a1625c
commit
d3dba58358
11 changed files with 113 additions and 38 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
package mage.client.util.layout;
|
package mage.client.util.layout;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.JLayeredPane;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for operations that modify cards' layout
|
* Interface for operations that modify cards' layout
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
package mage.client.util.layout.impl;
|
package mage.client.util.layout.impl;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.swing.JLayeredPane;
|
||||||
import mage.cards.MagePermanent;
|
import mage.cards.MagePermanent;
|
||||||
import mage.client.game.BattlefieldPanel;
|
import mage.client.game.BattlefieldPanel;
|
||||||
import mage.client.plugins.impl.Plugins;
|
import mage.client.plugins.impl.Plugins;
|
||||||
import mage.client.util.layout.CardLayoutStrategy;
|
import mage.client.util.layout.CardLayoutStrategy;
|
||||||
import mage.view.PermanentView;
|
import mage.view.PermanentView;
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Card layout for client version 1.3.0 and earlier.
|
* Card layout for client version 1.3.0 and earlier.
|
||||||
*
|
*
|
||||||
|
|
@ -54,14 +54,14 @@ public class OldCardLayoutStrategy implements CardLayoutStrategy {
|
||||||
}
|
}
|
||||||
int position = jLayeredPane.getPosition(perm);
|
int position = jLayeredPane.getPosition(perm);
|
||||||
perm.getLinks().clear();
|
perm.getLinks().clear();
|
||||||
Rectangle r = perm.getBounds();
|
Rectangle rectangleBaseCard = perm.getBounds();
|
||||||
if (!Plugins.getInstance().isCardPluginLoaded()) {
|
if (!Plugins.getInstance().isCardPluginLoaded()) {
|
||||||
for (UUID attachmentId: permanent.getAttachments()) {
|
for (UUID attachmentId: permanent.getAttachments()) {
|
||||||
MagePermanent link = permanents.get(attachmentId);
|
MagePermanent link = permanents.get(attachmentId);
|
||||||
if (link != null) {
|
if (link != null) {
|
||||||
perm.getLinks().add(link);
|
perm.getLinks().add(link);
|
||||||
r.translate(20, 20);
|
rectangleBaseCard.translate(20, 20);
|
||||||
link.setBounds(r);
|
link.setBounds(rectangleBaseCard);
|
||||||
jLayeredPane.setPosition(link, ++position);
|
jLayeredPane.setPosition(link, ++position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -70,14 +70,14 @@ public class OldCardLayoutStrategy implements CardLayoutStrategy {
|
||||||
for (UUID attachmentId: permanent.getAttachments()) {
|
for (UUID attachmentId: permanent.getAttachments()) {
|
||||||
MagePermanent link = permanents.get(attachmentId);
|
MagePermanent link = permanents.get(attachmentId);
|
||||||
if (link != null) {
|
if (link != null) {
|
||||||
link.setBounds(r);
|
link.setBounds(rectangleBaseCard);
|
||||||
perm.getLinks().add(link);
|
perm.getLinks().add(link);
|
||||||
if (index == 1) {
|
if (index == 1) {
|
||||||
r.translate(ATTACHMENTS_DX_OFFSET, ATTACHMENT_DY_OFFSET); // do it once
|
rectangleBaseCard.translate(ATTACHMENTS_DX_OFFSET, ATTACHMENT_DY_OFFSET); // do it once
|
||||||
} else {
|
} else {
|
||||||
r.translate(ATTACHMENT_DX_OFFSET, ATTACHMENT_DY_OFFSET);
|
rectangleBaseCard.translate(ATTACHMENT_DX_OFFSET, ATTACHMENT_DY_OFFSET);
|
||||||
}
|
}
|
||||||
perm.setBounds(r);
|
perm.setBounds(rectangleBaseCard);
|
||||||
jLayeredPane.moveToFront(link);
|
jLayeredPane.moveToFront(link);
|
||||||
jLayeredPane.moveToFront(perm);
|
jLayeredPane.moveToFront(perm);
|
||||||
jPanel.setComponentZOrder(link, index);
|
jPanel.setComponentZOrder(link, index);
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,10 @@
|
||||||
package mage.sets.magic2012;
|
package mage.sets.magic2012;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import mage.constants.*;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.DiesAttachedTriggeredAbility;
|
import mage.abilities.common.DiesAttachedTriggeredAbility;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.effects.Effect;
|
||||||
import mage.abilities.effects.common.AttachEffect;
|
import mage.abilities.effects.common.AttachEffect;
|
||||||
import mage.abilities.effects.common.ReturnToHandSourceEffect;
|
import mage.abilities.effects.common.ReturnToHandSourceEffect;
|
||||||
import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect;
|
import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect;
|
||||||
|
|
@ -42,6 +41,12 @@ import mage.abilities.keyword.EnchantAbility;
|
||||||
import mage.abilities.keyword.FirstStrikeAbility;
|
import mage.abilities.keyword.FirstStrikeAbility;
|
||||||
import mage.abilities.keyword.FlyingAbility;
|
import mage.abilities.keyword.FlyingAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
|
import mage.constants.AttachmentType;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.Duration;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.Rarity;
|
||||||
|
import mage.constants.Zone;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.target.common.TargetCreaturePermanent;
|
import mage.target.common.TargetCreaturePermanent;
|
||||||
|
|
||||||
|
|
@ -56,19 +61,27 @@ public class AngelicDestiny extends CardImpl {
|
||||||
this.expansionSetCode = "M12";
|
this.expansionSetCode = "M12";
|
||||||
this.subtype.add("Aura");
|
this.subtype.add("Aura");
|
||||||
|
|
||||||
this.color.setWhite(true);
|
// Enchant creature
|
||||||
|
|
||||||
TargetPermanent auraTarget = new TargetCreaturePermanent();
|
TargetPermanent auraTarget = new TargetCreaturePermanent();
|
||||||
this.getSpellAbility().addTarget(auraTarget);
|
this.getSpellAbility().addTarget(auraTarget);
|
||||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
|
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
|
||||||
Ability ability = new EnchantAbility(auraTarget.getTargetName());
|
Ability ability = new EnchantAbility(auraTarget.getTargetName());
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
||||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(4, 4, Duration.WhileOnBattlefield)));
|
// Enchanted creature gets +4/+4, has flying and first strike, and is an Angel in addition to its other types.
|
||||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA)));
|
ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(4, 4, Duration.WhileOnBattlefield));
|
||||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.AURA)));
|
Effect effect = new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA);
|
||||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AddCardSubtypeAttachedEffect("Angel", Duration.WhileOnBattlefield, AttachmentType.AURA)));
|
effect.setText(", has flying");
|
||||||
|
ability.addEffect(effect);
|
||||||
|
effect = new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.AURA);
|
||||||
|
effect.setText("and first strike");
|
||||||
|
ability.addEffect(effect);
|
||||||
|
effect = new AddCardSubtypeAttachedEffect("Angel", Duration.WhileOnBattlefield, AttachmentType.AURA);
|
||||||
|
effect.setText(", and is an Angel in addition to its other types");
|
||||||
|
ability.addEffect(effect);
|
||||||
|
this.addAbility(ability);
|
||||||
|
|
||||||
|
// When enchanted creature dies, return Angelic Destiny to its owner's hand.
|
||||||
this.addAbility(new DiesAttachedTriggeredAbility(new ReturnToHandSourceEffect(), "enchanted creature"));
|
this.addAbility(new DiesAttachedTriggeredAbility(new ReturnToHandSourceEffect(), "enchanted creature"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ public class IsochronScepter extends CardImpl {
|
||||||
this.expansionSetCode = "MRD";
|
this.expansionSetCode = "MRD";
|
||||||
|
|
||||||
// Imprint - When Isochron Scepter enters the battlefield, you may exile an instant card with converted mana cost 2 or less from your hand.
|
// Imprint - When Isochron Scepter enters the battlefield, you may exile an instant card with converted mana cost 2 or less from your hand.
|
||||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new IsochronScepterImprintEffect(), true, "<i>Imprint - </i>"));
|
this.addAbility(new EntersBattlefieldTriggeredAbility(new IsochronScepterImprintEffect(), true, "<i>Imprint — </i>"));
|
||||||
|
|
||||||
// {2}, {tap}: You may copy the exiled card. If you do, you may cast the copy without paying its mana cost.
|
// {2}, {tap}: You may copy the exiled card. If you do, you may cast the copy without paying its mana cost.
|
||||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new IsochronScepterCopyEffect(), new GenericManaCost(2));
|
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new IsochronScepterCopyEffect(), new GenericManaCost(2));
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
package mage.sets.returntoravnica;
|
package mage.sets.returntoravnica;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import mage.MageObject;
|
||||||
import mage.constants.AttachmentType;
|
import mage.constants.AttachmentType;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Duration;
|
import mage.constants.Duration;
|
||||||
|
|
@ -64,7 +65,6 @@ public class SoulTithe extends CardImpl {
|
||||||
super(ownerId, 23, "Soul Tithe", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}");
|
super(ownerId, 23, "Soul Tithe", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}");
|
||||||
this.expansionSetCode = "RTR";
|
this.expansionSetCode = "RTR";
|
||||||
this.subtype.add("Aura");
|
this.subtype.add("Aura");
|
||||||
this.color.setWhite(true);
|
|
||||||
|
|
||||||
// Enchant nonland permanent
|
// Enchant nonland permanent
|
||||||
TargetPermanent auraTarget = new TargetNonlandPermanent();
|
TargetPermanent auraTarget = new TargetNonlandPermanent();
|
||||||
|
|
@ -106,12 +106,12 @@ class SoulTitheSacrificeSourceUnlessPaysEffect extends OneShotEffect {
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Player player = game.getPlayer(source.getControllerId());
|
Player player = game.getPlayer(source.getControllerId());
|
||||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||||
if (player != null && permanent != null) {
|
MageObject sourceObject = source.getSourceObject(game);
|
||||||
|
if (player != null && permanent != null && sourceObject != null) {
|
||||||
int cmc = permanent.getManaCost().convertedManaCost();
|
int cmc = permanent.getManaCost().convertedManaCost();
|
||||||
if (player.chooseUse(Outcome.Benefit, "Pay {" + cmc + "} for " + permanent.getName() + "?", game)) {
|
if (player.chooseUse(Outcome.Benefit, "Pay {" + cmc + "} for " + permanent.getName() + "? (otherwise you sacrifice it)", game)) {
|
||||||
Cost cost = new GenericManaCost(cmc);
|
Cost cost = new GenericManaCost(cmc);
|
||||||
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false))
|
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,4 +111,35 @@ public class IsochronScepterTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAngelsGrace() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
|
||||||
|
addCard(Zone.HAND, playerA, "Isochron Scepter");
|
||||||
|
addCard(Zone.HAND, playerA, "Angel's Grace");
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Dross Crocodile", 4);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Isochron Scepter");
|
||||||
|
addTarget(playerA, "Angel's Grace");
|
||||||
|
|
||||||
|
attack(2, playerB, "Dross Crocodile");
|
||||||
|
attack(2, playerB, "Dross Crocodile");
|
||||||
|
attack(2, playerB, "Dross Crocodile");
|
||||||
|
attack(2, playerB, "Dross Crocodile");
|
||||||
|
|
||||||
|
activateAbility(2, PhaseStep.DECLARE_BLOCKERS, playerA, "{2},{T}:");
|
||||||
|
setChoice(playerA, "Yes");
|
||||||
|
setChoice(playerA, "Yes");
|
||||||
|
|
||||||
|
setStopAt(2, PhaseStep.END_COMBAT);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerA, "Isochron Scepter", 1);
|
||||||
|
assertExileCount("Angel's Grace", 1);
|
||||||
|
assertGraveyardCount(playerA, "Angel's Grace", 0);
|
||||||
|
|
||||||
|
assertLife(playerA, 1);
|
||||||
|
assertLife(playerB, 20);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -127,10 +127,8 @@ class PutIntoGraveFromAnywhereEffect extends ReplacementEffectImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||||
if (((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD
|
if (((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD
|
||||||
&& event.getTargetId().equals(source.getSourceId()))
|
&& event.getTargetId().equals(source.getSourceId())) {
|
||||||
{
|
if (condition == null || condition.apply(game, source)) {
|
||||||
if (condition == null || condition.apply(game, source))
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -351,10 +351,8 @@ public class ContinuousEffects implements Serializable {
|
||||||
if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, event)) {
|
if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, event)) {
|
||||||
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
||||||
if (!game.getScopeRelevant() || effect.hasSelfScope() || !event.getTargetId().equals(ability.getSourceId())) {
|
if (!game.getScopeRelevant() || effect.hasSelfScope() || !event.getTargetId().equals(ability.getSourceId())) {
|
||||||
if (checkAbilityStillExists(ability, effect, event, game)) { // TODO: This is really needed???
|
if (effect.applies(event, ability, game)) {
|
||||||
if (effect.applies(event, ability, game)) {
|
applicableAbilities.add(ability);
|
||||||
applicableAbilities.add(ability);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,10 +117,15 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
|
||||||
return false;
|
return false;
|
||||||
} else if (effect.isDiscarded()) {
|
} else if (effect.isDiscarded()) {
|
||||||
it.remove();
|
it.remove();
|
||||||
} else if (ability.getSourceId() != null && game.getObject(ability.getSourceId()) == null) { // Commander effects have no sourceId
|
|
||||||
it.remove(); // if the related source object does no longer exist the effect has to be removed
|
|
||||||
} else {
|
} else {
|
||||||
switch(effect.getDuration()) {
|
switch(effect.getDuration()) {
|
||||||
|
case WhileOnBattlefield:
|
||||||
|
case WhileInGraveyard:
|
||||||
|
case WhileOnStack:
|
||||||
|
if (ability.getSourceId() != null && game.getObject(ability.getSourceId()) == null) { // Commander effects have no sourceId
|
||||||
|
it.remove(); // if the related source object does no longer exist in game - the effect has to be removed
|
||||||
|
}
|
||||||
|
break;
|
||||||
case OneUse:
|
case OneUse:
|
||||||
if (effect.isUsed()) {
|
if (effect.isUsed()) {
|
||||||
it.remove();
|
it.remove();
|
||||||
|
|
|
||||||
|
|
@ -1424,6 +1424,35 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
Card card = copiedCards.next();
|
Card card = copiedCards.next();
|
||||||
Zone zone = state.getZone(card.getId());
|
Zone zone = state.getZone(card.getId());
|
||||||
if (zone != Zone.BATTLEFIELD && zone != Zone.STACK) {
|
if (zone != Zone.BATTLEFIELD && zone != Zone.STACK) {
|
||||||
|
switch (zone) {
|
||||||
|
case GRAVEYARD:
|
||||||
|
for(Player player: getPlayers().values()) {
|
||||||
|
if (player.getGraveyard().contains(card.getId())) {
|
||||||
|
player.getGraveyard().remove(card);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HAND:
|
||||||
|
for(Player player: getPlayers().values()) {
|
||||||
|
if (player.getHand().contains(card.getId())) {
|
||||||
|
player.getHand().remove(card);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LIBRARY:
|
||||||
|
for(Player player: getPlayers().values()) {
|
||||||
|
if (player.getLibrary().getCard(card.getId(), this) != null) {
|
||||||
|
player.getLibrary().remove(card.getId(), this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EXILED:
|
||||||
|
getExile().removeCard(card, this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
copiedCards.remove();
|
copiedCards.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,9 +136,10 @@ public class PermanentCard extends PermanentImpl {
|
||||||
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, ArrayList<UUID> appliedEffects) {
|
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, ArrayList<UUID> appliedEffects) {
|
||||||
Zone fromZone = game.getState().getZone(objectId);
|
Zone fromZone = game.getState().getZone(objectId);
|
||||||
Player controller = game.getPlayer(controllerId);
|
Player controller = game.getPlayer(controllerId);
|
||||||
if (controller != null && controller.removeFromBattlefield(this, game)) {
|
if (controller != null) {
|
||||||
ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, controllerId, fromZone, toZone, appliedEffects);
|
ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, controllerId, fromZone, toZone, appliedEffects);
|
||||||
if (!game.replaceEvent(event)) {
|
if (!game.replaceEvent(event)) {
|
||||||
|
controller.removeFromBattlefield(this, game);
|
||||||
Player owner = game.getPlayer(ownerId);
|
Player owner = game.getPlayer(ownerId);
|
||||||
game.rememberLKI(objectId, Zone.BATTLEFIELD, this);
|
game.rememberLKI(objectId, Zone.BATTLEFIELD, this);
|
||||||
if (owner != null) {
|
if (owner != null) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue