changes to the way abilities are added to cards

This commit is contained in:
betasteward 2015-03-23 13:04:09 -04:00
parent 65390e09a6
commit 72ff6f27b3
91 changed files with 2003 additions and 217 deletions

View file

@ -119,7 +119,7 @@ class ChorusOfTheConclaveReplacementEffect extends ReplacementEffectImpl {
if (xCost > 0) {
Ability ability = new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(xCost)));
ability.setRuleVisible(false);
card.addAbility(ability);
game.getState().addOtherAbility(card, ability);
ability.setControllerId(source.getControllerId());
ability.setSourceId(card.getId());
game.getState().addAbility(ability, source.getSourceId(), card);

View file

@ -115,7 +115,7 @@ class VarolzTheScarStripedEffect extends ContinuousEffectImpl {
ScavengeAbility ability = new ScavengeAbility(new ManaCostsImpl(card.getManaCost().getText()));
ability.setSourceId(cardId);
ability.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(cardId, ability);
game.getState().addOtherAbility(card, ability);
}
}
return true;

View file

@ -144,7 +144,7 @@ class GainAbilitySpellsEffect extends ContinuousEffectImpl {
Card card = spell.getCard();
if (card != null) {
if (!card.getAbilities().contains(ability)) {
card.addAbility(ability);
game.getState().addOtherAbility(card, ability);
SoulfireGrandMasterLeavesStackWatcher watcher = (SoulfireGrandMasterLeavesStackWatcher) game.getState().getWatchers().get("SoulfireGrandMasterLeavesStackWatcher");
if (watcher != null) {
watcher.addCardId(card.getId());

View file

@ -103,7 +103,7 @@ class MycosynthGolemEffect extends ReplacementEffectImpl {
if (object != null) {
Card card = (Card) object;
Ability ability = new AffinityForArtifactsAbility();
card.addAbility(ability);
game.getState().addOtherAbility(card, ability);
ability.setControllerId(source.getControllerId());
ability.setSourceId(card.getId());
game.getState().addAbility(ability, source.getSourceId(), card);

View file

@ -150,7 +150,7 @@ class PrimalPlasmaReplacementEffect extends ReplacementEffectImpl {
mageObject.getPower().setValue(2);
mageObject.getToughness().setValue(2);
if (mageObject instanceof Card) {
((Card)mageObject).addAbility(FlyingAbility.getInstance());
game.getState().addOtherAbility((Card)mageObject, FlyingAbility.getInstance());
} else {
((Token)mageObject).addAbility(FlyingAbility.getInstance());
}
@ -159,7 +159,7 @@ class PrimalPlasmaReplacementEffect extends ReplacementEffectImpl {
mageObject.getPower().setValue(1);
mageObject.getToughness().setValue(6);
if (mageObject instanceof Card) {
((Card)mageObject).addAbility(DefenderAbility.getInstance());
game.getState().addOtherAbility((Card)mageObject, DefenderAbility.getInstance());
} else {
((Token)mageObject).addAbility(DefenderAbility.getInstance());
}

View file

@ -112,7 +112,7 @@ class DelayEffect extends OneShotEffect {
// If the exiled card leaves exile by another way, the abilites won't be removed from the card
Abilities oldAbilities = card.getAbilities().copy();
SuspendAbility suspendAbility = new SuspendAbility(3, null, card);
card.addAbility(suspendAbility);
game.getState().addOtherAbility(card, suspendAbility);
for (Ability ability :card.getAbilities()) {
if (!oldAbilities.contains(ability)) {

View file

@ -124,7 +124,7 @@ class PastInFlamesEffect extends ContinuousEffectImpl {
if (ability != null) {
ability.setSourceId(cardId);
ability.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(cardId, ability);
game.getState().addOtherAbility(card, ability);
}
}
}

View file

@ -121,7 +121,7 @@ class SnapcasterMageEffect extends ContinuousEffectImpl {
}
ability.setSourceId(card.getId());
ability.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(card.getId(), ability);
game.getState().addOtherAbility(card, ability);
return true;
}
return false;

View file

@ -101,7 +101,7 @@ class ChiefEngineerEffect extends ReplacementEffectImpl {
if (object != null) {
Card card = (Card) object;
Ability ability = new ConvokeAbility();
card.addAbility(ability);
game.getState().addOtherAbility(card, ability);
ability.setControllerId(source.getControllerId());
ability.setSourceId(card.getId());
game.getState().addAbility(ability, source.getSourceId(), card);

View file

@ -98,7 +98,7 @@ class GruesomeEncoreEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Card card = game.getCard(source.getFirstTarget());
if (card != null) {
card.addAbility(HasteAbility.getInstance());
game.getState().addOtherAbility(card, HasteAbility.getInstance());
card.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId());
ExileTargetEffect exileEffect = new ExileTargetEffect();

View file

@ -110,7 +110,7 @@ class PostmortemLungeEffect extends OneShotEffect {
if (player == null) {
return false;
}
card.addAbility(HasteAbility.getInstance());
game.getState().addOtherAbility(card, HasteAbility.getInstance());
card.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId());
ExileTargetEffect exileEffect = new ExileTargetEffect();

View file

@ -107,7 +107,7 @@ class RecoupEffect extends ContinuousEffectImpl {
FlashbackAbility ability = new FlashbackAbility(card.getManaCost(), TimingRule.SORCERY);
ability.setSourceId(card.getId());
ability.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(card.getId(), ability);
game.getState().addOtherAbility(card, ability);
return true;
}
}

View file

@ -136,34 +136,20 @@ class PrimalPlasmaReplacementEffect extends ReplacementEffectImpl {
}
}
}
MageObject mageObject;
if (permanent instanceof PermanentCard) {
mageObject = ((PermanentCard) permanent).getCard();
} else {
mageObject = ((PermanentToken) permanent).getToken();
}
switch (choice.getChoice()) {
case choice33:
mageObject.getPower().setValue(3);
mageObject.getToughness().setValue(3);
permanent.getPower().setValue(3);
permanent.getToughness().setValue(3);
break;
case choice22:
mageObject.getPower().setValue(2);
mageObject.getToughness().setValue(2);
if (mageObject instanceof Card) {
((Card)mageObject).addAbility(FlyingAbility.getInstance());
} else {
((Token)mageObject).addAbility(FlyingAbility.getInstance());
}
permanent.getPower().setValue(2);
permanent.getToughness().setValue(2);
permanent.addAbility(FlyingAbility.getInstance(), source.getId(), game);
break;
case choice16:
mageObject.getPower().setValue(1);
mageObject.getToughness().setValue(6);
if (mageObject instanceof Card) {
((Card)mageObject).addAbility(DefenderAbility.getInstance());
} else {
((Token)mageObject).addAbility(DefenderAbility.getInstance());
}
permanent.getPower().setValue(1);
permanent.getToughness().setValue(6);
permanent.addAbility(DefenderAbility.getInstance(), source.getId(), game);
break;
}
}

View file

@ -63,7 +63,7 @@ public class BeastbreakerOfBalaGed extends LevelerCard {
Abilities<Ability> levelAbilities = new AbilitiesImpl<Ability>();
levelAbilities.add(TrampleAbility.getInstance());
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
// LEVEL 1-3
// 4/4
new LevelerCardBuilder.LevelAbility(1, 3, new AbilitiesImpl<Ability>(), 4, 4),
@ -71,7 +71,8 @@ public class BeastbreakerOfBalaGed extends LevelerCard {
// 4/4
// Trample
new LevelerCardBuilder.LevelAbility(4, -1, levelAbilities, 6, 6)
);
));
setMaxLevelCounters(4);
}
public BeastbreakerOfBalaGed(final BeastbreakerOfBalaGed card) {

View file

@ -72,10 +72,11 @@ public class BrimstoneMage extends LevelerCard {
ability.addTarget(new TargetCreatureOrPlayer());
abilities2.add(ability);
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 2, abilities1, 2, 3),
new LevelerCardBuilder.LevelAbility(3, -1, abilities2, 2, 4)
);
));
setMaxLevelCounters(3);
}
public BrimstoneMage (final BrimstoneMage card) {

View file

@ -59,10 +59,10 @@ public class CaravanEscort extends LevelerCard {
this.addAbility(new LevelUpAbility(new ManaCostsImpl("{2}")));
AbilitiesImpl<Ability> levelAbilities = new AbilitiesImpl<Ability>(FirstStrikeAbility.getInstance());
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 4, new AbilitiesImpl<Ability>(), 2, 2),
new LevelerCardBuilder.LevelAbility(5, -1, levelAbilities, 5, 5)
);
));
setMaxLevelCounters(5);
}

View file

