Merge origin/master

This commit is contained in:
LevelX2 2015-04-20 10:57:39 +02:00
commit 89ce87e16e
57 changed files with 2814 additions and 1336 deletions

View file

@ -64,7 +64,7 @@ public class VortexElemental extends CardImpl {
this.toughness = new MageInt(1);
// {U}: Put Vortex Elemental and each creature blocking or blocked by it on top of their owners' libraries, then those players shuffle their libraries.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new VortexElementaöEffect(), new ManaCostsImpl("{U}")));
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new VortexElementalEffect(), new ManaCostsImpl("{U}")));
// {3}{U}{U}: Target creature blocks Vortex Elemental this turn if able.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MustBeBlockedByTargetSourceEffect(), new ManaCostsImpl("{3}{U}{U}"));
@ -82,20 +82,20 @@ public class VortexElemental extends CardImpl {
}
}
class VortexElementaöEffect extends OneShotEffect {
class VortexElementalEffect extends OneShotEffect {
public VortexElementaöEffect() {
public VortexElementalEffect() {
super(Outcome.Benefit);
this.staticText = "Put {this} and each creature blocking or blocked by it on top of their owners' libraries, then those players shuffle their libraries";
}
public VortexElementaöEffect(final VortexElementaöEffect effect) {
public VortexElementalEffect(final VortexElementalEffect effect) {
super(effect);
}
@Override
public VortexElementaöEffect copy() {
return new VortexElementaöEffect(this);
public VortexElementalEffect copy() {
return new VortexElementalEffect(this);
}
@Override
@ -103,8 +103,8 @@ class VortexElementaöEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Combat combat = game.getState().getCombat();
Set<UUID> creaturesToReturn = new HashSet<UUID>();
Set<UUID> playersToShuffle = new HashSet<UUID>();
Set<UUID> creaturesToReturn = new HashSet<>();
Set<UUID> playersToShuffle = new HashSet<>();
creaturesToReturn.add(source.getSourceId());
if (combat != null) {
for(CombatGroup combatGroup: combat.getGroups()) {

View file

@ -72,6 +72,7 @@ public class SiftThroughSands extends CardImpl {
Effect effect = new DiscardControllerEffect(1);
effect.setText(", then discard a card");
this.getSpellAbility().addEffect(effect);
// If you've cast a spell named Peer Through Depths and a spell named Reach Through Mists this turn, you may search your library for a card named The Unspeakable, put it onto the battlefield, then shuffle your library.
this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), false, true), new SiftThroughSandsCondition(), rule));
this.getSpellAbility().addWatcher(new SiftThroughSandsWatcher());

View file

@ -107,7 +107,7 @@ class CommandeerEffect extends OneShotEffect {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
if (controller != null && spell != null) {
spell.setControllerId(controller.getId());
spell.chooseNewTargets(game, controller.getId(), false, false);
spell.chooseNewTargets(game, controller.getId(), false, false, null);
return true;
}
return false;

View file

@ -142,7 +142,7 @@ class LightningStormAddCounterEffect extends OneShotEffect {
Spell spell = game.getStack().getSpell(source.getSourceId());
if (spell != null) {
spell.addCounters(CounterType.CHARGE.createInstance(2), game);
return spell.chooseNewTargets(game, source.getControllerId(), false, false);
return spell.chooseNewTargets(game, source.getControllerId(), false, false, null);
}
return false;
}

View file

@ -90,27 +90,26 @@ class CurseOfInertiaTriggeredAbility extends TriggeredAbilityImpl {
super(Zone.BATTLEFIELD, new CurseOfInertiaTapOrUntapTargetEffect(), false);
}
public CurseOfInertiaTriggeredAbility(Effect effect, boolean optional, String text) {
super(Zone.BATTLEFIELD, effect, optional);
}
public CurseOfInertiaTriggeredAbility(final CurseOfInertiaTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType().equals(EventType.DECLARED_ATTACKERS);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType().equals(EventType.DECLARED_ATTACKERS)) {
Permanent enchantment = game.getPermanent(this.getSourceId());
if (enchantment != null
&& enchantment.getAttachedTo() != null
&& game.getCombat().getPlayerDefenders(game).contains(enchantment.getAttachedTo())) {
for (Effect effect: this.getEffects()) {
effect.setTargetPointer(new FixedTarget(game.getCombat().getAttackerId()));
}
TargetPermanent target = new TargetPermanent();
target.setTargetController(game.getCombat().getAttackerId());
addTarget(target);
return true;
}
}
return false;
}
@ -138,12 +137,9 @@ class CurseOfInertiaTapOrUntapTargetEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(this.getTargetPointer().getFirst(game, source));
Player player = game.getPlayer(source.getTargets().get(0).getTargetController());
if (player != null) {
Target target = new TargetPermanent();
if (target.canChoose(source.getSourceId(), player.getId(), game)
&& player.choose(outcome, target, source.getSourceId(), game)) {
Permanent targetPermanent = game.getPermanent(target.getFirstTarget());
Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (targetPermanent != null) {
if (targetPermanent.isTapped()) {
if (player.chooseUse(Outcome.Untap, "Untap that permanent?", game)) {
@ -157,7 +153,6 @@ class CurseOfInertiaTapOrUntapTargetEffect extends OneShotEffect {
return true;
}
}
}
return false;
}

View file

@ -70,7 +70,6 @@ public class DungeonGeists extends CardImpl {
this.expansionSetCode = "DKA";
this.subtype.add("Spirit");
this.color.setBlue(true);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
@ -112,40 +111,52 @@ class DungeonGeistsEffect extends ContinuousRuleModifyingEffectImpl {
}
@Override
public boolean apply(Game game, Ability source) {
return false;
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.UNTAP || event.getType() == GameEvent.EventType.ZONE_CHANGE || event.getType() == GameEvent.EventType.LOST_CONTROL;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
// Source must be on the battlefield (it's neccessary to check here because if as response to the enter
// the battlefield triggered ability the source dies (or will be exiled), then the ZONE_CHANGE or LOST_CONTROL
// event will happen before this effect is applied ever)
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
Permanent sourcePermanent = (Permanent) source.getSourceObjectIfItStillExists(game);
if (sourcePermanent == null || !sourcePermanent.getControllerId().equals(source.getControllerId())) {
this.used = true;
return false;
}
if (event.getType() == GameEvent.EventType.LOST_CONTROL) {
if (event.getTargetId().equals(source.getSourceId())) {
discard();
return false;
}
}
if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(source.getSourceId())) {
switch(event.getType()) {
case ZONE_CHANGE:
// end effect if source does a zone move
if (event.getTargetId().equals(source.getSourceId())) {
ZoneChangeEvent zEvent = (ZoneChangeEvent)event;
if (zEvent.getFromZone() == Zone.BATTLEFIELD) {
discard();
return false;
}
}
if (game.getTurn().getStepType() == PhaseStep.UNTAP && event.getType() == GameEvent.EventType.UNTAP) {
if (event.getTargetId().equals(targetPointer.getFirst(game, source))) {
return true;
break;
case UNTAP:
// prevent to untap the target creature
if (game.getTurn().getStepType() == PhaseStep.UNTAP && event.getTargetId().equals(targetPointer.getFirst(game, source))) {
Permanent targetCreature = game.getPermanent(targetPointer.getFirst(game, source));
if (targetCreature != null) {
return targetCreature.getControllerId().equals(game.getActivePlayerId());
} else {
discard();
return false;
}
}
break;
case LOST_CONTROL:
// end effect if source control is changed
if (event.getTargetId().equals(source.getSourceId())) {
discard();
return false;
}
break;
}
return false;
}
}

View file

@ -86,13 +86,14 @@ class CloneLegionEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
if (targetPlayer != null) {
if (controller != null && targetPlayer != null) {
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), targetPlayer.getId(), game)) {
if (permanent != null) {
EmptyToken token = new EmptyToken();
CardUtil.copyTo(token).from(permanent);
token.putOntoBattlefield(1, game, source.getSourceId(), targetPlayer.getId());
token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId());
}
}
return true;

View file

@ -48,7 +48,6 @@ import mage.constants.TargetController;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import static mage.filter.predicate.permanent.ControllerControlsIslandPredicate.filter;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
@ -157,7 +156,8 @@ class IcefallRegentEffect extends ContinuousRuleModifyingEffectImpl {
if (game.getTurn().getStepType() == PhaseStep.UNTAP && event.getType() == GameEvent.EventType.UNTAP) {
if (event.getTargetId().equals(targetPointer.getFirst(game, source))) {
return true;
Permanent targetCreature = game.getPermanent(targetPointer.getFirst(game, source));
return targetCreature != null && game.getActivePlayerId().equals(targetCreature.getControllerId());
}
}

View file

@ -36,7 +36,6 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.keyword.EnchantAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.AttachmentType;
import mage.constants.CardType;
@ -106,10 +105,8 @@ class MirrorMockeryEffect extends OneShotEffect {
}
Permanent enchanted = game.getPermanent(enchantment.getAttachedTo());
if (enchanted != null) {
Card card = game.getCard(enchanted.getId());
if (card != null) {
EmptyToken token = new EmptyToken();
CardUtil.copyTo(token).from(card);
CardUtil.copyTo(token).from(enchanted);
token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId());
@ -122,7 +119,6 @@ class MirrorMockeryEffect extends OneShotEffect {
game.addDelayedTriggeredAbility(delayedAbility);
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.exodus;
import java.util.UUID;
import mage.constants.Rarity;
/**
*
* @author LevelX2
*/
public class Pandemonium extends mage.sets.timeshifted.Pandemonium {
public Pandemonium(UUID ownerId) {
super(ownerId);
this.cardNumber = 93;
this.expansionSetCode = "EXO";
this.rarity = Rarity.RARE;
}
public Pandemonium(final Pandemonium card) {
super(card);
}
@Override
public Pandemonium copy() {
return new Pandemonium(this);
}
}

View file

@ -48,14 +48,12 @@ import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.SubLayer;
import mage.constants.TargetController;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.FilterObject;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
@ -230,9 +228,12 @@ class SoulfireGrandMasterCastFromHandReplacementEffect extends ReplacementEffect
if (zEvent.getFromZone() == Zone.STACK &&
zEvent.getToZone() == Zone.GRAVEYARD &&
event.getTargetId().equals(spellId)) {
Spell spell = game.getStack().getSpell(spellId);
if (spell != null && !spell.isCountered()) {
return true;
}
}
}
return false;
}

View file

@ -42,7 +42,6 @@ import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.combat.CombatGroup;
import mage.game.events.GameEvent;
@ -71,7 +70,7 @@ import mage.watchers.common.BlockedAttackerWatcher;
public class TheWretched extends CardImpl {
public TheWretched(UUID ownerId) {
super(ownerId, 239, "The Wretched", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{B}{B}");
super(ownerId, 59, "The Wretched", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{B}{B}");
this.expansionSetCode = "5ED";
this.subtype.add("Demon");
this.power = new MageInt(2);
@ -160,19 +159,6 @@ class TheWretchedEffect extends OneShotEffect {
}
}
return true;
// BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get("BlockedAttackerWatcher");
// if (watcher != null) {
// for (Permanent creature : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), game)) {
// if (watcher.creatureHasBlockedAttacker(theWretched, creature, game)
// && !creature.isRemovedFromCombat()) {
// ContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom, source.getControllerId()), new SourceOnBattlefieldControlUnchangedCondition(), "test");
// effect.setTargetPointer(new FixedTarget(creature.getId()));
// game.addEffect(effect, source);
// }
// }
// return true;
// }
// return false;
}
@Override

View file

@ -52,8 +52,6 @@ public class MimingSlime extends CardImpl {
super(ownerId, 126, "Miming Slime", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2}{G}");
this.expansionSetCode = "GTC";
this.color.setGreen(true);
// Put an X/X green Ooze creature token onto the battlefield, where X is the greatest power among creatures you control.
this.getSpellAbility().addEffect(new MimingSlimeEffect());
}

View file

@ -54,8 +54,6 @@ public class OozeFlux extends CardImpl {
super(ownerId, 128, "Ooze Flux", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}");
this.expansionSetCode = "GTC";
this.color.setGreen(true);
// {1}{G}, Remove one or more +1/+1 counters from among creatures you control: Put an X/X green Ooze creature token onto the battlefield, where X is the number of +1/+1 counters removed this way.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new OozeFluxCreateTokenEffect(new OozeToken()),new ManaCostsImpl("{1}{G}"));
ability.addCost(new RemoveVariableCountersTargetCost(new FilterControlledCreaturePermanent("creatures you control"), CounterType.P1P1, "one or more", 1));
@ -74,7 +72,7 @@ public class OozeFlux extends CardImpl {
class OozeFluxCreateTokenEffect extends OneShotEffect {
private Token token;
private final Token token;
public OozeFluxCreateTokenEffect(Token token) {
super(Outcome.PutCreatureInPlay);

View file

@ -107,7 +107,7 @@ class QuicksilverDragonEffect extends OneShotEffect {
numTargets += target.getTargets().size();
}
if (numTargets == 1 && spell.getSpellAbility().getTargets().getFirstTarget().equals(source.getSourceId())) {
spell.chooseNewTargets(game, source.getControllerId(), true, false);
spell.chooseNewTargets(game, source.getControllerId(), true, false, null);
}
return true;
}

View file

@ -48,6 +48,7 @@ import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import mage.util.CardUtil;
@ -143,8 +144,10 @@ class WorldgorgerDragonLeavesEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() -1));
MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) {
int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() -1;
ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter));
if (exile != null) {
exile = exile.copy();
for (UUID cardId : exile) {

View file

@ -87,9 +87,13 @@ class TheAbyssTriggeredAbility extends TriggeredAbilityImpl {
return new TheAbyssTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE) {
Player player = game.getPlayer(event.getPlayerId());
if (player != null) {
FilterCreaturePermanent filter = new FilterCreaturePermanent("nonartifact creature you control");
@ -105,7 +109,6 @@ class TheAbyssTriggeredAbility extends TriggeredAbilityImpl {
return true;
}
}
}
return false;
}

View file

@ -30,10 +30,12 @@ package mage.sets.limitedalpha;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
@ -81,11 +83,20 @@ class TimetwisterEffect extends OneShotEffect {
for (UUID playerId: sourcePlayer.getInRange()) {
Player player = game.getPlayer(playerId);
if (player != null) {
player.getLibrary().addAll(player.getHand().getCards(game), game);
player.getLibrary().addAll(player.getGraveyard().getCards(game), game);
for (Card card: player.getHand().getCards(game)) {
card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
}
for (Card card: player.getGraveyard().getCards(game)) {
card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
}
player.shuffleLibrary(game);
player.getHand().clear();
player.getGraveyard().clear();
}
}
game.getState().handleSimultaneousEvent(game); // needed here so state based triggered effects
for (UUID playerId: sourcePlayer.getInRange()) {
Player player = game.getPlayer(playerId);
if (player != null) {
player.drawCards(7, game);
}
}

View file

@ -0,0 +1,65 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.lorwyn;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.BecomesTappedCreatureControlledTriggeredAbility;
import mage.abilities.effects.common.GainLifeEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.filter.common.FilterControlledCreaturePermanent;
/**
*
* @author LevelX2
*/
public class JudgeOfCurrents extends CardImpl {
public JudgeOfCurrents(UUID ownerId) {
super(ownerId, 22, "Judge of Currents", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{W}");
this.expansionSetCode = "LRW";
this.subtype.add("Merfolk");
this.subtype.add("Wizard");
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// Whenever a Merfolk you control becomes tapped, you may gain 1 life.
this.addAbility(new BecomesTappedCreatureControlledTriggeredAbility(new GainLifeEffect(1), true, new FilterControlledCreaturePermanent("Merfolk", "a Merfolk you control")));
}
public JudgeOfCurrents(final JudgeOfCurrents card) {
super(card);
}
@Override
public JudgeOfCurrents copy() {
return new JudgeOfCurrents(this);
}
}

View file

@ -106,30 +106,24 @@ class GrandAbolisherEffect extends ContinuousRuleModifyingEffectImpl {
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
boolean spell = event.getType() == GameEvent.EventType.CAST_SPELL;
boolean activated = event.getType() == GameEvent.EventType.ACTIVATE_ABILITY;
if ((spell || activated) && game.getActivePlayerId().equals(source.getControllerId()) && game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) {
if (spell) {
return true;
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.CAST_SPELL || event.getType() == GameEvent.EventType.ACTIVATE_ABILITY;
}
// check source of activated ability
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (game.getActivePlayerId().equals(source.getControllerId()) && game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) {
switch(event.getType()) {
case CAST_SPELL:
return true;
case ACTIVATE_ABILITY:
Permanent permanent = game.getPermanent(event.getSourceId());
if (permanent != null) {
return permanent.getCardType().contains(CardType.ARTIFACT) || permanent.getCardType().contains(CardType.CREATURE)
|| permanent.getCardType().contains(CardType.ENCHANTMENT);
} else {
MageObject object = game.getObject(event.getSourceId());
if (object != null) {
return object.getCardType().contains(CardType.ARTIFACT) || object.getCardType().contains(CardType.CREATURE)
|| object.getCardType().contains(CardType.ENCHANTMENT);
}
}
}
return false;
}
}

View file

@ -94,7 +94,7 @@ class DivertEffect extends OneShotEffect {
cost.clearPaid();
if (!cost.pay(source, game, spell.getControllerId(),
spell.getControllerId(), false)) {
return spell.chooseNewTargets(game, source.getControllerId(), true, true);
return spell.chooseNewTargets(game, source.getControllerId(), true, true, null);
}
}
}

View file

@ -37,6 +37,9 @@ import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterSpell;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInHand;
@ -52,7 +55,7 @@ public class PainfulQuandary extends CardImpl {
this.expansionSetCode = "SOM";
// Whenever an opponent casts a spell, that player loses 5 life unless he or she discards a card.
this.addAbility(new SpellCastOpponentTriggeredAbility(new PainfulQuandryEffect(), false));
this.addAbility(new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new PainfulQuandryEffect(), new FilterSpell(), false, SetTargetPointer.PLAYER));
}
public PainfulQuandary(final PainfulQuandary card) {

View file

@ -0,0 +1,54 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.stronghold;
import java.util.UUID;
import mage.constants.Rarity;
/**
*
* @author LevelX2
*/
public class Cannibalize extends mage.sets.tempestremastered.Cannibalize {
public Cannibalize(UUID ownerId) {
super(ownerId);
this.cardNumber = 3;
this.expansionSetCode = "STH";
this.rarity = Rarity.COMMON;
}
public Cannibalize(final Cannibalize card) {
super(card);
}
@Override
public Cannibalize copy() {
return new Cannibalize(this);
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.stronghold;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public class SilverWyvern extends mage.sets.tempestremastered.SilverWyvern {
public SilverWyvern(UUID ownerId) {
super(ownerId);
this.cardNumber = 43;
this.expansionSetCode = "STH";
}
public SilverWyvern(final SilverWyvern card) {
super(card);
}
@Override
public SilverWyvern copy() {
return new SilverWyvern(this);
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.tempest;
import java.util.UUID;
/**
*
* @author anonymous
*/
public class CoffinQueen extends mage.sets.tempestremastered.CoffinQueen {
public CoffinQueen(UUID ownerId) {
super(ownerId);
this.cardNumber = 8;
this.expansionSetCode = "TMP";
}
public CoffinQueen(final CoffinQueen card) {
super(card);
}
@Override
public CoffinQueen copy() {
return new CoffinQueen(this);
}
}

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 mage.sets.tempest;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.combat.MustBeBlockedByTargetSourceEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author Sir-Speshkitty
*/
public class TrumpetingArmodon extends CardImpl {
public TrumpetingArmodon(UUID ownerId) {
super(ownerId, 156, "Trumpeting Armodon", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{G}");
this.expansionSetCode = "TMP";
this.subtype.add("Elephant");
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// {1}{G}: Target creature blocks Trumpeting Armodon this turn if able.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MustBeBlockedByTargetSourceEffect(), new ManaCostsImpl("{1}{G}"));
ability.addTarget(new TargetCreaturePermanent());
this.addAbility(ability);
}
public TrumpetingArmodon(final TrumpetingArmodon card) {
super(card);
}
@Override
public TrumpetingArmodon copy() {
return new TrumpetingArmodon(this);
}
}

View file

@ -0,0 +1,152 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.tempestremastered;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.filter.predicate.permanent.PermanentIdPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author LevelX2
*/
public class Cannibalize extends CardImpl {
public Cannibalize(UUID ownerId) {
super(ownerId, 83, "Cannibalize", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{1}{B}");
this.expansionSetCode = "TPR";
// Choose two target creatures controlled by the same player. Exile one of the creatures and put two +1/+1 counters on the other.
this.getSpellAbility().addEffect(new CannibalizeEffect());
this.getSpellAbility().addTarget(new TargetCreaturePermanentSameController(2,2,new FilterCreaturePermanent(),false));
}
public Cannibalize(final Cannibalize card) {
super(card);
}
@Override
public Cannibalize copy() {
return new Cannibalize(this);
}
}
class CannibalizeEffect extends OneShotEffect {
public CannibalizeEffect() {
super(Outcome.Benefit);
this.staticText = "Choose two target creatures controlled by the same player. Exile one of the creatures and put two +1/+1 counters on the other";
}
public CannibalizeEffect(final CannibalizeEffect effect) {
super(effect);
}
@Override
public CannibalizeEffect copy() {
return new CannibalizeEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) {
boolean exileDone = false;
int count = 0;
for(UUID targetId: getTargetPointer().getTargets(game, source)) {
Permanent creature = game.getPermanent(targetId);
if (creature != null) {
if ((count == 0 && controller.chooseUse(Outcome.Exile, "Exile " + creature.getLogName() +"?", game))
|| (count == 1 && !exileDone)) {
controller.moveCardToExileWithInfo(creature, null, "", source.getSourceId(), game, Zone.BATTLEFIELD, true);
exileDone = true;
} else {
creature.addCounters(CounterType.P1P1.createInstance(2), game);
game.informPlayers("Added two +1/+1 counters on " + creature.getLogName());
}
count++;
}
}
return true;
}
return false;
}
}
class TargetCreaturePermanentSameController extends TargetCreaturePermanent {
public TargetCreaturePermanentSameController(int minNumTargets, int maxNumTargets, FilterCreaturePermanent filter, boolean notTarget) {
super(minNumTargets, maxNumTargets, filter, notTarget);
this.targetName = filter.getMessage();
}
public TargetCreaturePermanentSameController(final TargetCreaturePermanentSameController target) {
super(target);
}
@Override
public boolean canTarget(UUID id, Ability source, Game game) {
UUID firstTarget = this.getFirstTarget();
if (firstTarget != null) {
Permanent permanent = game.getPermanent(firstTarget);
Permanent targetPermanent = game.getPermanent(id);
if (permanent == null || targetPermanent == null
|| !permanent.getControllerId().equals(targetPermanent.getOwnerId())) {
return false;
}
}
return super.canTarget(id, source, game);
}
@Override
public TargetCreaturePermanentSameController copy() {
return new TargetCreaturePermanentSameController(this);
}
}

View file

@ -0,0 +1,153 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.tempestremastered;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SkipUntapOptionalAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.common.FilterCreatureCard;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCardInGraveyard;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author anonymous
*/
public class CoffinQueen extends CardImpl {
public CoffinQueen(UUID ownerId) {
super(ownerId, 87, "Coffin Queen", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{B}");
this.expansionSetCode = "TPR";
this.subtype.add("Zombie");
this.subtype.add("Wizard");
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// You may choose not to untap Coffin Queen during your untap step.
this.addAbility(new SkipUntapOptionalAbility());
// {2}{B}, {tap}: Put target creature card from a graveyard onto the battlefield under your control. When Coffin Queen becomes untapped or you lose control of Coffin Queen, exile that creature.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl("{2}{B}"));
ability.addCost(new TapSourceCost());
ability.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard")));
ability.addEffect(new CoffinQueenCreateDelayedTriggerEffect());
this.addAbility(ability);
}
public CoffinQueen(final CoffinQueen card) {
super(card);
}
@Override
public CoffinQueen copy() {
return new CoffinQueen(this);
}
}
class CoffinQueenCreateDelayedTriggerEffect extends OneShotEffect {
public CoffinQueenCreateDelayedTriggerEffect() {
super(Outcome.Detriment);
this.staticText = "When Coffin Queen becomes untapped or you lose control of Coffin Queen, exile that creature";
}
public CoffinQueenCreateDelayedTriggerEffect(final CoffinQueenCreateDelayedTriggerEffect effect) {
super(effect);
}
@Override
public CoffinQueenCreateDelayedTriggerEffect copy() {
return new CoffinQueenCreateDelayedTriggerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent controlledCreature = game.getPermanent(source.getFirstTarget());
if (controlledCreature != null) {
DelayedTriggeredAbility delayedAbility = new CoffinQueenDelayedTriggeredAbility();
delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(controlledCreature.getId()));
delayedAbility.setSourceId(source.getSourceId());
delayedAbility.setControllerId(source.getControllerId());
delayedAbility.setSourceObject(source.getSourceObject(game), game);
delayedAbility.init(game);
game.addDelayedTriggeredAbility(delayedAbility);
return true;
}
return false;
}
}
class CoffinQueenDelayedTriggeredAbility extends DelayedTriggeredAbility {
CoffinQueenDelayedTriggeredAbility() {
super(new ExileTargetEffect(), Duration.EndOfGame, true);
}
CoffinQueenDelayedTriggeredAbility(CoffinQueenDelayedTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (GameEvent.EventType.LOST_CONTROL.equals(event.getType())
&& event.getSourceId().equals(getSourceId())) {
return true;
}
return GameEvent.EventType.UNTAPPED.equals(event.getType())
&& event.getTargetId() != null && event.getTargetId().equals(getSourceId());
}
@Override
public CoffinQueenDelayedTriggeredAbility copy() {
return new CoffinQueenDelayedTriggeredAbility(this);
}
@Override
public String getRule() {
return "When {this} becomes untapped or you lose control of {this}, exile that creature";
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.tempestremastered;
import java.util.UUID;
import mage.constants.Rarity;
/**
*
* @author LevelX2
*/
public class Pandemonium extends mage.sets.timeshifted.Pandemonium {
public Pandemonium(UUID ownerId) {
super(ownerId);
this.cardNumber = 149;
this.expansionSetCode = "TPR";
this.rarity = Rarity.RARE;
}
public Pandemonium(final Pandemonium card) {
super(card);
}
@Override
public Pandemonium copy() {
return new Pandemonium(this);
}
}

View file

@ -0,0 +1,118 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.tempestremastered;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ChooseNewTargetsTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.FilterStackObject;
import mage.filter.predicate.Predicate;
import mage.game.Game;
import mage.game.stack.StackObject;
import mage.target.Target;
import mage.target.TargetStackObject;
import mage.target.Targets;
/**
*
* @author LevelX2
*/
public class SilverWyvern extends CardImpl {
public SilverWyvern(UUID ownerId) {
super(ownerId, 68, "Silver Wyvern", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{U}{U}");
this.expansionSetCode = "TPR";
this.subtype.add("Drake");
this.power = new MageInt(4);
this.toughness = new MageInt(3);
// Flying
this.addAbility(FlyingAbility.getInstance());
// {U}: Change the target of target spell or ability that targets only Silver Wyvern. The new target must be a creature.
Effect effect = new ChooseNewTargetsTargetEffect(true, true);
effect.setText("Change the target of target spell or ability that targets only {this}. The new target must be a creature");
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{U}"));
FilterStackObject filter = new FilterStackObject();
filter.add(new TargetsOnlySourcePredicate(getId()));
ability.addTarget(new TargetStackObject(filter));
this.addAbility(ability);
}
public SilverWyvern(final SilverWyvern card) {
super(card);
}
@Override
public SilverWyvern copy() {
return new SilverWyvern(this);
}
}
class TargetsOnlySourcePredicate implements Predicate<MageObject> {
private final UUID sourceId;
public TargetsOnlySourcePredicate(UUID sourceId) {
this.sourceId = sourceId;
}
@Override
public boolean apply(MageObject input, Game game) {
StackObject stackObject = game.getStack().getStackObject(input.getId());
if (stackObject != null) {
Targets spellTargets = stackObject.getStackAbility().getTargets();
int numberOfTargets = 0;
for (Target target : spellTargets) {
if (target.getFirstTarget() == null || !target.getFirstTarget().toString().equals(sourceId.toString())) { // UUID != UUID does not work - it's always false
return false;
}
numberOfTargets += target.getTargets().size();
}
if (numberOfTargets == 1) {
return true;
}
}
return false;
}
@Override
public String toString() {
return "target spell or ability that targets only source";
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.timeshifted;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCreatureOrPlayer;
/**
*
* @author LevelX2
*/
public class Pandemonium extends CardImpl {
public Pandemonium(UUID ownerId) {
super(ownerId, 68, "Pandemonium", Rarity.SPECIAL, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}");
this.expansionSetCode = "TSB";
// Whenever a creature enters the battlefield, that creature's controller may have it deal damage equal to its power to target creature or player of his or her choice.
Ability ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new PandemoniumEffect(), new FilterCreaturePermanent(), false, SetTargetPointer.PERMANENT, "");
ability.addTarget(new TargetCreatureOrPlayer());
this.addAbility(ability);
}
public Pandemonium(final Pandemonium card) {
super(card);
}
@Override
public void adjustTargets(Ability ability, Game game) {
if (ability instanceof EntersBattlefieldAllTriggeredAbility) {
UUID creatureId = ability.getEffects().get(0).getTargetPointer().getFirst(game, ability);
Permanent creature = game.getPermanent(creatureId);
if (creature != null) {
ability.getTargets().get(0).setTargetController(creature.getControllerId());
}
}
}
@Override
public Pandemonium copy() {
return new Pandemonium(this);
}
}
class PandemoniumEffect extends OneShotEffect {
public PandemoniumEffect() {
super(Outcome.Benefit);
this.staticText = "that creature's controller may have it deal damage equal to its power to target creature or player of his or her choice";
}
public PandemoniumEffect(final PandemoniumEffect effect) {
super(effect);
}
@Override
public PandemoniumEffect copy() {
return new PandemoniumEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Permanent enteringCreature = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source));
if (enteringCreature != null) {
Permanent targetPermanent = game.getPermanent(source.getTargets().getFirstTarget());
if (targetPermanent != null) {
targetPermanent.damage(enteringCreature.getPower().getValue(), source.getSourceId(), game, false, true);
} else {
Player targetPlayer = game.getPlayer(source.getTargets().getFirstTarget());
if (targetPlayer != null) {
targetPlayer.damage(enteringCreature.getPower().getValue(), source.getSourceId(), game, false, true);
}
}
return true;
}
}
return false;
}
}

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 mage.sets.timespiral;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.continuous.BoostAllEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
/**
*
* @author Sir-Speshkitty
*/
public class BonesplitterSliver extends CardImpl {
public BonesplitterSliver(UUID ownerId) {
super(ownerId, 149, "Bonesplitter Sliver", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{3}{R}");
this.expansionSetCode = "TSP";
this.subtype.add("Sliver");
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// All Sliver creatures get +2/+0.
this.addAbility(
new SimpleStaticAbility(
Zone.BATTLEFIELD,
new BoostAllEffect(2, 0, Duration.WhileOnBattlefield, new FilterCreaturePermanent("Sliver","Sliver creatures"), false)
)
);
}
public BonesplitterSliver(final BonesplitterSliver card) {
super(card);
}
@Override
public BonesplitterSliver copy() {
return new BonesplitterSliver(this);
}
}

View file

@ -36,6 +36,7 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ColoredManaCost;
import mage.abilities.effects.common.RegenerateSourceEffect;
import mage.abilities.effects.common.SacrificeControllerEffect;
import mage.abilities.effects.common.SacrificeEffect;
import mage.cards.CardImpl;
import mage.constants.ColoredManaSymbol;
@ -54,7 +55,6 @@ public class SpinedFluke extends CardImpl {
this.subtype.add("Worm");
this.subtype.add("Horror");
this.color.setBlack(true);
this.power = new MageInt(5);
this.toughness = new MageInt(1);
}
@ -62,7 +62,7 @@ public class SpinedFluke extends CardImpl {
@Override
public void build() {
// When Spined Fluke enters the battlefield, sacrifice a creature.
this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeEffect(new FilterCreaturePermanent("a creature"), 1, "")));
this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeControllerEffect(new FilterCreaturePermanent("a creature"), 1, "")));
// {B}: Regenerate Spined Fluke.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ColoredManaCost(ColoredManaSymbol.B)));
}

View file

@ -60,7 +60,6 @@ public class VinesOfVastwood extends CardImpl {
public VinesOfVastwood(UUID ownerId) {
super(ownerId, 193, "Vines of Vastwood", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G}");
this.expansionSetCode = "ZEN";
this.color.setGreen(true);
// Kicker {G} (You may pay an additional {G} as you cast this spell.)
this.addAbility(new KickerAbility("{G}"));

View file

@ -0,0 +1,51 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.test.cards.abilities.activated;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class PutOntoBattlefieldTest extends CardTestPlayerBase {
/**
* Tests to put a token onto the battlefield
*/
@Test
public void testOozeFlux() {
// Enchantment
// {1}{G}, Remove one or more +1/+1 counters from among creatures you control: Put an X/X green Ooze creature token onto the battlefield, where X is the number of +1/+1 counters removed this way.
addCard(Zone.BATTLEFIELD, playerA, "Ooze Flux");
// Trample
// Kalonian Hydra enters the battlefield with four +1/+1 counters on it.
// Whenever Kalonian Hydra attacks, double the number of +1/+1 counters on each creature you control.
addCard(Zone.BATTLEFIELD, playerA, "Kalonian Hydra");
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{G},");
setChoice(playerA, "X=2"); // Remove how many
setChoice(playerA,"Kalonian Hydra");
setChoice(playerA, "X=2"); // Remove from Hydra
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPowerToughness(playerA, "Kalonian Hydra", 2, 2);
assertPermanentCount(playerA, "Ooze", 1);
assertPowerToughness(playerA, "Ooze", 2, 2);
}
}

View file

@ -30,7 +30,6 @@ 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;
@ -42,7 +41,7 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase {
/**
* Soulfire Grand Master
* Creature â Human Monk 2/2, 1W (2)
* 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
@ -233,5 +232,67 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase {
assertLife(playerA, 20);
}
/**
* I activated the ability of Soulfire grand master, it resolved, then i cast Stoke the Flames
* on Whisperwood Elemental, my opponenet sacrificed the elemental, so stoke didnt resolve,
* but i still got the life from lifelink.
*/
@Test
public void testSoulfireStokeTheFlames() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8);
addCard(Zone.HAND, playerA, "Stoke the Flames");
addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master", 1);
addCard(Zone.BATTLEFIELD, playerB, "Whisperwood Elemental", 1);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U/R}{U/R}:");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Stoke the Flames", "Whisperwood Elemental");
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Sacrifice {this}", null ,"{this} deals 4 damage");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Stoke the Flames", 1); // no legal target left so the spell is countered and goes to graveyard
assertGraveyardCount(playerB, "Whisperwood Elemental", 1);
assertLife(playerB, 20);
assertLife(playerA, 20);
}
/**
* Check if second ability resolved, the next spell that is counterer
* won't go to hand back because it did not resolve
*
*/
@Test
public void testSoulfireCounteredSpellDontGoesBack() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8);
addCard(Zone.HAND, playerA, "Stoke the Flames");
addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master", 1);
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
addCard(Zone.HAND, playerB, "Counterspell", 1);
addCard(Zone.BATTLEFIELD, playerB, "Whisperwood Elemental", 1);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U/R}{U/R}:");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Stoke the Flames", "Whisperwood Elemental");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Counterspell", "Stoke the Flames");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerB, "Counterspell", 1);
assertGraveyardCount(playerA, "Stoke the Flames", 1); // no legal target left so the spell is countered and goes to graveyard
assertLife(playerB, 20);
assertLife(playerA, 20);
}
}

View file

@ -74,16 +74,20 @@ public class DungeonGeistsTest extends CardTestPlayerBase {
public void testWithBlink() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
// When Dungeon Geists enters the battlefield, tap target creature an opponent controls.
// That creature doesn't untap during its controller's untap step for as long as you control Dungeon Geists.
addCard(Zone.HAND, playerA, "Dungeon Geists");
addCard(Zone.HAND, playerA, "Cloudshift");
addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm");
addCard(Zone.BATTLEFIELD, playerB, "Elite Vanguard");
addTarget(playerA, "Craw Wurm"); // first target Craw Wurm
addTarget(playerA, "Elite Vanguard"); // after Cloudshift effect (return back to battlefield) target Elite Vanguard
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dungeon Geists");
addTarget(playerA, "Craw Wurm"); // first target Craw Wurm
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cloudshift", "Dungeon Geists");
addTarget(playerA, "Elite Vanguard"); // after Cloudshift effect (return back to battlefield) target Elite Vanguard
setStopAt(2, PhaseStep.DRAW);
execute();

View file

@ -0,0 +1,66 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.test.cards.triggers.state;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class SynodCenturionTest extends CardTestPlayerBase {
/**
* Check that Synod Centurion gets sacrificed if no other artifacts are on the battlefield
*
*/
@Test
public void testAlone() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
addCard(Zone.BATTLEFIELD, playerA, "Demon's Horn");
addCard(Zone.HAND, playerA, "Shatter");
addCard(Zone.HAND, playerA, "Synod Centurion");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Synod Centurion");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Shatter", "Demon's Horn");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Demon's Horn", 1);
assertGraveyardCount(playerA, "Shatter", 1);
assertGraveyardCount(playerA, "Synod Centurion", 1);
}
/**
* Check that Synod Centurion gets sacrificed if the only other
* artifact left the battlefiled for a short time
*
*/
@Test
public void testWithFlicker() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 6);
addCard(Zone.BATTLEFIELD, playerA, "Bottle Gnomes");
addCard(Zone.HAND, playerA, "Cloudshift");
addCard(Zone.HAND, playerA, "Synod Centurion");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Synod Centurion");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cloudshift", "Bottle Gnomes");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Bottle Gnomes", 1);
assertGraveyardCount(playerA, "Cloudshift", 1);
assertGraveyardCount(playerA, "Synod Centurion", 1);
}
}

View file

@ -502,6 +502,18 @@ public class TestPlayer extends ComputerPlayer {
return super.announceXCost(min, max, message, game, ability, null);
}
@Override
public int getAmount(int min, int max, String message, Game game) {
if (!choices.isEmpty()) {
if (choices.get(0).startsWith("X=")) {
int xValue = Integer.parseInt(choices.get(0).substring(2));
choices.remove(0);
return xValue;
}
}
return super.getAmount(min, max, message, game);
}
protected Permanent findPermanent(FilterPermanent filter, UUID controllerId, Game game) {
List<Permanent> permanents = game.getBattlefield().getAllActivePermanents(filter, controllerId, game);
if (permanents.size() > 0) {

View file

@ -215,14 +215,20 @@ public abstract class AbilityImpl implements Ability {
else {
game.addEffect((ContinuousEffect) effect, this);
}
/**
* All restrained trigger events are fired now.
* To restrain the events is mainly neccessary because of the movement of multiple object at once.
* If the event is fired directly as one object moved, other objects are not already in the correct zone
* to check for their effects. (e.g. Valakut, the Molten Pinnacle)
*/
game.getState().handleSimultaneousEvent(game);
game.resetShortLivingLKI();
/**
* game.applyEffects() has to be done at least for every effect that moves cards/permanent between zones,
* so Static effects work as intened if dependant from the moved objects zone it is in
* Otherwise for example were static abilities with replacement effects deactivated to late
* Example: {@link org.mage.test.cards.replacement.DryadMilitantTest#testDiesByDestroy testDiesByDestroy}
*/
// game.applyEffects();
// some effects must be applied before next effect is resolved, because effect is dependend.
if (effect.applyEffectsAfter()) {
game.applyEffects();
}

View file

@ -4,10 +4,10 @@
*/
package mage.abilities.common;
import mage.constants.CardType;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
@ -18,12 +18,19 @@ import mage.game.permanent.Permanent;
*/
public class BecomesTappedCreatureControlledTriggeredAbility extends TriggeredAbilityImpl{
FilterControlledCreaturePermanent filter;
public BecomesTappedCreatureControlledTriggeredAbility(Effect effect, boolean optional) {
this(effect, optional, new FilterControlledCreaturePermanent("a creature you control"));
}
public BecomesTappedCreatureControlledTriggeredAbility(Effect effect, boolean optional, FilterControlledCreaturePermanent filter) {
super(Zone.BATTLEFIELD, effect, optional);
this.filter = filter;
}
public BecomesTappedCreatureControlledTriggeredAbility(final BecomesTappedCreatureControlledTriggeredAbility ability) {
super(ability);
this.filter = ability.filter;
}
@Override
@ -39,15 +46,11 @@ public class BecomesTappedCreatureControlledTriggeredAbility extends TriggeredAb
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null && permanent.getControllerId().equals(this.controllerId)
&& permanent.getCardType().contains(CardType.CREATURE)) {
return true;
}
return false;
return permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game);
}
@Override
public String getRule() {
return "When a creature you control becomes tapped, " + super.getRule();
return "When " + filter.getMessage() + " becomes tapped, " + super.getRule();
}
}

View file

@ -32,8 +32,10 @@ import mage.constants.Outcome;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.OneShotEffect;
import mage.filter.Filter;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
/**
* @author BetaSteward_at_googlemail.com
@ -43,21 +45,27 @@ public class ChooseNewTargetsTargetEffect extends OneShotEffect {
private boolean forceChange;
private boolean onlyOneTarget;
private FilterPermanent filterNewTarget;
public ChooseNewTargetsTargetEffect() {
this(false, false);
}
public ChooseNewTargetsTargetEffect(boolean forceChange, boolean onlyOneTarget) {
this(forceChange, onlyOneTarget, null);
}
/**
*
* @param forceChange forces the user to choose another target (only targets with maxtargets = 1 supported)
* @param onlyOneTarget only one target can be selected for the change
* @param filterNewTarget restriction to the new target
*/
public ChooseNewTargetsTargetEffect(boolean forceChange, boolean onlyOneTarget) {
public ChooseNewTargetsTargetEffect(boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) {
super(Outcome.Benefit);
this.forceChange = forceChange;
this.onlyOneTarget = onlyOneTarget;
this.filterNewTarget = filterNewTarget;
}
public ChooseNewTargetsTargetEffect(final ChooseNewTargetsTargetEffect effect) {
@ -68,9 +76,9 @@ public class ChooseNewTargetsTargetEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(source.getFirstTarget());
if (spell != null) {
return spell.chooseNewTargets(game, source.getControllerId(), forceChange, onlyOneTarget);
StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget());
if (stackObject != null) {
return stackObject.chooseNewTargets(game, source.getControllerId(), forceChange, onlyOneTarget, filterNewTarget);
}
return false;
}
@ -82,6 +90,9 @@ public class ChooseNewTargetsTargetEffect extends OneShotEffect {
@Override
public String getText(Mode mode) {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
StringBuilder sb = new StringBuilder();
if (forceChange) {
sb.append("change the target of target ");

View file

@ -95,10 +95,11 @@ public class AddCountersTargetEffect extends OneShotEffect {
permanent.addCounters(newCounter, game);
int numberAdded = permanent.getCounters().getCount(counter.getName()) - before;
affectedTargets ++;
if (!game.isSimulation())
if (!game.isSimulation()) {
game.informPlayers(sourceObject.getLogName() +": "+ controller.getName()+ " puts " +
numberAdded + " " + counter.getName().toLowerCase() + " counter on " + permanent.getLogName());
}
}
} else {
Player player = game.getPlayer(uuid);
if (player != null) {

View file

@ -31,7 +31,7 @@ public class ShadowAbility extends EvasionAbility implements MageSingleton {
@Override
public String getRule() {
return "Shadow";
return "Shadow <i>(This creature can block or be blocked by only creatures with shadow.)</i>";
}
@Override
@ -53,10 +53,7 @@ class ShadowEffect extends RestrictionEffect implements MageSingleton {
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
if (permanent.getAbilities().containsKey(ShadowAbility.getInstance().getId())) {
return true;
}
return false;
return permanent.getAbilities().containsKey(ShadowAbility.getInstance().getId());
}
@Override
@ -66,11 +63,8 @@ class ShadowEffect extends RestrictionEffect implements MageSingleton {
@Override
public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game) {
if (blocker.getAbilities().containsKey(ShadowAbility.getInstance().getId())
|| game.getContinuousEffects().asThough(blocker.getId(), AsThoughEffectType.BLOCK_SHADOW, source, blocker.getControllerId(), game)) {
return true;
}
return false;
return blocker.getAbilities().containsKey(ShadowAbility.getInstance().getId())
|| game.getContinuousEffects().asThough(blocker.getId(), AsThoughEffectType.BLOCK_SHADOW, source, blocker.getControllerId(), game);
}
@Override

View file

@ -584,7 +584,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
permanent.entersBattlefield(sourceId, game, event.getFromZone(), true);
game.setScopeRelevant(false);
game.applyEffects();
game.fireEvent(new ZoneChangeEvent(permanent, event.getPlayerId(), fromZone, Zone.BATTLEFIELD));
game.addSimultaneousEvent(new ZoneChangeEvent(permanent, event.getPlayerId(), fromZone, Zone.BATTLEFIELD));
return true;
}
if (facedown) {

View file

@ -60,7 +60,7 @@ public enum CardRepository {
// raise this if db structure was changed
private static final long CARD_DB_VERSION = 37;
// raise this if new cards were added to the server
private static final long CARD_CONTENT_VERSION = 12;
private static final long CARD_CONTENT_VERSION = 13;
private final Random random = new Random();
private Dao<CardInfo, Object> cardDao;

View file

@ -177,7 +177,16 @@ public class GameEvent {
ADD_COUNTER, COUNTER_ADDED,
ADD_COUNTERS, COUNTERS_ADDED,
COUNTER_REMOVED,
LOSE_CONTROL, LOST_CONTROL,
LOSE_CONTROL,
/* LOST_CONTROL
targetId id of the creature that lost control
sourceId id of the creature that lost control
playerId player that controlles the creature before
amount not used for this event
flag not used for this event
*/
LOST_CONTROL,
GAIN_CONTROL, GAINED_CONTROL,
CREATE_TOKEN,

View file

@ -108,7 +108,7 @@ public class PermanentToken extends PermanentImpl {
if (!game.replaceEvent(new ZoneChangeEvent(this, sourceId, this.getControllerId(), Zone.BATTLEFIELD, Zone.EXILED))) {
game.rememberLKI(objectId, Zone.BATTLEFIELD, this);
if (game.getPlayer(controllerId).removeFromBattlefield(this, game)) {
game.fireEvent(new ZoneChangeEvent(this, sourceId, this.getControllerId(), Zone.BATTLEFIELD, Zone.EXILED));
game.addSimultaneousEvent(new ZoneChangeEvent(this, sourceId, this.getControllerId(), Zone.BATTLEFIELD, Zone.EXILED));
return true;
}
}

View file

@ -55,6 +55,7 @@ import mage.constants.SpellAbilityType;
import mage.constants.Zone;
import mage.counters.Counter;
import mage.counters.Counters;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
@ -81,6 +82,7 @@ public class Spell implements StackObject, Card {
private UUID controllerId;
private boolean copiedSpell;
private boolean faceDown;
private boolean countered;
public Spell(Card card, SpellAbility ability, UUID controllerId, Zone fromZone) {
this.card = card;
@ -99,6 +101,7 @@ public class Spell implements StackObject, Card {
}
this.controllerId = controllerId;
this.fromZone = fromZone;
this.countered = false;
}
public Spell(final Spell spell) {
@ -198,8 +201,8 @@ public class Spell implements StackObject, Card {
result |= spellAbility.resolve(game);
}
}
game.getState().handleSimultaneousEvent(game);
game.resetShortLivingLKI();
// game.getState().handleSimultaneousEvent(game);
// game.resetShortLivingLKI();
index++;
}
}
@ -319,7 +322,7 @@ public class Spell implements StackObject, Card {
* @return
*/
public boolean chooseNewTargets(Game game, UUID playerId) {
return chooseNewTargets(game, playerId, false, false);
return chooseNewTargets(game, playerId, false, false, null);
}
/**
@ -377,13 +380,13 @@ public class Spell implements StackObject, Card {
*
* @param game
* @param playerId - player that can/has to change the taregt of the spell
* @param forceChange - does only work for targets with maximum of one
* targetId
* @param onlyOneTarget - 114.6b one target must be changed to another
* target
* @param forceChange - does only work for targets with maximum of one targetId
* @param onlyOneTarget - 114.6b one target must be changed to another target
* @param filterNewTarget restriction for the new target, if null nothing is cheched
* @return
*/
public boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget) {
@Override
public boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) {
Player player = game.getPlayer(playerId);
if (player != null) {
StringBuilder newTargetDescription = new StringBuilder();
@ -393,7 +396,7 @@ public class Spell implements StackObject, Card {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
Mode mode = spellAbility.getModes().get(modeId);
for (Target target : mode.getTargets()) {
Target newTarget = chooseNewTarget(player, spellAbility, mode, target, forceChange, game);
Target newTarget = chooseNewTarget(player, spellAbility, mode, target, forceChange, filterNewTarget, game);
// clear the old target and copy all targets from new target
target.clearChosen();
for (UUID targetId : newTarget.getTargets()) {
@ -424,7 +427,7 @@ public class Spell implements StackObject, Card {
* @param game
* @return
*/
private Target chooseNewTarget(Player player, SpellAbility spellAbility, Mode mode, Target target, boolean forceChange, Game game) {
private Target chooseNewTarget(Player player, SpellAbility spellAbility, Mode mode, Target target, boolean forceChange, FilterPermanent filterNewTarget, Game game) {
Target newTarget = target.copy();
newTarget.clearChosen();
for (UUID targetId : target.getTargets()) {
@ -443,6 +446,14 @@ public class Spell implements StackObject, Card {
newTarget.clearChosen();
// TODO: Distinction between "spell controller" and "player that can change the target" - here player is used for both
newTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), spellAbility, game);
// check target restriction
if (newTarget.getFirstTarget() != null && filterNewTarget != null) {
Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget());
if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) {
game.informPlayer(player, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")");
newTarget.clearChosen();
}
}
} while (player.isInGame() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1));
// choose a new target
} else {
@ -473,6 +484,12 @@ public class Spell implements StackObject, Card {
} else {
newTarget.addTarget(targetId, target.getTargetAmount(targetId), spellAbility, game, false);
}
} else if (newTarget.getFirstTarget() != null && filterNewTarget != null) {
Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget());
if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) {
game.informPlayer(player, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")");
again = true;
}
} else {
// valid target was selected, add it to the new target definition
newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), spellAbility, game, false);
@ -506,6 +523,7 @@ public class Spell implements StackObject, Card {
@Override
public void counter(UUID sourceId, Game game) {
this.countered = true;
if (!isCopiedSpell()) {
card.moveToZone(Zone.GRAVEYARD, sourceId, game, false);
}
@ -987,4 +1005,8 @@ public class Spell implements StackObject, Card {
throw new UnsupportedOperationException("Not supported.");
}
public boolean isCountered() {
return countered;
}
}

View file

@ -97,9 +97,10 @@ public class SpellStack extends ArrayDeque<StackObject> {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) {
if ( stackObject instanceof Spell ) {
game.rememberLKI(objectId, Zone.STACK, (Spell)stackObject);
}
} else {
this.remove(stackObject);
stackObject.counter(sourceId, game); // tries to move to graveyard
}
stackObject.counter(sourceId, game);
if (!game.isSimulation())
game.informPlayers(counteredObjectName + " is countered by " + sourceObject.getLogName());
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId()));

View file

@ -54,10 +54,15 @@ import mage.target.Targets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.cards.Card;
import mage.constants.AbilityWord;
import mage.constants.Outcome;
import mage.filter.FilterPermanent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetAmount;
import mage.watchers.Watcher;
/**
@ -535,4 +540,197 @@ public class StackAbility implements StackObject, Ability {
throw new UnsupportedOperationException("Not supported.");
}
/**
* 114.6. Some effects allow a player to change the target(s) of a spell or
* ability, and other effects allow a player to choose new targets for a
* spell or ability.
*
* 114.6a If an effect allows a player to "change the
* target(s)" of a spell or ability, each target can be changed only to
* another legal target. If a target can't be changed to another legal
* target, the original target is unchanged, even if the original target is
* itself illegal by then. If all the targets aren't changed to other legal
* targets, none of them are changed.
*
* 114.6b If an effect allows a player to "change a target" of a
* spell or ability, the process described in rule 114.6a
* is followed, except that only one of those targets may be changed
* (rather than all of them or none of them).
*
* 114.6c If an effect allows a
* player to "change any targets" of a spell or ability, the process
* described in rule 114.6a is followed, except that any number of those
* targets may be changed (rather than all of them or none of them).
*
* 114.6d If an effect allows a player to "choose new targets" for a spell or
* ability, the player may leave any number of the targets unchanged, even
* if those targets would be illegal. If the player chooses to change some
* or all of the targets, the new targets must be legal and must not cause
* any unchanged targets to become illegal.
*
* 114.6e When changing targets or
* choosing new targets for a spell or ability, only the final set of
* targets is evaluated to determine whether the change is legal.
*
* Example: Arc Trail is a sorcery that reads "Arc Trail deals 2 damage to
* target creature or player and 1 damage to another target creature or
* player." The current targets of Arc Trail are Runeclaw Bear and Llanowar
* Elves, in that order. You cast Redirect, an instant that reads "You may
* choose new targets for target spell," targeting Arc Trail. You can change
* the first target to Llanowar Elves and change the second target to
* Runeclaw Bear.
*
* 114.7. Modal spells and abilities may have different targeting
* requirements for each mode. An effect that allows a player to change the
* target(s) of a modal spell or ability, or to choose new targets for a
* modal spell or ability, doesn't allow that player to change its mode.
* (See rule 700.2.)
*
* 706.10c Some effects copy a spell or ability and state that its
* controller may choose new targets for the copy. The player may leave any
* number of the targets unchanged, even if those targets would be illegal.
* If the player chooses to change some or all of the targets, the new
* targets must be legal. Once the player has decided what the copy's
* targets will be, the copy is put onto the stack with those targets.
*
* @param game
* @param playerId - player that can/has to change the target of the ability
* @param forceChange - does only work for targets with maximum of one targetId
* @param onlyOneTarget - 114.6b one target must be changed to another target
* @param filterNewTarget restriction for the new target, if null nothing is cheched
* @return
*/
@Override
public boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) {
Player player = game.getPlayer(playerId);
if (player != null) {
StringBuilder newTargetDescription = new StringBuilder();
// Some abilities can have more than one mode
for (UUID modeId : ability.getModes().getSelectedModes()) {
Mode mode = ability.getModes().get(modeId);
for (Target target : mode.getTargets()) {
Target newTarget = chooseNewTarget(player, getStackAbility(), mode, target, forceChange, filterNewTarget, game);
// clear the old target and copy all targets from new target
target.clearChosen();
for (UUID targetId : newTarget.getTargets()) {
target.addTarget(targetId, newTarget.getTargetAmount(targetId), ability, game, false);
}
}
newTargetDescription.append(((AbilityImpl)ability).getTargetDescription(mode.getTargets(), game));
}
if (newTargetDescription.length() > 0 && !game.isSimulation()) {
game.informPlayers(this.getName() + " is now " + newTargetDescription.toString());
}
return true;
}
return false;
}
/**
* Handles the change of one target instance of a mode
*
* @param player - player that can choose the new target
* @param ability
* @param mode
* @param target
* @param forceChange
* @param game
* @return
*/
private Target chooseNewTarget(Player player, Ability ability, Mode mode, Target target, boolean forceChange, FilterPermanent filterNewTarget, Game game) {
Target newTarget = target.copy();
newTarget.clearChosen();
for (UUID targetId : target.getTargets()) {
String targetNames = getNamesOfTargets(targetId, game);
// change the target?
if (targetNames != null
&& (forceChange || player.chooseUse(mode.getEffects().get(0).getOutcome(), "Change this target: " + targetNames + "?", game))) {
// choose exactly one other target
if (forceChange && target.possibleTargets(this.getSourceId(), getControllerId(), game).size() > 1) { // controller of ability must be used (e.g. TargetOpponent)
int iteration = 0;
do {
if (iteration > 0 && !game.isSimulation()) {
game.informPlayer(player, "You may only select exactly one target that must be different from the origin target!");
}
iteration++;
newTarget.clearChosen();
// TODO: Distinction between "ability controller" and "player that can change the target" - here player is used for both
newTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), ability, game);
// check target restriction
if (newTarget.getFirstTarget() != null && filterNewTarget != null) {
Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget());
if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) {
game.informPlayer(player, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")");
newTarget.clearChosen();
}
}
} while (player.isInGame() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1));
// choose a new target
} else {
// build a target definition with exactly one possible target to select that replaces old target
Target tempTarget = target.copy();
if (target instanceof TargetAmount) {
((TargetAmount)tempTarget).setAmountDefinition(new StaticValue(target.getTargetAmount(targetId)));
}
tempTarget.setMinNumberOfTargets(1);
tempTarget.setMaxNumberOfTargets(1);
boolean again;
do {
again = false;
tempTarget.clearChosen();
if (!tempTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), ability, game)) {
if (player.chooseUse(Outcome.Benefit, "No target object selected. Reset to original target?", game)) {
// use previous target no target was selected
newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false);
} else {
again = true;
}
} else {
// if possible add the alternate Target - it may not be included in the old definition nor in the already selected targets of the new definition
if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) {
if (player.isHuman()) {
game.informPlayer(player, "This target was already selected from origin ability. You can only keep this target!");
again = true;
} else {
newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false);
}
} else if (newTarget.getFirstTarget() != null && filterNewTarget != null) {
Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget());
if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) {
game.informPlayer(player, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")");
again = true;
}
} else {
// valid target was selected, add it to the new target definition
newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, false);
}
}
} while (again && player.isInGame());
}
}
// keep the target
else {
newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false);
}
}
return newTarget;
}
private String getNamesOfTargets(UUID targetId, Game game) {
MageObject object = game.getObject(targetId);
String targetNames = null;
if (object == null) {
Player targetPlayer = game.getPlayer(targetId);
if (targetPlayer != null) {
targetNames = targetPlayer.getName();
}
} else {
targetNames = object.getName();
}
return targetNames;
}
}