@ -68,7 +68,7 @@ public class CastThroughTime extends CardImpl {
// Instant and sorcery spells you control have rebound.
// (Exile the spell as it resolves if you cast it from your hand. At the beginning of your next upkeep, you may cast that card from exile without paying its mana cost.)
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainReboundEffect()), new LeavesBattlefieldWatcher());
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainReboundEffect()));
}
public CastThroughTime(final CastThroughTime card) {
@ -131,52 +131,52 @@ class GainReboundEffect extends ContinuousEffectImpl {
}
}
if (!found) {
Ability ability = new AttachedReboundAbility();
card.addAbility(ability);
Ability ability = new ReboundAbility();
// card.addAbility(ability);
ability.setControllerId(source.getControllerId());
ability.setSourceId(card.getId());
game.getState().addAbility(ability, card);
game.getState().addOtherAbility(card, ability);
}
}
}
}
class AttachedReboundAbility extends ReboundAbility {}
//class AttachedReboundAbility extends ReboundAbility {}
class LeavesBattlefieldWatcher extends Watcher {
public LeavesBattlefieldWatcher() {
super("LeavesBattlefieldWatcher", WatcherScope.CARD);
}
public LeavesBattlefieldWatcher(final LeavesBattlefieldWatcher watcher) {
super(watcher);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(this.getSourceId())) {
ZoneChangeEvent zEvent = (ZoneChangeEvent)event;
if (zEvent.getFromZone() == Zone.BATTLEFIELD) {
Player player = game.getPlayer(this.getControllerId());
if (player != null) {
for (Card card : player.getHand().getCards(CastThroughTime.filter, game)) {
Iterator<Ability> it = card.getAbilities().iterator();
while (it.hasNext()) {
if (it.next() instanceof AttachedReboundAbility) {
it.remove();
}
}
}
}
}
}
}
@Override
public LeavesBattlefieldWatcher copy() {
return new LeavesBattlefieldWatcher(this);
}
}
//class LeavesBattlefieldWatcher extends Watcher {
//
// public LeavesBattlefieldWatcher() {
// super("LeavesBattlefieldWatcher", WatcherScope.CARD);
// }
//
// public LeavesBattlefieldWatcher(final LeavesBattlefieldWatcher watcher) {
// super(watcher);
// }
//
// @Override
// public void watch(GameEvent event, Game game) {
// if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(this.getSourceId())) {
// ZoneChangeEvent zEvent = (ZoneChangeEvent)event;
// if (zEvent.getFromZone() == Zone.BATTLEFIELD) {
// Player player = game.getPlayer(this.getControllerId());
// if (player != null) {
// for (Card card : player.getHand().getCards(CastThroughTime.filter, game)) {
// Iterator<Ability> it = card.getAbilities().iterator();
// while (it.hasNext()) {
// if (it.next() instanceof AttachedReboundAbility) {
// it.remove();
// }
// }
// }
// }
// }
// }
// }
//
// @Override
// public LeavesBattlefieldWatcher copy() {
// return new LeavesBattlefieldWatcher(this);
// }
//
//}

View file

@ -78,10 +78,11 @@ public class CoralhelmCommander extends LevelerCard {
abilities2.add(FlyingAbility.getInstance());
abilities2.add(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, true)));
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(2, 3, abilities1, 3, 3),
new LevelerCardBuilder.LevelAbility(4, -1, abilities2, 4, 4)
);
));
setMaxLevelCounters(4);
}
public CoralhelmCommander(final CoralhelmCommander card) {

View file

@ -44,6 +44,7 @@ import mage.abilities.effects.common.CopyTargetSpellEffect;
import mage.abilities.keyword.LevelUpAbility;
import mage.abilities.keyword.LevelerCardBuilder;
import mage.cards.CardImpl;
import mage.cards.LevelerCard;
import mage.filter.FilterSpell;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
@ -55,7 +56,7 @@ import mage.target.TargetSpell;
*
* @author North
*/
public class EchoMage extends CardImpl {
public class EchoMage extends LevelerCard {
private static final FilterSpell filter = new FilterSpell("instant or sorcery spell");
@ -94,9 +95,10 @@ public class EchoMage extends CardImpl {
ability.addCost(new TapSourceCost());
abilities2.add(ability);
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(2, 3, abilities1, 2, 4),
new LevelerCardBuilder.LevelAbility(4, -1, abilities2, 2, 5));
new LevelerCardBuilder.LevelAbility(4, -1, abilities2, 2, 5)));
setMaxLevelCounters(4);
}
public EchoMage(final EchoMage card) {

View file

@ -69,10 +69,11 @@ public class EnclaveCryptologist extends LevelerCard {
ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new TapSourceCost());
Abilities<Ability> abilities2 = new AbilitiesImpl<Ability>(ability);
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 2, abilities1, 0, 1),
new LevelerCardBuilder.LevelAbility(3, -1, abilities2, 0, 1)
);
));
setMaxLevelCounters(3);
}
public EnclaveCryptologist (final EnclaveCryptologist card) {

View file

@ -76,10 +76,11 @@ public class GuulDrazAssassin extends LevelerCard {
ability2.addCost(new TapSourceCost());
abilities2.add(ability2);
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(2, 3, abilities1, 2, 2),
new LevelerCardBuilder.LevelAbility(4, -1, abilities2, 4, 4)
);
));
setMaxLevelCounters(4);
}
public GuulDrazAssassin (final GuulDrazAssassin card) {

View file

@ -74,10 +74,11 @@ public class HadaSpyPatrol extends LevelerCard {
abilities2.add(ShroudAbility.getInstance());
abilities2.add(new CantBeBlockedSourceAbility());
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 2, abilities1, 2, 2),
new LevelerCardBuilder.LevelAbility(3, -1, abilities2, 3, 3)
);
));
setMaxLevelCounters(3);
}
public HadaSpyPatrol(final HadaSpyPatrol card) {

View file

@ -62,10 +62,11 @@ public class HalimarWavewatch extends LevelerCard {
Abilities<Ability> levelAbilities = new AbilitiesImpl<Ability>();
levelAbilities.add(new IslandwalkAbility());
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 4, new AbilitiesImpl<Ability>(), 0, 6),
new LevelerCardBuilder.LevelAbility(5, -1, levelAbilities, 6, 6)
);
));
setMaxLevelCounters(5);
}
public HalimarWavewatch(final HalimarWavewatch card) {

View file

@ -42,6 +42,7 @@ import mage.abilities.effects.PreventionEffectImpl;
import mage.abilities.keyword.LevelUpAbility;
import mage.abilities.keyword.LevelerCardBuilder;
import mage.cards.CardImpl;
import mage.cards.LevelerCard;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
@ -50,7 +51,7 @@ import mage.game.permanent.Permanent;
*
* @author North
*/
public class HedronFieldPurists extends CardImpl {
public class HedronFieldPurists extends LevelerCard {
public HedronFieldPurists(UUID ownerId) {
super(ownerId, 25, "Hedron-Field Purists", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{W}");
@ -75,9 +76,10 @@ public class HedronFieldPurists extends CardImpl {
Abilities<Ability> abilities2 = new AbilitiesImpl<>();
abilities2.add(new SimpleStaticAbility(Zone.BATTLEFIELD, new HedronFieldPuristsEffect(2)));
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 4, abilities1, 1, 4),
new LevelerCardBuilder.LevelAbility(5, -1, abilities2, 2, 5));
new LevelerCardBuilder.LevelAbility(5, -1, abilities2, 2, 5)));
setMaxLevelCounters(5);
}
public HedronFieldPurists(final HedronFieldPurists card) {

View file

@ -65,10 +65,11 @@ public class IkiralOutrider extends LevelerCard {
Abilities<Ability> abilities2 = new AbilitiesImpl<Ability>();
abilities2.add(VigilanceAbility.getInstance());
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 3, abilities1, 2, 6),
new LevelerCardBuilder.LevelAbility(4, -1, abilities2, 3, 10)
);
));
setMaxLevelCounters(4);
}
public IkiralOutrider (final IkiralOutrider card) {

View file

@ -92,10 +92,11 @@ public class JoragaTreespeaker extends LevelerCard {
new TapSourceCost()),
Duration.WhileOnBattlefield, filter)));
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 4, abilities1, 1, 2),
new LevelerCardBuilder.LevelAbility(5, -1, abilities2, 1, 4)
);
));
setMaxLevelCounters(5);
}
public JoragaTreespeaker(final JoragaTreespeaker card) {

View file

@ -69,10 +69,11 @@ public class KabiraVindicator extends LevelerCard {
Abilities<Ability> abilities2 = new AbilitiesImpl<Ability>();
abilities2.add(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(2, 2, Duration.WhileOnBattlefield, new FilterCreaturePermanent(), true)));
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(2, 4, abilities1, 3, 6),
new LevelerCardBuilder.LevelAbility(5, -1, abilities2, 4, 8)
);
));
setMaxLevelCounters(5);
}
public KabiraVindicator(final KabiraVindicator card) {

View file

@ -72,10 +72,11 @@ public class KarganDragonlord extends LevelerCard {
abilities2.add(TrampleAbility.getInstance());
abilities2.add(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}")));
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(4, 7, abilities1, 4, 4),
new LevelerCardBuilder.LevelAbility(8, -1, abilities2, 8, 8)
);
));
setMaxLevelCounters(8);
}
public KarganDragonlord(final KarganDragonlord card) {

View file

@ -72,10 +72,11 @@ public class KazanduTuskcaller extends LevelerCard {
new CreateTokenEffect(new ElephantToken(), 2),
new TapSourceCost()));
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(2, 5, abilities1, 1, 1),
new LevelerCardBuilder.LevelAbility(6, -1, abilities2, 1, 1)
);
));
setMaxLevelCounters(6);
}
public KazanduTuskcaller(final KazanduTuskcaller card) {

View file

@ -67,10 +67,11 @@ public class KnightOfCliffhaven extends LevelerCard {
abilities2.add(FlyingAbility.getInstance());
abilities2.add(VigilanceAbility.getInstance());
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 3, abilities1, 2, 3),
new LevelerCardBuilder.LevelAbility(4, -1, abilities2, 4, 4)
);
));
setMaxLevelCounters(4);
}
public KnightOfCliffhaven(final KnightOfCliffhaven card) {

View file

@ -75,10 +75,11 @@ public class LighthouseChronologist extends LevelerCard {
Abilities<Ability> abilities2 = new AbilitiesImpl<>();
abilities2.add(new LighthouseChronologistAbility());
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(4, 6, abilities1, 2, 4),
new LevelerCardBuilder.LevelAbility(7, -1, abilities2, 3, 5)
);
));
setMaxLevelCounters(7);
}
public LighthouseChronologist (final LighthouseChronologist card) {

View file

@ -42,6 +42,7 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.LevelUpAbility;
import mage.abilities.keyword.LevelerCardBuilder;
import mage.cards.CardImpl;
import mage.cards.LevelerCard;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
@ -51,7 +52,7 @@ import mage.game.permanent.Permanent;
*
* @author North
*/
public class LordOfShatterskullPass extends CardImpl {
public class LordOfShatterskullPass extends LevelerCard {
public LordOfShatterskullPass(UUID ownerId) {
super(ownerId, 156, "Lord of Shatterskull Pass", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{R}");
@ -74,9 +75,10 @@ public class LordOfShatterskullPass extends CardImpl {
Abilities<Ability> abilities2 = new AbilitiesImpl<Ability>();
abilities2.add(new AttacksTriggeredAbility(new LordOfShatterskullPassEffect(), false));
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 5, abilities1, 6, 6),
new LevelerCardBuilder.LevelAbility(6, -1, abilities2, 6, 6));
new LevelerCardBuilder.LevelAbility(6, -1, abilities2, 6, 6)));
setMaxLevelCounters(6);
}
public LordOfShatterskullPass(final LordOfShatterskullPass card) {

View file

@ -66,10 +66,11 @@ public class NirkanaCutthroat extends LevelerCard {
abilities2.add(FirstStrikeAbility.getInstance());
abilities2.add(DeathtouchAbility.getInstance());
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 2, abilities1, 4, 3),
new LevelerCardBuilder.LevelAbility(3, -1, abilities2, 5, 4)
);
));
setMaxLevelCounters(3);
}
public NirkanaCutthroat (final NirkanaCutthroat card) {

View file

@ -65,10 +65,11 @@ public class NullChampion extends LevelerCard {
Abilities<Ability> abilities2 = new AbilitiesImpl<Ability>();
abilities2.add(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{B}")));
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 3, abilities1, 4, 2),
new LevelerCardBuilder.LevelAbility(4, -1, abilities2, 7, 3)
);
));
setMaxLevelCounters(4);
}
public NullChampion (final NullChampion card) {

View file

@ -65,10 +65,11 @@ public class SkywatcherAdept extends LevelerCard {
Abilities<Ability> abilities2 = new AbilitiesImpl<Ability>();
abilities2.add(FlyingAbility.getInstance());
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 2, abilities1, 2, 2),
new LevelerCardBuilder.LevelAbility(3, -1, abilities2, 4, 2)
);
));
setMaxLevelCounters(3);
}
public SkywatcherAdept(final SkywatcherAdept card) {

View file

@ -65,10 +65,11 @@ public class StudentOfWarfare extends LevelerCard {
Abilities<Ability> abilities2 = new AbilitiesImpl<Ability>();
abilities2.add(DoubleStrikeAbility.getInstance());
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(2, 6, abilities1, 3, 3),
new LevelerCardBuilder.LevelAbility(7, -1, abilities2, 4, 4)
);
));
setMaxLevelCounters(7);
}
public StudentOfWarfare (final StudentOfWarfare card) {

View file

@ -70,10 +70,11 @@ public class TranscendentMaster extends LevelerCard {
LifelinkAbility.getInstance(),
IndestructibleAbility.getInstance());
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(6, 11, abilities1, 6, 6),
new LevelerCardBuilder.LevelAbility(12, -1, abilities2, 9, 9)
);
));
setMaxLevelCounters(12);
}
public TranscendentMaster(final TranscendentMaster card) {

View file

@ -70,10 +70,11 @@ public class ZulaportEnforcer extends LevelerCard {
Abilities<Ability> levelAbilities = new AbilitiesImpl<Ability>();
levelAbilities.add(ZulaportEnforcerAbility.getInstance());
LevelerCardBuilder.construct(this,
this.addAbilities(LevelerCardBuilder.construct(
new LevelerCardBuilder.LevelAbility(1, 2, new AbilitiesImpl<Ability>(), 3, 3),
new LevelerCardBuilder.LevelAbility(3, -1, levelAbilities, 5, 5)
);
));
setMaxLevelCounters(3);
}
public ZulaportEnforcer(final ZulaportEnforcer card) {

View file

@ -93,7 +93,7 @@ class SedrisTheTraitorKingEffect extends ContinuousEffectImpl {
UnearthAbility ability = new UnearthAbility(new ManaCostsImpl("{2}{B}"));
ability.setSourceId(cardId);
ability.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(cardId, ability);
game.getState().addOtherAbility(card, ability);
}
}
return true;

View file

@ -94,7 +94,7 @@ class CrucibleOfWorldsEffect extends ContinuousEffectImpl {
PlayLandFromGraveyardAbility ability = new PlayLandFromGraveyardAbility(card.getName());
ability.setSourceId(cardId);
ability.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(cardId, ability);
game.getState().addOtherAbility(card, ability);
}
}
return true;

View file

@ -116,7 +116,6 @@ class WhipOfErebosEffect extends OneShotEffect {
Card card = game.getCard(this.getTargetPointer().getFirst(game, source));
Player controller = game.getPlayer(source.getControllerId());
if (controller != null && card != null) {
card.addAbility(HasteAbility.getInstance());
if (controller.putOntoBattlefieldWithInfo(card, game, Zone.GRAVEYARD, source.getSourceId())) {
// gains haste
ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom);

View file

@ -162,7 +162,7 @@ class DralnuLichLordFlashbackEffect extends ContinuousEffectImpl {
}
ability.setSourceId(card.getId());
ability.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(card.getId(), ability);
game.getState().addOtherAbility(card, ability);
return true;
}
return false;

View file

@ -110,20 +110,20 @@ class TeferiMageOfZhalfirAddFlashEffect extends ContinuousEffectImpl {
for (UUID cardId: controller.getGraveyard()) {
Card card = game.getCard(cardId);
if (card.getCardType().contains(CardType.CREATURE)) {
game.getState().addOtherAbility(cardId, FlashAbility.getInstance());
game.getState().addOtherAbility(card, FlashAbility.getInstance());
}
}
// on Hand
for (UUID cardId: controller.getHand()) {
Card card = game.getCard(cardId);
if (card.getCardType().contains(CardType.CREATURE)) {
game.getState().addOtherAbility(cardId, FlashAbility.getInstance());
game.getState().addOtherAbility(card, FlashAbility.getInstance());
}
}
// in Exile
for (Card card: game.getState().getExile().getAllCards(game)) {
if (card.getOwnerId().equals(controller.getId()) && card.getCardType().contains(CardType.CREATURE)) {
game.getState().addOtherAbility(card.getId(), FlashAbility.getInstance());
game.getState().addOtherAbility(card, FlashAbility.getInstance());
}
}
// in Library seems not relevant yet

View file

@ -80,7 +80,8 @@ public class Necromancy extends CardImpl {
// You may cast Necromancy as though it had flash. If you cast it any time a sorcery couldn't have been cast, the controller of the permanent it becomes sacrifices it at the beginning of the next cleanup step.
this.addAbility(new SimpleStaticAbility(Zone.ALL, new CastSourceAsThoughItHadFlashEffect(this, Duration.EndOfGame, true)));
this.addAbility(new CastAtInstantTimeTriggeredAbility());
// When Necromancy enters the battlefield, if it's on the battlefield, it becomes an Aura with "enchant creature put onto the battlefield with Necromancy."
// Put target creature card from a graveyard onto the battlefield under your control and attach Necromancy to it.
// When Necromancy leaves the battlefield, that creature's controller sacrifices it.
@ -111,9 +112,6 @@ class CastSourceAsThoughItHadFlashEffect extends AsThoughEffectImpl {
public CastSourceAsThoughItHadFlashEffect(Card card, Duration duration, boolean sacrificeIfCastAsInstant) {
super(AsThoughEffectType.CAST_AS_INSTANT, duration, Outcome.Benefit);
this.sacrificeIfCastAsInstant = sacrificeIfCastAsInstant;
if (sacrificeIfCastAsInstant) {
card.addAbility(new CastAtInstantTimeTriggeredAbility());
}
staticText = "You may cast {this} as though it had flash";
}

View file

@ -0,0 +1,103 @@
/*
* 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.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class ChampionTest extends CardTestPlayerBase {
/**
* 702.71. Champion
* 702.71a Champion represents two triggered abilities. Champion an [object] means When this
* permanent enters the battlefield, sacrifice it unless you exile another [object] you control and
* When this permanent leaves the battlefield, return the exiled card to the battlefield under its
* owners control.
*
* 702.71b The two abilities represented by champion are linked. See rule 607, Linked Abilities.
*
* 702.71c A permanent is championed by another permanent if the latter exiles the former as the
* direct result of a champion ability.
*
*/
/**
* Lightning Crafter
* Creature Goblin Shaman 3/3, 3R (4)
* Champion a Goblin or Shaman (When this enters the battlefield, sacrifice
* it unless you exile another Goblin or Shaman you control. When this
* leaves the battlefield, that card returns to the battlefield.)
* {T}: Lightning Crafter deals 3 damage to target creature or player.
*
*/
@Test
public void testChampionCreature() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
addCard(Zone.BATTLEFIELD, playerA, "Goblin Roughrider");
addCard(Zone.HAND, playerA, "Lightning Crafter");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Crafter");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Lightning Crafter", 1);
assertExileCount("Goblin Roughrider", 1);
}
@Test
public void testExiledCreatureReturns() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
addCard(Zone.BATTLEFIELD, playerA, "Goblin Roughrider");
addCard(Zone.HAND, playerA, "Lightning Crafter");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Crafter");
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals 3 damage to target creature or player.", "Lightning Crafter");
setStopAt(3, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Lightning Crafter", 0);
assertPermanentCount(playerA, "Goblin Roughrider", 1);
assertExileCount("Goblin Roughrider", 0);
assertGraveyardCount(playerA, "Lightning Crafter", 1);
}
}

View file

@ -0,0 +1,110 @@
/*
* 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.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class ConspireTest extends CardTestPlayerBase {
/**
* 702.77. Conspire
* 702.77a Conspire is a keyword that represents two abilities. The first is a static ability that functions
* while the spell with conspire is on the stack. The second is a triggered ability that functions
* while the spell with conspire is on the stack. Conspire means As an additional cost to cast
* this spell, you may tap two untapped creatures you control that each share a color with it and
* When you cast this spell, if its conspire cost was paid, copy it. If the spell has any targets, you
* may choose new targets for the copy. Paying a spells conspire cost follows the rules for
* paying additional costs in rules 601.2b and 601.2eg.
*
* 702.77b If a spell has multiple instances of conspire, each is paid separately and triggers based on
* its own payment, not any other instance of conspire
*
*/
/**
* Burn Trail
* Sorcery, 3R (4)
* Burn Trail deals 3 damage to target creature or player.
*
* Conspire (As you cast this spell, you may tap two untapped creatures you
* control that share a color with it. When you do, copy it and you may
* choose a new target for the copy.)
*
*/
@Test
public void testConspire() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
addCard(Zone.BATTLEFIELD, playerA, "Goblin Roughrider");
addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin");
addCard(Zone.HAND, playerA, "Burn Trail");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Burn Trail", playerB);
setChoice(playerA, "Yes");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerB, 14);
assertGraveyardCount(playerA, "Burn Trail", 1);
assertTapped("Goblin Roughrider", true);
assertTapped("Raging Goblin", true);
}
@Test
public void testConspireNotUsed() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
addCard(Zone.BATTLEFIELD, playerA, "Goblin Roughrider");
addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin");
addCard(Zone.HAND, playerA, "Burn Trail");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Burn Trail", playerB);
setChoice(playerA, "No");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerB, 17);
assertGraveyardCount(playerA, "Burn Trail", 1);
assertTapped("Goblin Roughrider", false);
assertTapped("Raging Goblin", false);
}
}