View file

@ -31,6 +31,7 @@ package mage.game.stack;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.filter.FilterPermanent;
import mage.game.Controllable;
import mage.game.Game;
@ -41,6 +42,7 @@ public interface StackObject extends MageObject, Controllable {
void counter(UUID sourceId, Game game);
Ability getStackAbility();
int getConvertedManaCost();
boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget);
@Override
StackObject copy();
}

View file

@ -104,4 +104,8 @@ public interface Target extends Serializable {
UUID getFirstTarget();
Target copy();
// some targets are choosen from players that are not the controller of the ability (e.g. Pandemonium)
void setTargetController(UUID playerId);
UUID getTargetController();
}

View file

@ -60,6 +60,7 @@ public abstract class TargetImpl implements Target {
// is the target handled as targeted spell/ability (notTarget = true is used for not targeted effects like e.g. sacrifice)
protected boolean notTarget = false;
protected boolean atRandom = false;
protected UUID targetController = null; // if null the ability controller is the targetController
@Override
public abstract TargetImpl copy();
@ -84,6 +85,7 @@ public abstract class TargetImpl implements Target {
this.zoneChangeCounters.putAll(target.zoneChangeCounters);
this.atRandom = target.atRandom;
this.notTarget = target.notTarget;
this.targetController = target.targetController;
}
@Override
@ -421,5 +423,15 @@ public abstract class TargetImpl implements Target {
this.atRandom = atRandom;
}
@Override
public void setTargetController(UUID playerId) {
this.targetController = playerId;
}
@Override
public UUID getTargetController() {
return targetController;
}
}