View file

@ -0,0 +1,99 @@
/*
* 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.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class DashTest extends CardTestPlayerBase {
/**
* 702.108. Dash
* 702.108a Dash represents three abilities: two static abilities that function while the card with dash is
* on the stack, one of which may create a delayed triggered ability, and a static ability that
* functions while the object with dash is on the battlefield. Dash [cost] means You may cast
* this card by paying [cost] rather that its mana cost, If this spells dash cost was paid, return the
* permanent this spell becomes to its owners hand at the beginning of the next end step, and As
* long as this permanents dash cost was paid, it has haste. Paying a cards dash cost follows the
* rules for paying alternative costs in rules 601.2b and 601.2eg.
*
*/
/**
* Screamreach Brawler
* Creature Orc Berserker 2/3, 2R (3)
* Dash {1}{R} (You may cast this spell for its dash cost. If you do, it
* gains haste, and it's returned from the battlefield to its owner's hand
* at the beginning of the next end step.)
*
*/
@Test
public void testDash() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.HAND, playerA, "Screamreach Brawler");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Screamreach Brawler");
setChoice(playerA, "Yes");
attack(1, playerA, "Screamreach Brawler");
setStopAt(2, PhaseStep.UNTAP);
execute();
assertLife(playerB, 18);
assertPermanentCount(playerA, "Screamreach Brawler", 0);
assertHandCount(playerA, "Screamreach Brawler", 1);
}
@Test
public void testNoDash() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
addCard(Zone.HAND, playerA, "Screamreach Brawler");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Screamreach Brawler");
setChoice(playerA, "No");
attack(1, playerA, "Screamreach Brawler");
setStopAt(2, PhaseStep.UNTAP);
execute();
assertLife(playerB, 20);
assertPermanentCount(playerA, "Screamreach Brawler", 1);
assertHandCount(playerA, "Screamreach Brawler", 0);
}
}

View file

@ -0,0 +1,105 @@
/*
* 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.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class FadingTest extends CardTestPlayerBase {
/**
* 702.31. Fading
* 702.31a Fading is a keyword that represents two abilities. Fading N means This permanent
* enters the battlefield with N fade counters on it and At the beginning of your upkeep, remove
* a fade counter from this permanent. If you cant, sacrifice the permanent.
*/
/**
* Blastoderm
* Creature Beast 5/5, 2GG (4)
* Shroud (This creature can't be the target of spells or abilities.)
* Fading 3 (This creature enters the battlefield with three fade counters
* on it. At the beginning of your upkeep, remove a fade counter from it.
* If you can't, sacrifice it.)
*
*/
@Test
public void testFading() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
addCard(Zone.HAND, playerA, "Blastoderm");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blastoderm");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Blastoderm", 1);
this.assertCounterCount("Blastoderm", CounterType.FADE, 3);
}
@Test
public void testFades() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
addCard(Zone.HAND, playerA, "Blastoderm");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blastoderm");
setStopAt(5, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Blastoderm", 1);
this.assertCounterCount("Blastoderm", CounterType.FADE, 1);
}
@Test
public void testFadesAway() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
addCard(Zone.HAND, playerA, "Blastoderm");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blastoderm");
setStopAt(9, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Blastoderm", 0);
assertGraveyardCount(playerA, "Blastoderm", 1);
}
}

View file

@ -0,0 +1,133 @@
/*
* 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.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class GraftTest extends CardTestPlayerBase {
/**
* 702.57. Graft
* 702.57a Graft represents both a static ability and a triggered ability. Graft N means This
* permanent enters the battlefield with N +1/+1 counters on it and Whenever another creature
* enters the battlefield, if this permanent has a +1/+1 counter on it, you may move a +1/+1
* counter from this permanent onto that creature.
* 702.57b If a creature has multiple instances of graft, each one works separately.
*
*/
/**
* Sporeback Troll
* Creature Troll Mutant 0/0, 3G (4)
* Graft 2 (This creature enters the battlefield with two +1/+1 counters on it.
* Whenever another creature enters the battlefield, you may move a +1/+1
* counter from this creature onto it.)
* {1}{G}: Regenerate target creature with a +1/+1 counter on it.
*
*/
/**
* Cytoplast Root-Kin
* Creature Elemental Mutant 0/0, 2GG (4)
* Graft 4 (This creature enters the battlefield with four +1/+1 counters on
* it. Whenever another creature enters the battlefield, you may move a +1/+1
* counter from this creature onto it.)
* When Cytoplast Root-Kin enters the battlefield, put a +1/+1 counter on
* each other creature you control with a +1/+1 counter on it.
* {2}: Move a +1/+1 counter from target creature you control onto Cytoplast Root-Kin.
*
*/
@Test
public void testGraft() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
addCard(Zone.HAND, playerA, "Sporeback Troll");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sporeback Troll");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Sporeback Troll", 1);
assertPowerToughness(playerA, "Sporeback Troll", 2, 2);
assertCounterCount("Sporeback Troll", CounterType.P1P1, 2);
}
@Test
public void testMoveCounters() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 8);
addCard(Zone.HAND, playerA, "Cytoplast Root-Kin");
addCard(Zone.HAND, playerA, "Sporeback Troll");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cytoplast Root-Kin");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sporeback Troll");
setChoice(playerA, "Yes");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Sporeback Troll", 1);
assertPermanentCount(playerA, "Cytoplast Root-Kin", 1);
assertPowerToughness(playerA, "Sporeback Troll", 3, 3);
assertPowerToughness(playerA, "Cytoplast Root-Kin", 3, 3);
assertCounterCount("Sporeback Troll", CounterType.P1P1, 3);
assertCounterCount("Cytoplast Root-Kin", CounterType.P1P1, 3);
}
@Test
public void testDontMoveCounters() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 8);
addCard(Zone.HAND, playerA, "Cytoplast Root-Kin");
addCard(Zone.HAND, playerA, "Sporeback Troll");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cytoplast Root-Kin");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sporeback Troll");
setChoice(playerA, "No");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Sporeback Troll", 1);
assertPermanentCount(playerA, "Cytoplast Root-Kin", 1);
assertPowerToughness(playerA, "Sporeback Troll", 2, 2);
assertPowerToughness(playerA, "Cytoplast Root-Kin", 4, 4);
assertCounterCount("Sporeback Troll", CounterType.P1P1, 2);
assertCounterCount("Cytoplast Root-Kin", CounterType.P1P1, 4);
}
}

View file

@ -0,0 +1,84 @@
/*
* 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.abilities.keywords;
import mage.cards.Card;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class HideawayTest extends CardTestPlayerBase {
/**
* 702.74. Hideaway
* 702.74a Hideaway represents a static ability and a triggered ability. Hideaway means This
* permanent enters the battlefield tapped and When this permanent enters the battlefield, look at
* the top four cards of your library. Exile one of them face down and put the rest on the bottom of
* your library in any order. The exiled card gains Any player who has controlled the permanent
* that exiled this card may look at this card in the exile zone.
*
*/
/**
* Shelldock Isle
* Land
* Hideaway (This land enters the battlefield tapped. When it does, look at
* the top four cards of your library, exile one face down, then put the
* rest on the bottom of your library.)
* {T}: Add {U} to your mana pool.
* {U}, {T}: You may play the exiled card without paying its mana cost if a
* library has twenty or fewer cards in it.
*
*/
@Test
public void testHideaway() {
addCard(Zone.HAND, playerA, "Shelldock Isle");
this.playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shelldock Isle");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Shelldock Isle", 1);
assertExileCount(playerA, 1);
for (Card card :currentGame.getExile().getAllCards(currentGame)){
Assert.assertTrue("Exiled card is not face down", card.isFaceDown(currentGame));
}
}
}

View file

@ -0,0 +1,115 @@
/*
* 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.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class MadnessTest extends CardTestPlayerBase {
/**
* 702.34. Madness
* 702.34a Madness is a keyword that represents two abilities. The first is a static ability that functions
* while the card with madness is in a players hand. The second is a triggered ability that
* functions when the first ability is applied. Madness [cost] means If a player would discard
* this card, that player discards it, but may exile it instead of putting it into his or her graveyard
* and When this card is exiled this way, its owner may cast it by paying [cost] rather than paying
* its mana cost. If that player doesnt, he or she puts this card into his or her graveyard.
* 702.34b Casting a spell using its madness ability follows the rules for paying alternative costs in
* rules 601.2b and 601.2eg.
*
*/
/**
* Arrogant Wurm
* 3GG
* Creature -- Wurm
* 4/4
* Trample
* Madness {2}{G} (If you discard this card, you may cast it for its
* madness cost instead of putting it into your graveyard.)
*
*/
/**
* Raven's Crime
* B
* Sorcery
* Target player discards a card.
* Retrace (You may cast this card from your graveyard by discarding a land
* card in addition to paying its other costs.)
*
*/
@Test
public void testMadness() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
addCard(Zone.HAND, playerA, "Arrogant Wurm");
addCard(Zone.HAND, playerA, "Raven's Crime");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raven's Crime", playerA);
setChoice(playerA, "Yes");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Arrogant Wurm", 1);
assertGraveyardCount(playerA, "Raven's Crime", 1);
assertHandCount(playerA, 0);
}
@Test
public void testNoMadness() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
addCard(Zone.HAND, playerA, "Arrogant Wurm");
addCard(Zone.HAND, playerA, "Raven's Crime");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raven's Crime", playerA);
setChoice(playerA, "No");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Arrogant Wurm", 0);
assertGraveyardCount(playerA, "Raven's Crime", 1);
assertGraveyardCount(playerA, "Arrogant Wurm", 1);
assertHandCount(playerA, 0);
}
}

View file

@ -0,0 +1,111 @@
/*
* 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.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class ModularTest extends CardTestPlayerBase {
/**
* 702.42. Modular
* 702.42a Modular represents both a static ability and a triggered ability. Modular N means This
* permanent enters the battlefield with N +1/+1 counters on it and When this permanent is put
* into a graveyard from the battlefield, you may put a +1/+1 counter on target artifact creature for
* each +1/+1 counter on this permanent.
* 702.42b If a creature has multiple instances of modular, each one works separately.
*
*/
/**
* Arcbound Bruiser
* Artifact Creature Golem 0/0, 5 (5)
* Modular 3 (This enters the battlefield with three +1/+1 counters on it.
* When it dies, you may put its +1/+1 counters on target artifact creature.)
*
*/
/**
* Arcbound Hybrid
* Artifact Creature Beast 0/0, 4 (4)
* Haste
* Modular 2 (This enters the battlefield with two +1/+1 counters on it.
* When it dies, you may put its +1/+1 counters on target artifact creature.)
*
*/
@Test
public void testModularEnters() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
addCard(Zone.HAND, playerA, "Arcbound Bruiser");
addCard(Zone.HAND, playerA, "Arcbound Hybrid");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Arcbound Bruiser");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Arcbound Hybrid");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Arcbound Bruiser", 1);
assertPermanentCount(playerA, "Arcbound Hybrid", 1);
assertPowerToughness(playerA, "Arcbound Bruiser", 3, 3);
assertPowerToughness(playerA, "Arcbound Hybrid", 2, 2);
}
@Test
public void testModularLeaves() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.HAND, playerA, "Arcbound Bruiser");
addCard(Zone.HAND, playerA, "Arcbound Hybrid");
addCard(Zone.HAND, playerA, "Lightning Bolt");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Arcbound Bruiser");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Arcbound Hybrid");
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Arcbound Bruiser");
setStopAt(3, PhaseStep.BEGIN_COMBAT);
setChoice(playerA, "Yes");
execute();
assertPermanentCount(playerA, "Arcbound Bruiser", 0);
assertPermanentCount(playerA, "Arcbound Hybrid", 1);
assertGraveyardCount(playerA, "Arcbound Bruiser", 1);
assertPowerToughness(playerA, "Arcbound Hybrid", 5, 5);
}
}

View file

@ -0,0 +1,115 @@
/*
* 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.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class ReplicateTest extends CardTestPlayerBase {
/**
* 702.55. Replicate
* 702.55a Replicate is a keyword that represents two abilities. The first is a static ability that
* functions while the spell with replicate is on the stack. The second is a triggered ability that
* functions while the spell with replicate is on the stack. Replicate [cost] means As an
* additional cost to cast this spell, you may pay [cost] any number of times and When you cast
* this spell, if a replicate cost was paid for it, copy it for each time its replicate cost was paid. If
* the spell has any targets, you may choose new targets for any of the copies. Paying a spells
* replicate cost follows the rules for paying additional costs in rules 601.2b and 601.2eg.
* 702.55b If a spell has multiple instances of replicate, each is paid separately and triggers based on
* the payments made for it, not any other instance of replicate.
*
*/
/**
* Train of Thought
* Sorcery, 1U (2)
* Replicate {1}{U} (When you cast this spell, copy it for each time you paid its replicate cost.)
* Draw a card.
*
*/
@Test
public void testReplicate1Time() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
addCard(Zone.HAND, playerA, "Train of Thought");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Train of Thought");
setChoice(playerA, "Yes");
setChoice(playerA, "No");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Train of Thought", 1);
assertHandCount(playerA, 2);
}
@Test
public void testReplicate2Times() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
addCard(Zone.HAND, playerA, "Train of Thought");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Train of Thought");
setChoice(playerA, "Yes");
setChoice(playerA, "Yes");
setChoice(playerA, "No");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Train of Thought", 1);
assertHandCount(playerA, 3);
}
@Test
public void testNotReplicate() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
addCard(Zone.HAND, playerA, "Train of Thought");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Train of Thought");
setChoice(playerA, "No");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Train of Thought", 1);
assertHandCount(playerA, 1);
}
}

View file

@ -0,0 +1,70 @@
/*
* 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.abilities.other;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class ChiefEngineerTest extends CardTestPlayerBase {
/**
* Chief Engineer
* Creature Vedalken Artificer 1/3, 1U (2)
* Artifact spells you cast have convoke. (Your creatures can help cast
* those spells. Each creature you tap while casting an artifact spell pays
* for {1} or one mana of that creature's color.)
*
*/
@Ignore // at this time player.getPlayable() does not take into account convoke payments
@Test
public void testGainsConvoke() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.BATTLEFIELD, playerA, "Chief Engineer");
addCard(Zone.BATTLEFIELD, playerA, "Alpha Myr");
addCard(Zone.HAND, playerA, "Goblin Roughrider");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Goblin Roughrider");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Goblin Roughrider", 1);
assertTapped("Alpha Myr", true);
}
}

View file

@ -0,0 +1,73 @@
/*
* 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.abilities.other;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class ChorusOfTheConclaveTest extends CardTestPlayerBase {
/**
* Chorus of the Conclave
* 4GGWW
* Legendary Creature -- Dryad
* 3/8
* Forestwalk
* As an additional cost to cast creature spells, you may pay any amount of
* mana. If you do, that creature enters the battlefield with that many
* additional +1/+1 counters on it.
*
*/
@Test
public void testPlayCreature() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
addCard(Zone.BATTLEFIELD, playerA, "Chorus of the Conclave");
addCard(Zone.HAND, playerA, "Goblin Roughrider");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Goblin Roughrider");
setChoice(playerA, "Yes");
setChoice(playerA, "X=1");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Goblin Roughrider", 1);
assertCounterCount("Goblin Roughrider", CounterType.P1P1, 1);
}
}

View file

@ -0,0 +1,83 @@
/*
* 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.abilities.other;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class CrucibleOfWorldsTest extends CardTestPlayerBase {
/**
* Crucible of Worlds
* Artifact, 3 (3)
* You may play land cards from your graveyard.
*
*/
@Test
public void testPlayLand() {
addCard(Zone.BATTLEFIELD, playerA, "Crucible of Worlds");
addCard(Zone.GRAVEYARD, playerA, "Swamp");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Swamp");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Swamp", 1);
assertGraveyardCount(playerA, "Swamp", 0);
}
@Test
public void testCantPlayMoreThanOneLandPerTurn() {
addCard(Zone.BATTLEFIELD, playerA, "Crucible of Worlds");
addCard(Zone.GRAVEYARD, playerA, "Swamp");
addCard(Zone.GRAVEYARD, playerA, "Plains");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Swamp");
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Play Plains");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Swamp", 1);
assertGraveyardCount(playerA, "Swamp", 0);
assertPermanentCount(playerA, "Plains", 0);
assertGraveyardCount(playerA, "Plains", 1);
}
}

View file