View file

@ -97,7 +97,11 @@ public class Targets extends ArrayList<Target> {
}
while (!isChosen()) {
Target target = this.getUnchosen().get(0);
if (!target.chooseTarget(outcome, playerId, source, game)) {
UUID targetController = playerId;
if (target.getTargetController() != null) { // some targets can have controller different than ability controller
targetController = target.getTargetController();
}
if (!target.chooseTarget(outcome, targetController, source, game)) {
return false;
}
}

View file

@ -132,7 +132,7 @@ public class CardUtil {
* @param increaseCount
*/
public static void increaseCost(Ability ability, int increaseCount) {
adjustCost(ability, -increaseCount);
adjustAbilityCost(ability, -increaseCount);
adjustAlternativeCosts(ability, -increaseCount);
}
@ -143,7 +143,7 @@ public class CardUtil {
* @param reduceCount
*/
public static void reduceCost(Ability ability, int reduceCount) {
adjustCost(ability, reduceCount);
adjustAbilityCost(ability, reduceCount);
adjustAlternativeCosts(ability, reduceCount);
}
@ -154,7 +154,7 @@ public class CardUtil {
* @param reduceCount
*/
public static void adjustCost(SpellAbility spellAbility, int reduceCount) {
CardUtil.adjustCost((Ability) spellAbility, reduceCount);
CardUtil.adjustAbilityCost((Ability) spellAbility, reduceCount);
adjustAlternativeCosts(spellAbility, reduceCount);
}
@ -208,7 +208,7 @@ public class CardUtil {
* @param ability
* @param reduceCount
*/
private static void adjustCost(Ability ability, int reduceCount) {
public static void adjustAbilityCost(Ability ability, int reduceCount) {
ManaCosts<ManaCost> adjustedCost = adjustCost(ability.getManaCostsToPay(), reduceCount);
ability.getManaCostsToPay().clear();
ability.getManaCostsToPay().addAll(adjustedCost);

View file

@ -117,6 +117,9 @@ git log 65f731557bb55d0c85723e382001bdf9701f0a7f..HEAD --diff-filter=A --name-st
since 1.3.0-2015-03-14v6
git log b79d6e64cff01726be93cbbfffca8a6f18188a3c..HEAD --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt
since 1.3.0-2015-03-14v8
git log 47b17535194c6aa5397a966463c8b17d37f8bd44..HEAD --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt
3. Copy added_cards.txt to trunk\Utils folder
4. Run script:
> perl extract_in_wiki_format.perl