@ -0,0 +1,70 @@
/*
* 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.abilities.other;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class MycosynthGolemTest extends CardTestPlayerBase {
/**
* Mycosynth Golem
* Artifact Creature Golem 4/5, 11 (11)
* Affinity for artifacts (This spell costs {1} less to cast for each
* artifact you control.)
* Artifact creature spells you cast have affinity for artifacts. (They cost
* {1} less to cast for each artifact you control.)
*
*/
@Ignore // at this time player.getPlayable() does not account for spells that gain abilities
@Test
public void testSpellsAffinity() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mycosynth Golem");
addCard(Zone.HAND, playerA, "Alpha Myr");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpha Myr");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Alpha Myr", 1);
assertHandCount(playerA, "Alpha Myr", 0);
}
}

View file

@ -0,0 +1,127 @@
/*
* 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.abilities.other;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class NecromancyTest extends CardTestPlayerBase {
/**
* Necromancy
* Enchantment, 2B (3)
* You may cast Necromancy as though it had flash. If you cast it any time a
* sorcery couldn't have been cast, the controller of the permanent it
* becomes sacrifices it at the beginning of the next cleanup step.
* When Necromancy enters the battlefield, if it's on the battlefield, it
* becomes an Aura with "enchant creature put onto the battlefield with
* Necromancy." Put target creature card from a graveyard onto the
* battlefield under your control and attach Necromancy to it. When
* Necromancy leaves the battlefield, that creature's controller sacrifices it.
*
*/
@Test
public void testNecromancy() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.HAND, playerA, "Necromancy");
addCard(Zone.GRAVEYARD, playerA, "Craw Wurm");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromancy");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Craw Wurm", 1);
assertPermanentCount(playerA, "Necromancy", 1);
assertGraveyardCount(playerA, "Craw Wurm", 0);
}
@Test
public void testNecromancyFlash() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.HAND, playerA, "Necromancy");
addCard(Zone.GRAVEYARD, playerA, "Craw Wurm");
castSpell(1, PhaseStep.UPKEEP, playerA, "Necromancy");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Craw Wurm", 1);
assertPermanentCount(playerA, "Necromancy", 1);
assertGraveyardCount(playerA, "Craw Wurm", 0);
}
@Test
public void testNecromancyFlashSacrifice() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.HAND, playerA, "Necromancy");
addCard(Zone.GRAVEYARD, playerA, "Craw Wurm");
castSpell(1, PhaseStep.UPKEEP, playerA, "Necromancy");
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Craw Wurm", 0);
assertPermanentCount(playerA, "Necromancy", 0);
assertGraveyardCount(playerA, "Craw Wurm", 1);
assertGraveyardCount(playerA, "Necromancy", 1);
}
@Test
public void testNecromancyLeaves() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.HAND, playerA, "Necromancy");
addCard(Zone.HAND, playerA, "Disenchant");
addCard(Zone.GRAVEYARD, playerA, "Craw Wurm");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromancy");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Disenchant", "Necromancy");
setStopAt(2, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Craw Wurm", 0);
assertPermanentCount(playerA, "Necromancy", 0);
assertGraveyardCount(playerA, "Necromancy", 1);
assertGraveyardCount(playerA, "Craw Wurm", 1);
}
}

View file

@ -0,0 +1,71 @@
/*
* 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.abilities.other;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class PastInFlamesTest extends CardTestPlayerBase {
/**
* Past in Flames
* Sorcery, 3R (4)
* Each instant and sorcery card in your graveyard gains flashback until end
* of turn. The flashback cost is equal to its mana cost.
* Flashback {4}{R} (You may cast this card from your graveyard for its
* flashback cost. Then exile it.)
*
*/
@Test
public void testSpellsGainFlashback() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.HAND, playerA, "Past in Flames");
addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Past in Flames");
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertExileCount("Lightning Bolt", 1);
assertGraveyardCount(playerA, "Lightning Bolt", 0);
assertLife(playerB, 17);
assertLife(playerA, 20);
}
}

View file

@ -0,0 +1,93 @@
/*
* 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.abilities.other;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class SoulfireGrandMasterTest extends CardTestPlayerBase {
/**
* Soulfire Grand Master
* Creature Human Monk 2/2, 1W (2)
* Lifelink
* Instant and sorcery spells you control have lifelink.
* {2}{U/R}{U/R}: The next time you cast an instant or sorcery spell from
* your hand this turn, put that card into your hand instead of into your
* graveyard as it resolves.
*
*/
@Ignore // at this time player.getPlayable() does not account for spells that gain abilities
@Test
public void testSpellsGainLifelink() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master");
addCard(Zone.HAND, playerA, "Lightning Bolt");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Lightning Bolt", 1);
assertHandCount(playerA, "Lightning Bolt", 0);
assertLife(playerB, 17);
assertLife(playerA, 23);
}
@Ignore // at this time player.getPlayable() does not account for spells that gain abilities
@Test
public void testSpellsReturnToHand() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master");
addCard(Zone.HAND, playerA, "Lightning Bolt");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U/R}{U/R}:");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Lightning Bolt", 0);
assertHandCount(playerA, "Lightning Bolt", 1);
assertLife(playerA, 23);
assertLife(playerB, 17);
}
}

View file

@ -0,0 +1,73 @@
/*
* 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.abilities.other;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author BetaSteward
*/
public class VarolzTheScarStripedTest extends CardTestPlayerBase {
/**
* Varolz, the Scar-Striped
* Legendary Creature Troll Warrior 2/2, 1BG (3)
* Each creature card in your graveyard has scavenge. The scavenge cost is
* equal to its mana cost. (Exile a creature card from your graveyard and
* pay its mana cost: Put a number of +1/+1 counters equal to that card's
* power on target creature. Scavenge only as a sorcery.)
* Sacrifice another creature: Regenerate Varolz, the Scar-Striped.
*
*/
@Test
public void testUseScavenge() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
addCard(Zone.BATTLEFIELD, playerA, "Varolz, the Scar-Striped");
addCard(Zone.BATTLEFIELD, playerA, "Goblin Roughrider");
addCard(Zone.GRAVEYARD, playerA, "Goblin Roughrider");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scavenge", "Goblin Roughrider");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Goblin Roughrider", 1);
assertGraveyardCount(playerA, "Goblin Roughrider", 0);
assertExileCount("Goblin Roughrider", 1);
assertCounterCount("Goblin Roughrider", CounterType.P1P1, 3);
}
}

View file

@ -361,6 +361,19 @@ public interface Ability extends Controllable, Serializable {
boolean canChooseTarget(Game game);
/**
* Gets the list of sub-abilities associated with this ability.
* @return
*/
List<Ability> getSubAbilities();
/**
* Adds a sub-ability to this ability.
*
* @param ability The {@link Ability} to add.
*/
void addSubAbility(Ability ability);
List<Watcher> getWatchers();
void addWatcher(Watcher watcher);

View file

@ -86,6 +86,7 @@ public abstract class AbilityImpl implements Ability {
private static final transient Logger logger = Logger.getLogger(AbilityImpl.class);
private static final List<Watcher> emptyWatchers = new ArrayList<>();
private static final List<Ability> emptyAbilities = new ArrayList<>();
protected UUID id;
protected UUID originalId;
@ -110,6 +111,7 @@ public abstract class AbilityImpl implements Ability {
protected boolean worksFaceDown = false;
protected MageObject sourceObject;
protected List<Watcher> watchers = null;
protected List<Ability> subAbilities = null;
public AbilityImpl(AbilityType abilityType, Zone zone) {
this.id = UUID.randomUUID();
@ -145,6 +147,12 @@ public abstract class AbilityImpl implements Ability {
watchers.add(watcher.copy());
}
}
if (ability.subAbilities != null) {
this.subAbilities = new ArrayList<>();
for (Ability subAbility: ability.subAbilities) {
subAbilities.add(subAbility.copy());
}
}
this.modes = ability.modes.copy();
this.ruleAtTheTop = ability.ruleAtTheTop;
this.ruleVisible = ability.ruleVisible;
@ -562,6 +570,11 @@ public abstract class AbilityImpl implements Ability {
watcher.setControllerId(controllerId);
}
}
if (subAbilities != null) {
for (Ability subAbility: subAbilities) {
subAbility.setControllerId(controllerId);
}
}
}
@ -579,6 +592,11 @@ public abstract class AbilityImpl implements Ability {
this.sourceId = sourceId;
}
}
if (subAbilities != null) {
for (Ability subAbility: subAbilities) {
subAbility.setSourceId(sourceId);
}
}
if (watchers != null) {
for (Watcher watcher: watchers) {
watcher.setSourceId(sourceId);
@ -660,6 +678,23 @@ public abstract class AbilityImpl implements Ability {
watchers.add(watcher);
}
@Override
public List<Ability> getSubAbilities() {
if (subAbilities != null)
return subAbilities;
else
return emptyAbilities;
}
@Override
public void addSubAbility(Ability ability) {
if (subAbilities == null)
subAbilities = new ArrayList<>();
ability.setSourceId(this.sourceId);
ability.setControllerId(this.controllerId);
subAbilities.add(ability);
}
@Override
public boolean isUsesStack() {
return usesStack;

View file

@ -107,8 +107,7 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl implements Sou
}
if (card != null) {
// add ability to card only once
card.addAbility(ability);
discard();
game.getState().addOtherAbility(card, ability);
return true;
}
} else {

View file

@ -127,7 +127,7 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl {
for (UUID cardId : targetPointer.getTargets(game, source)) {
Card card = game.getCard(cardId);
if (card != null) {
card.addAbility(ability);
game.getState().addOtherAbility(card, ability);
affectedTargets++;
}
}

View file

@ -116,7 +116,7 @@ public class BestowAbility extends SpellAbility {
this.addEffect(new AttachEffect(Outcome.BoostCreature));
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BestowTypeChangingEffect());
ability.setRuleVisible(false);
card.addAbility(ability);
addSubAbility(ability);
}
public BestowAbility(final BestowAbility ability) {

View file

@ -105,12 +105,12 @@ public class ChampionAbility extends StaticAbility {
Ability ability1 = new EntersBattlefieldTriggeredAbility(
new SacrificeSourceUnlessPaysEffect(new ChampionExileCost(filter, new StringBuilder(card.getName()).append(" championed permanents").toString())),false);
ability1.setRuleVisible(false);
card.addAbility(ability1);
addSubAbility(ability1);
// When this permanent leaves the battlefield, return the exiled card to the battlefield under its owner's control.
Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false);
ability2.setRuleVisible(false);
card.addAbility(ability2);
addSubAbility(ability2);
}
public ChampionAbility(final ChampionAbility ability) {

View file

@ -86,7 +86,7 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
public ConspireAbility(Card card) {
super(Zone.STACK, null);
setRuleAtTheTop(false);
card.addAbility(new ConspireTriggeredAbility());
addSubAbility(new ConspireTriggeredAbility());
}
public ConspireAbility(final ConspireAbility ability) {

View file

@ -79,7 +79,7 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts
Effect effect = new ReturnToHandTargetEffect();
effect.setTargetPointer(new FixedTarget(card.getId()));
ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), false));
card.addAbility(ability);
addSubAbility(ability);
}

View file

@ -72,7 +72,7 @@ public class EvokeAbility extends StaticAbility implements AlternativeSourceCost
this.addEvokeCost(manaString);
Ability ability = new ConditionalTriggeredAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceEffect()), EvokedCondition.getInstance(), "Sacrifice {this} when it enters the battlefield and was evoked.");
ability.setRuleVisible(false);
card.addAbility(ability);
addSubAbility(ability);
}

View file

@ -28,7 +28,7 @@ public class FadingAbility extends EntersBattlefieldAbility {
super(new AddCountersSourceEffect(CounterType.FADE.createInstance(fadeCounter)), "with");
Ability ability = new BeginningOfUpkeepTriggeredAbility(new FadingEffect(), TargetController.YOU, false);
ability.setRuleVisible(false);
card.addAbility(ability);
addSubAbility(ability);
StringBuilder sb = new StringBuilder("Fading ");
sb.append(fadeCounter);
sb.append(" <i>(This permanent enters the battlefield with ")

View file

@ -73,7 +73,7 @@ public class GraftAbility extends TriggeredAbilityImpl {
sb.append(theCardtype.toString().toLowerCase(Locale.ENGLISH)).append(" ");
}
this.cardtype = sb.toString().trim();
card.addAbility(new GraftStaticAbility(amount));
addSubAbility(new GraftStaticAbility(amount));
}
public GraftAbility(GraftAbility ability) {

View file

@ -67,7 +67,7 @@ public class HauntAbility extends TriggeredAbilityImpl {
public HauntAbility(Card card, Effect effect) {
super(Zone.ALL, effect , false);
card.addAbility(new HauntExileAbility());
addSubAbility(new HauntExileAbility());
}
public HauntAbility(final HauntAbility ability) {

View file

@ -68,11 +68,11 @@ public class HideawayAbility extends StaticAbility {
super(Zone.BATTLEFIELD, new EntersBattlefieldEffect(new TapSourceEffect(true)));
Ability ability = new EntersBattlefieldTriggeredAbility(new HideawayExileEffect(), false);
ability.setRuleVisible(false);
card.addAbility(ability);
addSubAbility(ability);
// Allow controller to look at face down card
ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new HideawayLookAtFaceDownCardEffect());
ability.setRuleVisible(false);
card.addAbility(ability);
addSubAbility(ability);
}
public HideawayAbility(final HideawayAbility ability) {

View file

@ -134,9 +134,11 @@ public class LevelerCardBuilder {
*
* @param card
* @param levelAbilities
* @return list of levelAbilities to add to card
*/
public static void construct(Card card, LevelAbility... levelAbilities) {
public static List<Ability> construct(LevelAbility... levelAbilities) {
LevelerCardBuilder builder = new LevelerCardBuilder();
List<Ability> abilities = new ArrayList<>();
for (LevelAbility levelAbility : levelAbilities) {
// set main params
@ -153,22 +155,11 @@ public class LevelerCardBuilder {
builder.addAbility(addedAbility);
}
// build static abilities and add them to card
for (Ability simpleStaticAbility : builder.build()) {
card.addAbility(simpleStaticAbility);
}
// build static abilities and add them to list
abilities.addAll(builder.build());
}
// set max level counters (for ai)
if (card instanceof LevelerCard) {
int maxValue = 0;
for (LevelAbility levelAbility : levelAbilities) {
if (levelAbility.getLevel1() > maxValue) {
maxValue = levelAbility.getLevel1();
}
}
((LevelerCard) card).setMaxLevelCounters(maxValue);
}
return abilities;
}
public static class LevelAbility {

View file

@ -41,7 +41,7 @@ public class MadnessAbility extends StaticAbility {
@SuppressWarnings("unchecked")
public MadnessAbility(Card card, ManaCosts madnessCost) {
super(Zone.HAND, new MadnessReplacementEffect((ManaCosts<ManaCost>)madnessCost));
card.addAbility(new MadnessTriggeredAbility((ManaCosts<ManaCost>)madnessCost));
addSubAbility(new MadnessTriggeredAbility((ManaCosts<ManaCost>)madnessCost));
rule = "Madness " + madnessCost.getText() + " <i>(If you discard this card, you may cast it for its madness cost instead of putting it into your graveyard.)<i/>";
}

View file

@ -59,9 +59,9 @@ public class ModularAbility extends DiesTriggeredAbility {
if (sunburst) {
Ability ability = new SunburstAbility(card);
ability.setRuleVisible(false);
card.addAbility(ability);
addSubAbility(ability);
} else {
card.addAbility(new ModularStaticAbility(amount));
addSubAbility(new ModularStaticAbility(amount));
}
}

View file

@ -148,7 +148,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesFaceDownCreatureEffect(morphCosts, (megamorph ? FaceDownType.MEGAMORPHED :FaceDownType.MORPHED)));
ability.setWorksFaceDown(true);
ability.setRuleVisible(false);
card.addAbility(ability);
addSubAbility(ability);
}

View file

@ -96,7 +96,7 @@ public class ReboundAbility extends TriggeredAbilityImpl {
((ZoneChangeEvent) event).getToZone() == Zone.STACK) {
Card card = (Card) game.getObject(event.getTargetId());
if (card.getAbilities().contains(this)) {
if (card.getAbilities(game).contains(this)) {
this.installReboundEffect = true;
}
}

View file

@ -66,7 +66,7 @@ public class ReplicateAbility extends StaticAbility implements OptionalAdditiona
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, reminderTextMana, new ManaCostsImpl(manaString));
this.additionalCost.setRepeatable(true);
setRuleAtTheTop(true);
card.addAbility(new ReplicateTriggeredAbility());
addSubAbility(new ReplicateTriggeredAbility());
}
public ReplicateAbility(final ReplicateAbility ability) {

View file

@ -179,8 +179,8 @@ public class SuspendAbility extends ActivatedAbilityImpl {
if (card.getManaCost().isEmpty()) {
setRuleAtTheTop(true);
}
card.addAbility(new SuspendBeginningOfUpkeepTriggeredAbility());
card.addAbility(new SuspendPlayCardAbility(card.getCardType().contains(CardType.CREATURE)));
addSubAbility(new SuspendBeginningOfUpkeepTriggeredAbility());
addSubAbility(new SuspendPlayCardAbility(card.getCardType().contains(CardType.CREATURE)));
}
ruleText = sb.toString();
}
@ -196,18 +196,18 @@ public class SuspendAbility extends ActivatedAbilityImpl {
SuspendAbility ability = new SuspendAbility(0, null, card, false);
ability.setSourceId(card.getId());
ability.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(card.getId(), ability);
game.getState().addOtherAbility(card, ability);
SuspendBeginningOfUpkeepTriggeredAbility ability1 = new SuspendBeginningOfUpkeepTriggeredAbility();
ability1.setSourceId(card.getId());
ability1.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(card.getId(), ability1);
game.getState().addOtherAbility(card, ability1);
game.getState().addAbility(ability1, source.getSourceId(), card);
SuspendPlayCardAbility ability2 = new SuspendPlayCardAbility(card.getCardType().contains(CardType.CREATURE));
ability2.setSourceId(card.getId());
ability2.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(card.getId(), ability2);
game.getState().addOtherAbility(card, ability2);
game.getState().addAbility(ability2, source.getSourceId(), card);
}

View file

@ -33,6 +33,7 @@ import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.Mana;
import mage.abilities.Abilities;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.constants.Rarity;
@ -47,7 +48,7 @@ public interface Card extends MageObject {
int getCardNumber();
Rarity getRarity();
void setOwnerId(UUID ownerId);
void addAbility(Ability ability);
public Abilities<Ability> getAbilities(Game game);
void setSpellAbility(SpellAbility ability);
SpellAbility getSpellAbility();
List<String> getRules(); // gets base card rules

View file

@ -36,10 +36,11 @@ import java.util.UUID;
import mage.MageObject;
import mage.MageObjectImpl;
import mage.Mana;
import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl;
import mage.abilities.Ability;
import mage.abilities.PlayLandAbility;
import mage.abilities.SpellAbility;
import mage.abilities.keyword.MorphAbility;
import mage.abilities.mana.ManaAbility;
import mage.constants.CardType;
import mage.constants.ColoredManaSymbol;
@ -226,11 +227,11 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
for (String data : cardState.getInfo().values()) {
rules.add(data);
}
for (Ability ability: cardState.getAbilities()) {
rules.add(ability.getRule());
}
}
}
// for (Ability ability: state.getAbilities()) {
// rules.add(ability.getRule());
// }
return rules;
} catch (Exception e) {
logger.error("Exception in rules generation for card: " + this.getName(), e);
@ -238,12 +239,48 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
return rulesError;
}
/**
* Gets all base abilities - does not include additional abilities added by
* other cards or effects
* @return A list of {@link Ability} - this collection is modifiable
*/
@Override
public void addAbility(Ability ability) {
ability.setSourceId(this.getId());
abilities.add(ability);
public Abilities<Ability> getAbilities() {
return super.getAbilities();
}
/**
* Gets all current abilities - includes additional abilities added by
* other cards or effects
* @param game
* @return A list of {@link Ability} - this collection is not modifiable
*/
@Override
public Abilities<Ability> getAbilities(Game game) {
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(objectId);
if (otherAbilities == null) {
return abilities;
}
Abilities<Ability> all = new AbilitiesImpl<>();
all.addAll(abilities);
all.addAll(otherAbilities);
return all;
}
protected void addAbility(Ability ability) {
ability.setSourceId(this.getId());
abilities.add(ability);
for (Ability subAbility: ability.getSubAbilities()) {
abilities.add(subAbility);
}
}
protected void addAbilities(List<Ability> abilities) {
for (Ability ability: abilities) {
addAbility(ability);
}
}
protected void addAbility(Ability ability, Watcher watcher) {
addAbility(ability);
ability.addWatcher(watcher);

View file

@ -53,7 +53,7 @@ public abstract class LevelerCard extends CardImpl {
return maxLevelCounters;
}
public void setMaxLevelCounters(int maxLevelCounters) {
protected void setMaxLevelCounters(int maxLevelCounters) {
this.maxLevelCounters = maxLevelCounters;
}

View file

@ -145,6 +145,19 @@ public abstract class SplitCard extends CardImpl {
return allAbilites;
}
@Override
public Abilities<Ability> getAbilities(Game game) {
Abilities<Ability> allAbilites = new AbilitiesImpl<>();
for (Ability ability : super.getAbilities(game)) {
if (ability instanceof SpellAbility && !((SpellAbility)ability).getSpellAbilityType().equals(SpellAbilityType.SPLIT)) {
allAbilites.add(ability);
}
}
allAbilites.addAll(leftHalfCard.getAbilities(game));
allAbilites.addAll(rightHalfCard.getAbilities(game));
return allAbilites;
}
@Override
public List<String> getRules() {
List<String> rules = new ArrayList<>();

View file

@ -2,6 +2,9 @@ package mage.game;
import java.util.HashMap;
import java.util.Map;
import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl;
import mage.abilities.Ability;
import mage.counters.Counters;
/**
*
@ -12,8 +15,10 @@ public class CardState {
protected boolean faceDown;
protected Map<String, String> info;
protected Counters counters;
protected Abilities<Ability> abilities;
private static final Map<String, String> emptyInfo = new HashMap<>();
private static final Abilities<Ability> emptyAbilities = new AbilitiesImpl<>();
public CardState() {
counters = new Counters();
@ -26,6 +31,12 @@ public class CardState {
info.putAll(state.info);
}
counters = state.counters.copy();
if (state.abilities != null) {
abilities = new AbilitiesImpl<>();
for (Ability ability: state.abilities) {
abilities.add(ability.copy());
}
}
}
public CardState copy() {
@ -62,10 +73,37 @@ public class CardState {
return info;
}
public Abilities<Ability> getAbilities() {
if (abilities == null) {
return emptyAbilities;
}
return abilities;
}
public void addAbility(Ability ability) {
if (abilities == null) {
abilities = new AbilitiesImpl<>();
}
abilities.add(ability);
for (Ability sub: ability.getSubAbilities()) {
abilities.add(sub);
}
}
public void clearAbilities() {
if (abilities != null) {
for (Ability ability: abilities) {
ability.setSourceId(null);
ability.setControllerId(null);
}
abilities = null;
}
}
public void clear() {
counters.clear();
info = null;
clearAbilities();
}
}

View file

@ -58,7 +58,6 @@ import mage.watchers.Watchers;
import java.io.Serializable;
import java.util.*;
import org.apache.log4j.Logger;
/**
*
@ -80,7 +79,6 @@ public class GameState implements Serializable, Copyable<GameState> {
private final Map<UUID, LookedAt> lookedAt = new HashMap<>();
private final DelayedTriggeredAbilities delayed;
private final SpecialActions specialActions;
private final Map<UUID, Abilities<Ability>> otherAbilities = new HashMap<>();
private final TurnMods turnMods;
private final Watchers watchers;
@ -156,9 +154,6 @@ public class GameState implements Serializable, Copyable<GameState> {
this.values.put(entry.getKey(), entry.getValue());
}
this.zones.putAll(state.zones);
for (Map.Entry<UUID, Abilities<Ability>> entry: state.otherAbilities.entrySet()) {
otherAbilities.put(entry.getKey(), entry.getValue().copy());
}
this.paused = state.paused;
this.simultaneousEvents.addAll(state.simultaneousEvents);
for (Map.Entry<UUID, CardState> entry: state.cardState.entrySet()) {
@ -601,6 +596,9 @@ public class GameState implements Serializable, Copyable<GameState> {
watcher.setSourceId(attachedTo.getId());
watchers.add(watcher);
}
for (Ability sub: ability.getSubAbilities()) {
addAbility(sub, sourceId, attachedTo);
}
}
public void addCommandObject(CommandObject commandObject) {
@ -678,41 +676,32 @@ public class GameState implements Serializable, Copyable<GameState> {
* @return
*/
public Abilities<ActivatedAbility> getActivatedOtherAbilities(UUID objectId, Zone zone) {
if (otherAbilities.containsKey(objectId)) {
return otherAbilities.get(objectId).getActivatedAbilities(zone);
if (cardState.containsKey(objectId)) {
return cardState.get(objectId).getAbilities().getActivatedAbilities(zone);
}
return null;
}
public Abilities<Ability> getAllOtherAbilities(UUID objectId) {
if (otherAbilities.containsKey(objectId)) {
return otherAbilities.get(objectId);
if (cardState.containsKey(objectId)) {
return cardState.get(objectId).getAbilities();
}
return null;
}
public void addOtherAbility(UUID objectId, Ability ability) {
if (!otherAbilities.containsKey(objectId)) {
otherAbilities.put(objectId, new AbilitiesImpl(ability));
} else {
otherAbilities.get(objectId).add(ability);
}
}
/**
* Adds the ability to continuous or triggered abilities
* @param ability
* @param card
*/
public void addOtherAbility(Ability ability, Card card) {
addOtherAbility(card.getId(), ability);
addAbility(ability, card.getId(), card);
}
private void resetOtherAbilities() {
otherAbilities.clear();
public void addOtherAbility(Card attachedTo, Ability ability) {
ability.setSourceId(attachedTo.getId());
ability.setControllerId(attachedTo.getOwnerId());
if (!cardState.containsKey(attachedTo.getId())) {
cardState.put(attachedTo.getId(), new CardState());
}
cardState.get(attachedTo.getId()).addAbility(ability);
addAbility(ability, attachedTo.getId(), attachedTo);
}
/**
@ -734,7 +723,9 @@ public class GameState implements Serializable, Copyable<GameState> {
triggers.removeAllGainedAbilities();
getContinuousEffects().removeAllTemporaryEffects();
this.setLegendaryRuleActive(true);
this.resetOtherAbilities();
for (CardState state: cardState.values()) {
state.clearAbilities();
}
}
public void clear() {
@ -754,7 +745,6 @@ public class GameState implements Serializable, Copyable<GameState> {
legendaryRuleActive = true;
gameOver = false;
specialActions.clear();
otherAbilities.clear();
cardState.clear();
combat.clear();
turnMods.clear();

View file

@ -125,7 +125,6 @@ public interface Permanent extends Card, Controllable {
String getValue();
@Deprecated
@Override
void addAbility(Ability ability);
@Deprecated
void addAbility(Ability ability, Game game);

View file

@ -38,6 +38,8 @@ import java.util.Map;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
@ -244,6 +246,16 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
}
@Override
public Abilities<Ability> getAbilities() {
return abilities;
}
@Override
public Abilities<Ability> getAbilities(Game game) {
return abilities;
}
@Override
@Deprecated
public void addAbility(Ability ability) {

View file

@ -601,6 +601,11 @@ public class Spell implements StackObject, Card {
return card.getAbilities();
}
@Override
public Abilities<Ability> getAbilities(Game game) {
return card.getAbilities(game);
}
@Override
public boolean hasAbility(UUID abilityId, Game game) {
return card.hasAbility(abilityId, game);
@ -674,7 +679,6 @@ public class Spell implements StackObject, Card {
spellAbilities.add(spellAbility);
}
@Override
public void addAbility(Ability ability) {}
@Override

View file

@ -487,6 +487,16 @@ public class StackAbility implements StackObject, Ability {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public List<Ability> getSubAbilities() {
return this.ability.getSubAbilities();
}
@Override
public void addSubAbility(Ability ability) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public MageObject getSourceObject(Game game) {
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.