forked from External/mage
Merge branch 'master' into case-of-the-pilfered-proof
This commit is contained in:
commit
ca45c06f71
1595 changed files with 24451 additions and 9637 deletions
|
|
@ -34,8 +34,10 @@ public enum MageIdentifier {
|
|||
SerraParagonWatcher,
|
||||
OneWithTheMultiverseWatcher("Without paying manacost"),
|
||||
JohannApprenticeSorcererWatcher,
|
||||
AssembleThePlayersWatcher,
|
||||
KaghaShadowArchdruidWatcher,
|
||||
CourtOfLocthwainWatcher("Without paying manacost"),
|
||||
LaraCroftTombRaiderWatcher,
|
||||
|
||||
// ----------------------------//
|
||||
// alternate casts //
|
||||
|
|
@ -68,7 +70,9 @@ public enum MageIdentifier {
|
|||
SqueeDubiousMonarchAlternateCast,
|
||||
WorldheartPhoenixAlternateCast,
|
||||
XandersPactAlternateCast,
|
||||
TheTombOfAclazotzWatcher;
|
||||
TheTombOfAclazotzWatcher,
|
||||
|
||||
MeTheImmortalAlternateCast;
|
||||
|
||||
/**
|
||||
* Additional text if there is need to differentiate two very similar effects
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
|
||||
|
||||
package mage;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface MageItem extends Serializable {
|
||||
|
||||
UUID getId();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,15 @@ public interface MageObject extends MageItem, Serializable, Copyable<MageObject>
|
|||
|
||||
void setImageNumber(Integer imageNumber);
|
||||
|
||||
/**
|
||||
* Get image file name
|
||||
* - empty for default name from a card
|
||||
* - non-empty for face down objects like Morph (GUI show empty name, but image must show some image)
|
||||
*/
|
||||
String getImageFileName();
|
||||
|
||||
void setImageFileName(String imageFile);
|
||||
|
||||
String getName();
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ public abstract class MageObjectImpl implements MageObject {
|
|||
|
||||
private String expansionSetCode = "";
|
||||
private String cardNumber = "";
|
||||
private String imageFileName = "";
|
||||
private int imageNumber = 0;
|
||||
|
||||
protected List<SuperType> supertype = new ArrayList<>();
|
||||
|
|
@ -77,6 +78,7 @@ public abstract class MageObjectImpl implements MageObject {
|
|||
frameStyle = object.frameStyle;
|
||||
expansionSetCode = object.expansionSetCode;
|
||||
cardNumber = object.cardNumber;
|
||||
imageFileName = object.imageFileName;
|
||||
imageNumber = object.imageNumber;
|
||||
power = object.power.copy();
|
||||
toughness = object.toughness.copy();
|
||||
|
|
@ -266,6 +268,16 @@ public abstract class MageObjectImpl implements MageObject {
|
|||
this.cardNumber = cardNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImageFileName() {
|
||||
return imageFileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageFileName(String imageFileName) {
|
||||
this.imageFileName = imageFileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getImageNumber() {
|
||||
return imageNumber;
|
||||
|
|
|
|||
|
|
@ -35,15 +35,6 @@ import java.util.UUID;
|
|||
*/
|
||||
public interface Ability extends Controllable, Serializable {
|
||||
|
||||
/**
|
||||
* Gets the globally unique id of the ability contained within the game.
|
||||
*
|
||||
* @return A {@link java.util.UUID} which the game will use to store and
|
||||
* retrieve the exact instance of this ability.
|
||||
*/
|
||||
@Override
|
||||
UUID getId();
|
||||
|
||||
/**
|
||||
* Assigns a new {@link java.util.UUID}
|
||||
*
|
||||
|
|
@ -71,14 +62,6 @@ public interface Ability extends Controllable, Serializable {
|
|||
*/
|
||||
AbilityType getAbilityType();
|
||||
|
||||
/**
|
||||
* Gets the id of the player in control of this ability.
|
||||
*
|
||||
* @return The {@link java.util.UUID} of the controlling player.
|
||||
*/
|
||||
@Override
|
||||
UUID getControllerId();
|
||||
|
||||
/**
|
||||
* Sets the id of the controller of this ability.
|
||||
*
|
||||
|
|
@ -228,6 +211,8 @@ public interface Ability extends Controllable, Serializable {
|
|||
* Retrieves the {@link Target} located at the 0th index in the
|
||||
* {@link Targets}. A call to the method is equivalent to
|
||||
* {@link #getTargets()}.get(0).getFirstTarget().
|
||||
* <p>
|
||||
* Warning, if you effect uses target pointers then it must search getTargetPointer too
|
||||
*
|
||||
* @return The {@link java.util.UUID} of the first target within the targets
|
||||
* list.
|
||||
|
|
|
|||
|
|
@ -428,6 +428,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
case MORE_THAN_MEETS_THE_EYE:
|
||||
case BESTOW:
|
||||
case MORPH:
|
||||
case DISGUISE:
|
||||
// from Snapcaster Mage:
|
||||
// If you cast a spell from a graveyard using its flashback ability, you can't pay other alternative costs
|
||||
// (such as that of Foil). (2018-12-07)
|
||||
|
|
@ -649,6 +650,11 @@ public abstract class AbilityImpl implements Ability {
|
|||
return controllerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getControllerOrOwnerId() {
|
||||
return getControllerId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setControllerId(UUID controllerId) {
|
||||
this.controllerId = controllerId;
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
if (!approvingObjects.isEmpty()) {
|
||||
Card card = game.getCard(sourceId);
|
||||
Zone zone = game.getState().getZone(sourceId);
|
||||
if(card != null && card.isOwnedBy(playerId) && Zone.HAND.match(zone)) {
|
||||
if (card != null && card.isOwnedBy(playerId) && Zone.HAND.match(zone)) {
|
||||
// Regular casting, to be an alternative to the AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE from hand (e.g. One with the Multiverse):
|
||||
approvingObjects.add(new ApprovingObject(this, game));
|
||||
}
|
||||
|
|
@ -160,8 +160,8 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
Player player = game.getPlayer(playerId);
|
||||
if (player != null
|
||||
&& player.getCastSourceIdWithAlternateMana()
|
||||
.getOrDefault(getSourceId(), Collections.emptySet())
|
||||
.contains(MageIdentifier.Default)
|
||||
.getOrDefault(getSourceId(), Collections.emptySet())
|
||||
.contains(MageIdentifier.Default)
|
||||
) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
|
|
@ -181,11 +181,10 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
}
|
||||
return ActivationStatus.getFalse();
|
||||
} else {
|
||||
if(canChooseTarget(game, playerId)) {
|
||||
if(approvingObjects == null || approvingObjects.isEmpty()) {
|
||||
if (canChooseTarget(game, playerId)) {
|
||||
if (approvingObjects == null || approvingObjects.isEmpty()) {
|
||||
return ActivationStatus.withoutApprovingObject(true);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return new ActivationStatus(approvingObjects);
|
||||
}
|
||||
}
|
||||
|
|
@ -308,22 +307,27 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a card object with the spell characteristics like color, types,
|
||||
* Returns combined card object with the spell characteristics like color, types,
|
||||
* subtypes etc. E.g. if you cast a Bestow card as enchantment, the
|
||||
* characteristics don't include the creature type.
|
||||
* <p>
|
||||
* Warning, it's not a real card - use it as a blueprint or characteristics searching
|
||||
*
|
||||
* @param game
|
||||
* @return card object with the spell characteristics
|
||||
*/
|
||||
public Card getCharacteristics(Game game) {
|
||||
Card spellCharacteristics = game.getSpell(this.getId());
|
||||
if (spellCharacteristics == null) {
|
||||
// playable check (without put to stack)
|
||||
spellCharacteristics = game.getCard(this.getSourceId());
|
||||
}
|
||||
|
||||
if (spellCharacteristics != null) {
|
||||
if (getSpellAbilityCastMode() != SpellAbilityCastMode.NORMAL) {
|
||||
spellCharacteristics = getSpellAbilityCastMode().getTypeModifiedCardObjectCopy(spellCharacteristics, this);
|
||||
// transform characteristics (morph, transform, bestow, etc)
|
||||
spellCharacteristics = getSpellAbilityCastMode().getTypeModifiedCardObjectCopy(spellCharacteristics, this, game);
|
||||
}
|
||||
spellCharacteristics = spellCharacteristics.copy();
|
||||
}
|
||||
return spellCharacteristics;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
package mage.abilities.abilityword;
|
||||
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.common.TapTargetCost;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.AbilityWord;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.permanent.TappedPredicate;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
|
||||
public class CohortAbility extends SimpleActivatedAbility {
|
||||
|
||||
private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.ALLY, "an untapped Ally you control");
|
||||
|
||||
static {
|
||||
filter.add(TappedPredicate.UNTAPPED);
|
||||
}
|
||||
|
||||
public CohortAbility(Effect effect) {
|
||||
super(Zone.BATTLEFIELD, effect, new TapSourceCost());
|
||||
this.addCost(new TapTargetCost(new TargetControlledPermanent(filter)));
|
||||
this.setAbilityWord(AbilityWord.COHORT);
|
||||
}
|
||||
|
||||
protected CohortAbility(final CohortAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CohortAbility copy() {
|
||||
return new CohortAbility(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import mage.game.Game;
|
|||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
|
|
@ -42,7 +43,7 @@ public class AttackedByCreatureTriggeredAbility extends TriggeredAbilityImpl {
|
|||
super(zone, effect, optional);
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
this.filter = filter;
|
||||
setTriggerPhrase("Whenever " + filter.getMessage() + " attacks you, ");
|
||||
setTriggerPhrase("Whenever " + CardUtil.addArticle(filter.getMessage()) + " attacks you, ");
|
||||
}
|
||||
|
||||
protected AttackedByCreatureTriggeredAbility(final AttackedByCreatureTriggeredAbility ability) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.target.targetpointer.FixedTargets;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
|
@ -91,7 +92,7 @@ public class AttacksWithCreaturesTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
getEffects().setValue(VALUEKEY_NUMBER_ATTACKERS, attackers.size());
|
||||
if (setTargetPointer) {
|
||||
getEffects().setTargetPointer(new FixedTargets(attackers, game));
|
||||
getEffects().setTargetPointer(new FixedTargets(new ArrayList<>(attackers), game));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
|
|
@ -14,20 +13,29 @@ import mage.game.events.GameEvent;
|
|||
public class CounterRemovedFromSourceWhileExiledTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private final CounterType counterType;
|
||||
private final boolean onlyController;
|
||||
|
||||
public CounterRemovedFromSourceWhileExiledTriggeredAbility(CounterType counterType, Effect effect) {
|
||||
this(counterType, effect, false);
|
||||
}
|
||||
|
||||
public CounterRemovedFromSourceWhileExiledTriggeredAbility(CounterType counterType, Effect effect, boolean optional) {
|
||||
this(counterType, effect, optional, false);
|
||||
}
|
||||
|
||||
public CounterRemovedFromSourceWhileExiledTriggeredAbility(CounterType counterType, Effect effect, boolean optional, boolean onlyController) {
|
||||
super(Zone.EXILED, effect, optional);
|
||||
this.counterType = counterType;
|
||||
setTriggerPhrase("Whenever a " + counterType.getName() + " counter is removed from {this} while it's exiled, ");
|
||||
this.onlyController = onlyController;
|
||||
setTriggerPhrase("Whenever " + (
|
||||
onlyController ? ("you remove a " + counterType.getName() + " counter") : ("a " + counterType.getName() + " counter is removed")
|
||||
) + " from {this} while it's exiled, ");
|
||||
}
|
||||
|
||||
private CounterRemovedFromSourceWhileExiledTriggeredAbility(final CounterRemovedFromSourceWhileExiledTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.counterType = ability.counterType;
|
||||
this.onlyController = ability.onlyController;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -42,6 +50,8 @@ public class CounterRemovedFromSourceWhileExiledTriggeredAbility extends Trigger
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return event.getData().equals(counterType.getName()) && event.getTargetId().equals(this.getSourceId());
|
||||
return event.getData().equals(counterType.getName())
|
||||
&& event.getTargetId().equals(this.getSourceId())
|
||||
&& (!onlyController || event.getPlayerId().equals(getControllerId()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
|
|
@ -12,6 +11,7 @@ import mage.game.events.DamagedPlayerEvent;
|
|||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
|
|
@ -43,7 +43,7 @@ public class DealsDamageToAPlayerAllTriggeredAbility extends TriggeredAbilityImp
|
|||
this.onlyCombat = onlyCombat;
|
||||
this.affectsDefendingPlayer = affectsDefendingPlayer;
|
||||
this.targetController = targetController;
|
||||
setTriggerPhrase("Whenever " + filter.getMessage() + " deals " + (onlyCombat ? "combat " : "") + "damage to "
|
||||
setTriggerPhrase("Whenever " + CardUtil.addArticle(filter.getMessage()) + " deals " + (onlyCombat ? "combat " : "") + "damage to "
|
||||
+ (targetController == TargetController.OPPONENT ? "an opponent" : "a player") + ", ");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,11 @@ public class DrawCardOpponentTriggeredAbility extends TriggeredAbilityImpl {
|
|||
private final boolean setTargetPointer;
|
||||
|
||||
public DrawCardOpponentTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
this(Zone.BATTLEFIELD, effect, optional, setTargetPointer);
|
||||
}
|
||||
|
||||
public DrawCardOpponentTriggeredAbility(Zone zone, Effect effect, boolean optional, boolean setTargetPointer) {
|
||||
super(zone, effect, optional);
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
setTriggerPhrase("Whenever an opponent draws a card, ");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,12 @@ import mage.abilities.effects.Effect;
|
|||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.*;
|
||||
import mage.watchers.common.CardsDrawnThisTurnWatcher;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
|
|
@ -37,7 +34,6 @@ public class DrawNthCardTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
public DrawNthCardTriggeredAbility(Zone zone, Effect effect, boolean optional, TargetController targetController, int cardNumber) {
|
||||
super(zone, effect, optional);
|
||||
this.addWatcher(new DrawCardWatcher());
|
||||
this.targetController = targetController;
|
||||
this.cardNumber = cardNumber;
|
||||
this.addHint(hint);
|
||||
|
|
@ -77,7 +73,8 @@ public class DrawNthCardTriggeredAbility extends TriggeredAbilityImpl {
|
|||
default:
|
||||
throw new IllegalArgumentException("TargetController " + targetController + " not supported");
|
||||
}
|
||||
return DrawCardWatcher.checkEvent(event.getPlayerId(), event, game, cardNumber);
|
||||
CardsDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsDrawnThisTurnWatcher.class);
|
||||
return watcher != null && watcher.getCardsDrawnThisTurn(event.getPlayerId()) == cardNumber;
|
||||
}
|
||||
|
||||
public String generateTriggerPhrase() {
|
||||
|
|
@ -98,35 +95,3 @@ public class DrawNthCardTriggeredAbility extends TriggeredAbilityImpl {
|
|||
return new DrawNthCardTriggeredAbility(this);
|
||||
}
|
||||
}
|
||||
|
||||
class DrawCardWatcher extends Watcher {
|
||||
|
||||
private final Map<UUID, List<UUID>> drawMap = new HashMap<>();
|
||||
|
||||
DrawCardWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() != GameEvent.EventType.DREW_CARD) {
|
||||
return;
|
||||
}
|
||||
if (!drawMap.containsKey(event.getPlayerId())) {
|
||||
drawMap.putIfAbsent(event.getPlayerId(), new ArrayList<>());
|
||||
}
|
||||
drawMap.get(event.getPlayerId()).add(event.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
drawMap.clear();
|
||||
}
|
||||
|
||||
static boolean checkEvent(UUID playerId, GameEvent event, Game game, int cardNumber) {
|
||||
Map<UUID, List<UUID>> drawMap = game.getState().getWatcher(DrawCardWatcher.class).drawMap;
|
||||
return drawMap.containsKey(playerId) && Objects.equals(drawMap.get(playerId).size(), cardNumber) && event.getId().equals(drawMap.get(playerId).get(cardNumber - 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public class OneOrMoreCountersAddedTriggeredAbility extends TriggeredAbilityImpl
|
|||
setTriggerPhrase("Whenever one or more " + counterType.getName() + " counters are put on {this}, ");
|
||||
}
|
||||
|
||||
private OneOrMoreCountersAddedTriggeredAbility(final OneOrMoreCountersAddedTriggeredAbility ability) {
|
||||
protected OneOrMoreCountersAddedTriggeredAbility(final OneOrMoreCountersAddedTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.counterType = ability.counterType;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public class ScryTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
|
||||
public ScryTriggeredAbility(Zone zone, Effect effect, boolean optional) {
|
||||
super(zone, effect, false);
|
||||
super(zone, effect, optional);
|
||||
setTriggerPhrase("Whenever you scry, ");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* @author PurpleCrowbar
|
||||
*/
|
||||
public class SkipExtraTurnsAbility extends SimpleStaticAbility {
|
||||
|
||||
private final boolean onlyOpponents;
|
||||
|
||||
public SkipExtraTurnsAbility() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public SkipExtraTurnsAbility(boolean onlyOpponents) {
|
||||
super(Zone.BATTLEFIELD, new SkipExtraTurnsEffect(onlyOpponents));
|
||||
this.onlyOpponents = onlyOpponents;
|
||||
}
|
||||
|
||||
private SkipExtraTurnsAbility(final SkipExtraTurnsAbility ability) {
|
||||
super(ability);
|
||||
this.onlyOpponents = ability.onlyOpponents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkipExtraTurnsAbility copy() {
|
||||
return new SkipExtraTurnsAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "If a" + (onlyOpponents ? "n opponent" : " player") + " would begin an extra turn, that player skips that turn instead.";
|
||||
}
|
||||
}
|
||||
|
||||
class SkipExtraTurnsEffect extends ReplacementEffectImpl {
|
||||
|
||||
private final boolean onlyOpponents;
|
||||
|
||||
SkipExtraTurnsEffect(boolean onlyOpponents) {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment);
|
||||
this.onlyOpponents = onlyOpponents;
|
||||
}
|
||||
|
||||
private SkipExtraTurnsEffect(final SkipExtraTurnsEffect effect) {
|
||||
super(effect);
|
||||
this.onlyOpponents = effect.onlyOpponents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkipExtraTurnsEffect copy() {
|
||||
return new SkipExtraTurnsEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
Player player = game.getPlayer(event.getPlayerId());
|
||||
MageObject sourceObject = game.getObject(source);
|
||||
if (player != null && sourceObject != null) {
|
||||
game.informPlayers(sourceObject.getLogName() + ": Extra turn of " + player.getLogName() + " skipped");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.EXTRA_TURN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return !onlyOpponents || game.getPlayer(source.getControllerId()).hasOpponent(event.getPlayerId(), game);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
* @author PurpleCrowbar
|
||||
*/
|
||||
public class SurveilTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public SurveilTriggeredAbility(Effect effect) {
|
||||
this(Zone.BATTLEFIELD, effect);
|
||||
}
|
||||
|
||||
public SurveilTriggeredAbility(Zone zone, Effect effect) {
|
||||
super(zone, effect);
|
||||
setTriggerPhrase("Whenever you surveil, " + (zone == Zone.GRAVEYARD ? "if {this} is in your graveyard, " : ""));
|
||||
}
|
||||
|
||||
private SurveilTriggeredAbility(final SurveilTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurveilTriggeredAbility copy() {
|
||||
return new SurveilTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.SURVEILED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (isControlledBy(event.getPlayerId())) {
|
||||
this.getEffects().setValue("amount", event.getAmount());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -29,8 +29,8 @@ public class TurnFaceUpAbility extends SpecialAction {
|
|||
this.addCost(costs);
|
||||
this.usesStack = false;
|
||||
this.abilityType = AbilityType.SPECIAL_ACTION;
|
||||
this.setRuleVisible(false); // will be made visible only to controller in CardView
|
||||
this.setWorksFaceDown(true);
|
||||
this.setRuleVisible(false); // hide in face up, but show in face down view (it will be enabled as default ability)
|
||||
}
|
||||
|
||||
protected TurnFaceUpAbility(final TurnFaceUpAbility ability) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
|
||||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.watchers.common.PlayerAttackedStepWatcher;
|
||||
import mage.game.combat.CombatGroup;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A condition which checks whether any players being attacked are poisoned
|
||||
|
|
@ -14,17 +15,23 @@ import mage.watchers.common.PlayerAttackedStepWatcher;
|
|||
* @author alexander-novo
|
||||
*/
|
||||
public enum AttackedPlayersPoisonedCondition implements Condition {
|
||||
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return game.getCombat().getDefenders().stream().map(defender -> game.getPlayer(defender))
|
||||
.anyMatch(player -> player != null && player.getCounters().containsKey(CounterType.POISON));
|
||||
return game.getCombat()
|
||||
.getGroups()
|
||||
.stream()
|
||||
.map(CombatGroup::getDefenderId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.map(game::getPlayer)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(player -> player.getCounters().containsKey(CounterType.POISON));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "one or more players being attacked are poisoned";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ import mage.abilities.hint.Hint;
|
|||
import mage.game.Game;
|
||||
import mage.watchers.common.PermanentsSacrificedWatcher;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
|
|
@ -20,11 +24,12 @@ public enum SacrificedArtifactThisTurnCondition implements Condition {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return game
|
||||
.getState()
|
||||
.getWatcher(PermanentsSacrificedWatcher.class)
|
||||
.getThisTurnSacrificedPermanents(source.getControllerId())
|
||||
.stream()
|
||||
return Optional.ofNullable(game
|
||||
.getState()
|
||||
.getWatcher(PermanentsSacrificedWatcher.class)
|
||||
.getThisTurnSacrificedPermanents(source.getControllerId()))
|
||||
.map(List::stream)
|
||||
.orElseGet(Stream::empty)
|
||||
.anyMatch(permanent -> permanent.isArtifact(game));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import mage.game.events.GameEvent;
|
|||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Objects;
|
||||
|
|
@ -25,16 +26,23 @@ import java.util.UUID;
|
|||
public class CollectEvidenceCost extends CostImpl {
|
||||
|
||||
private final int amount;
|
||||
private final boolean withSource;
|
||||
|
||||
public CollectEvidenceCost(int amount) {
|
||||
this(amount, false);
|
||||
}
|
||||
|
||||
public CollectEvidenceCost(int amount, boolean withSource) {
|
||||
super();
|
||||
this.amount = amount;
|
||||
this.withSource = withSource;
|
||||
this.text = "collect evidence " + amount;
|
||||
}
|
||||
|
||||
private CollectEvidenceCost(final CollectEvidenceCost cost) {
|
||||
super(cost);
|
||||
this.amount = cost.amount;
|
||||
this.withSource = cost.withSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -83,7 +91,11 @@ public class CollectEvidenceCost extends CostImpl {
|
|||
.mapToInt(MageObject::getManaValue)
|
||||
.sum() >= amount;
|
||||
if (paid) {
|
||||
player.moveCards(cards, Zone.EXILED, source, game);
|
||||
if (withSource) {
|
||||
player.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source));
|
||||
} else {
|
||||
player.moveCards(cards, Zone.EXILED, source, game);
|
||||
}
|
||||
game.fireEvent(GameEvent.getEvent(
|
||||
GameEvent.EventType.EVIDENCE_COLLECTED,
|
||||
source.getSourceId(), source, source.getControllerId(), amount
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import mage.util.CardUtil;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author nantuko
|
||||
|
|
@ -95,7 +96,7 @@ public class ExileFromGraveCost extends CostImpl {
|
|||
CardUtil.getSourceName(game, source)
|
||||
);
|
||||
if (setTargetPointer) {
|
||||
source.getEffects().setTargetPointer(new FixedTargets(cardsToExile, game));
|
||||
source.getEffects().setTargetPointer(new FixedTargets(cardsToExile.getCards(game), game));
|
||||
}
|
||||
paid = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,10 +50,10 @@ public class ConditionalAsThoughEffect extends AsThoughEffectImpl {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
conditionState = condition.apply(game, source);
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.apply(game, source);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.apply(game, source);
|
||||
}
|
||||
if (!conditionState && effect.getDuration() == Duration.OneUse) {
|
||||
|
|
@ -69,10 +69,10 @@ public class ConditionalAsThoughEffect extends AsThoughEffectImpl {
|
|||
public boolean applies(UUID sourceId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
conditionState = condition.apply(game, source);
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.applies(sourceId, affectedAbility, source, game, playerId);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.applies(sourceId, affectedAbility, source, game, playerId);
|
||||
}
|
||||
return false;
|
||||
|
|
@ -82,10 +82,10 @@ public class ConditionalAsThoughEffect extends AsThoughEffectImpl {
|
|||
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
|
||||
conditionState = condition.apply(game, source);
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.applies(sourceId, source, affectedControllerId, game);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.applies(sourceId, source, affectedControllerId, game);
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -81,10 +81,10 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
|
|||
} else {
|
||||
condition = baseCondition;
|
||||
}
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
effect.init(source, game);
|
||||
if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
otherwiseEffect.init(source, game);
|
||||
}
|
||||
initDone = true;
|
||||
|
|
@ -122,10 +122,10 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
boolean conditionState = condition != null && condition.apply(game, source);
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.apply(game, source);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.apply(game, source);
|
||||
}
|
||||
switch (effect.getDuration()) {
|
||||
|
|
|
|||
|
|
@ -52,10 +52,10 @@ public class ConditionalContinuousRuleModifyingEffect extends ContinuousRuleModi
|
|||
} else {
|
||||
condition = baseCondition;
|
||||
}
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
effect.init(source, game);
|
||||
if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
otherwiseEffect.init(source, game);
|
||||
}
|
||||
initDone = true;
|
||||
|
|
@ -82,10 +82,10 @@ public class ConditionalContinuousRuleModifyingEffect extends ContinuousRuleModi
|
|||
init(source, game);
|
||||
}
|
||||
if (condition.apply(game, source)) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.applies(event, source, game);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.applies(event, source, game);
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -51,10 +51,10 @@ public class ConditionalCostModificationEffect extends CostModificationEffectImp
|
|||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||
conditionState = condition.apply(game, source);
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.apply(game, source, abilityToModify);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.apply(game, source, abilityToModify);
|
||||
}
|
||||
if (!conditionState && effect.getDuration() == Duration.OneUse) {
|
||||
|
|
@ -70,10 +70,10 @@ public class ConditionalCostModificationEffect extends CostModificationEffectImp
|
|||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
conditionState = condition.apply(game, source);
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.applies(abilityToModify, source, game);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.applies(abilityToModify, source, game);
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public class ConditionalOneShotEffect extends OneShotEffect {
|
|||
if (toApply.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
toApply.setTargetPointer(this.targetPointer);
|
||||
toApply.setTargetPointer(this.getTargetPointer().copy());
|
||||
toApply.stream().forEach(effect -> effect.apply(game, source));
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,10 +68,10 @@ public class ConditionalPreventionEffect extends PreventionEffectImpl {
|
|||
} else {
|
||||
condition = baseCondition;
|
||||
}
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
effect.init(source, game);
|
||||
if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
otherwiseEffect.init(source, game);
|
||||
}
|
||||
initDone = true;
|
||||
|
|
@ -80,10 +80,10 @@ public class ConditionalPreventionEffect extends PreventionEffectImpl {
|
|||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.replaceEvent(event, source, game);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.replaceEvent(event, source, game);
|
||||
}
|
||||
|
||||
|
|
@ -110,10 +110,10 @@ public class ConditionalPreventionEffect extends PreventionEffectImpl {
|
|||
}
|
||||
conditionState = condition.apply(game, source);
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.applies(event, source, game);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.applies(event, source, game);
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -60,10 +60,10 @@ public class ConditionalReplacementEffect extends ReplacementEffectImpl {
|
|||
} else {
|
||||
condition = baseCondition;
|
||||
}
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
effect.init(source, game);
|
||||
if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
otherwiseEffect.init(source, game);
|
||||
}
|
||||
initDone = true;
|
||||
|
|
@ -72,10 +72,10 @@ public class ConditionalReplacementEffect extends ReplacementEffectImpl {
|
|||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.replaceEvent(event, source, game);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.replaceEvent(event, source, game);
|
||||
}
|
||||
if (!conditionState && effect.getDuration() == Duration.OneUse) {
|
||||
|
|
@ -100,10 +100,10 @@ public class ConditionalReplacementEffect extends ReplacementEffectImpl {
|
|||
}
|
||||
conditionState = condition.apply(game, source);
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.applies(event, source, game);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.applies(event, source, game);
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -64,10 +64,10 @@ public class ConditionalRequirementEffect extends RequirementEffect {
|
|||
} else {
|
||||
condition = baseCondition;
|
||||
}
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
effect.init(source, game);
|
||||
if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
otherwiseEffect.init(source, game);
|
||||
}
|
||||
initDone = true;
|
||||
|
|
@ -80,10 +80,10 @@ public class ConditionalRequirementEffect extends RequirementEffect {
|
|||
}
|
||||
conditionState = condition.apply(game, source);
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.applies(permanent, source, game);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.applies(permanent, source, game);
|
||||
}
|
||||
if (!conditionState && effect.getDuration() == Duration.OneUse) {
|
||||
|
|
|
|||
|
|
@ -63,10 +63,10 @@ public class ConditionalRestrictionEffect extends RestrictionEffect {
|
|||
} else {
|
||||
condition = baseCondition;
|
||||
}
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
effect.init(source, game);
|
||||
if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
otherwiseEffect.init(source, game);
|
||||
}
|
||||
initDone = true;
|
||||
|
|
@ -79,10 +79,10 @@ public class ConditionalRestrictionEffect extends RestrictionEffect {
|
|||
}
|
||||
conditionState = condition.apply(game, source);
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return effect.applies(permanent, source, game);
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
return otherwiseEffect.applies(permanent, source, game);
|
||||
}
|
||||
if (effect.getDuration() == Duration.OneUse) {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public class IntPlusDynamicValue implements DynamicValue {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(baseValue);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(baseValue).append(" plus ");
|
||||
return sb.append(value.toString()).toString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package mage.abilities.dynamicvalue.common;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
|
|
@ -16,12 +15,9 @@ public enum TargetPermanentPowerCount implements DynamicValue {
|
|||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
Permanent sourcePermanent = game.getPermanent(sourceAbility.getFirstTarget());
|
||||
if (sourcePermanent == null) {
|
||||
sourcePermanent = (Permanent) game.getLastKnownInformation(sourceAbility.getFirstTarget(), Zone.BATTLEFIELD);
|
||||
}
|
||||
if (sourcePermanent != null) {
|
||||
return sourcePermanent.getPower().getValue();
|
||||
Permanent targetPermanent = effect.getTargetPointer().getFirstTargetPermanentOrLKI(game, sourceAbility);
|
||||
if (targetPermanent != null) {
|
||||
return targetPermanent.getPower().getValue();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@ public interface ContinuousEffect extends Effect {
|
|||
|
||||
boolean isInactive(Ability source, Game game);
|
||||
|
||||
/**
|
||||
* Init ability data like ZCC or targets on first check in game cycle (ApplyEffects)
|
||||
* <p>
|
||||
* Warning, if you setup target pointer in init then must call super.init at the end (after all choices)
|
||||
*/
|
||||
void init(Ability source, Game game);
|
||||
|
||||
void init(Ability source, Game game, UUID activePlayerId);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package mage.abilities.effects;
|
|||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.CompoundAbility;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.keyword.ChangelingAbility;
|
||||
import mage.constants.*;
|
||||
import mage.filter.Filter;
|
||||
|
|
@ -154,6 +153,11 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
|
|||
this.discarded = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void initNewTargetPointer() {
|
||||
// continuous effect uses init code, so do nothing here
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
init(source, game, game.getActivePlayerId());
|
||||
|
|
@ -161,7 +165,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
|
|||
|
||||
@Override
|
||||
public void init(Ability source, Game game, UUID activePlayerId) {
|
||||
targetPointer.init(game, source);
|
||||
getTargetPointer().init(game, source);
|
||||
// 20210112 - 611.2c
|
||||
// 611.2c If a continuous effect generated by the resolution of a spell or ability modifies the
|
||||
// characteristics or changes the controller of any objects, the set of objects it affects is
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ public abstract class EffectImpl implements Effect {
|
|||
protected EffectType effectType;
|
||||
|
||||
// read related docs about static and dynamic targets in ContinuousEffectImpl.affectedObjectsSet
|
||||
protected TargetPointer targetPointer = new FirstTargetPointer();
|
||||
// warning, do not change it directly, use setTargetPointer instead
|
||||
private TargetPointer targetPointer = new FirstTargetPointer();
|
||||
|
||||
protected String staticText = "";
|
||||
protected Map<String, Object> values;
|
||||
|
|
@ -30,6 +31,8 @@ public abstract class EffectImpl implements Effect {
|
|||
public EffectImpl(Outcome outcome) {
|
||||
this.id = UUID.randomUUID();
|
||||
this.outcome = outcome;
|
||||
|
||||
initNewTargetPointer();
|
||||
}
|
||||
|
||||
protected EffectImpl(final EffectImpl effect) {
|
||||
|
|
@ -48,6 +51,11 @@ public abstract class EffectImpl implements Effect {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init target pointer by default (see TargetPointer for details)
|
||||
*/
|
||||
abstract public void initNewTargetPointer();
|
||||
|
||||
@Override
|
||||
public UUID getId() {
|
||||
return id;
|
||||
|
|
@ -81,7 +89,13 @@ public abstract class EffectImpl implements Effect {
|
|||
|
||||
@Override
|
||||
public Effect setTargetPointer(TargetPointer targetPointer) {
|
||||
if (targetPointer == null) {
|
||||
// first target pointer is default
|
||||
throw new IllegalArgumentException("Wrong code usage: target pointer can't be set to null: " + this);
|
||||
}
|
||||
|
||||
this.targetPointer = targetPointer;
|
||||
initNewTargetPointer();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package mage.abilities.effects;
|
|||
|
||||
import mage.constants.EffectType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.target.targetpointer.TargetPointer;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -15,6 +16,12 @@ public abstract class OneShotEffect extends EffectImpl {
|
|||
this.effectType = EffectType.ONESHOT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void initNewTargetPointer() {
|
||||
// one short effects don't use init logic
|
||||
this.getTargetPointer().setInitialized();
|
||||
}
|
||||
|
||||
protected OneShotEffect(final OneShotEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
|
@ -24,4 +31,10 @@ public abstract class OneShotEffect extends EffectImpl {
|
|||
super.setText(staticText);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Effect setTargetPointer(TargetPointer targetPointer) {
|
||||
super.setTargetPointer(targetPointer);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class BecomeBlockedTargetEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Set<MageObjectReference> morSet = new HashSet<>();
|
||||
for (UUID targetId : targetPointer.getTargets(game, source)) {
|
||||
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(targetId);
|
||||
if (permanent == null) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ public class ConjureCardEffect extends OneShotEffect {
|
|||
}
|
||||
Set<Card> cards = new HashSet<>();
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Card card = cardInfo.getCard();
|
||||
Card card = cardInfo.createCard();
|
||||
cards.add(card);
|
||||
}
|
||||
game.loadCards(cards, source.getControllerId());
|
||||
|
|
|
|||
|
|
@ -79,12 +79,12 @@ public class CopyTargetSpellEffect extends OneShotEffect {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Spell spell;
|
||||
if (useLKI) {
|
||||
spell = game.getSpellOrLKIStack(targetPointer.getFirst(game, source));
|
||||
spell = game.getSpellOrLKIStack(getTargetPointer().getFirst(game, source));
|
||||
} else {
|
||||
spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
|
||||
spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source));
|
||||
}
|
||||
if (spell == null) {
|
||||
spell = (Spell) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.STACK);
|
||||
spell = (Spell) game.getLastKnownInformation(getTargetPointer().getFirst(game, source), Zone.STACK);
|
||||
}
|
||||
if (spell != null) {
|
||||
spell.createCopyOnStack(game, source, useController ? spell.getControllerId() : source.getControllerId(),
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class CopyTargetStackAbilityEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source));
|
||||
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(getTargetPointer().getFirst(game, source));
|
||||
if (stackAbility == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public class CounterUnlessPaysEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
StackObject spell = game.getStack().getStackObject(targetPointer.getFirst(game, source));
|
||||
StackObject spell = game.getStack().getStackObject(getTargetPointer().getFirst(game, source));
|
||||
if (spell == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect {
|
|||
DelayedTriggeredAbility delayedAbility = ability.copy();
|
||||
if (this.copyTargets) {
|
||||
if (source.getTargets().isEmpty()) {
|
||||
delayedAbility.getEffects().setTargetPointer(targetPointer);
|
||||
delayedAbility.getEffects().setTargetPointer(this.getTargetPointer().copy());
|
||||
} else {
|
||||
delayedAbility.getTargets().addAll(source.getTargets());
|
||||
for (Effect effect : delayedAbility.getEffects()) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import mage.util.functions.CopyTokenFunction;
|
|||
import mage.util.functions.EmptyCopyApplier;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
|
|
@ -273,9 +274,8 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
Permanent tokenPermanent = game.getPermanent(tokenId);
|
||||
if (tokenPermanent != null) {
|
||||
addedTokenPermanents.add(tokenPermanent);
|
||||
// add counters if necessary ie Ochre Jelly
|
||||
if (counter != null
|
||||
&& numberOfCounters > 0) {
|
||||
// TODO: Workaround to add counters to all created tokens, necessary for correct interactions with cards like Chatterfang, Squirrel General and Ochre Jelly / Printlifter Ooze. See #10786
|
||||
if (counter != null && numberOfCounters > 0) {
|
||||
tokenPermanent.addCounters(counter.createInstance(numberOfCounters), source.getControllerId(), source, game);
|
||||
}
|
||||
}
|
||||
|
|
@ -418,7 +418,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
} else {
|
||||
effect = new SacrificeTargetEffect("sacrifice the token copies", source.getControllerId());
|
||||
}
|
||||
effect.setTargetPointer(new FixedTargets(addedTokenPermanents, game));
|
||||
effect.setTargetPointer(new FixedTargets(new ArrayList<>(addedTokenPermanents), game));
|
||||
|
||||
DelayedTriggeredAbility exileAbility;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -9,6 +8,7 @@ import mage.abilities.dynamicvalue.common.StaticValue;
|
|||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.Token;
|
||||
|
|
@ -30,6 +30,8 @@ public class CreateTokenEffect extends OneShotEffect {
|
|||
private final boolean attacking;
|
||||
private String additionalRules;
|
||||
private List<UUID> lastAddedTokenIds = new ArrayList<>();
|
||||
private CounterType counterType;
|
||||
private DynamicValue numberOfCounters;
|
||||
|
||||
public CreateTokenEffect(Token token) {
|
||||
this(token, StaticValue.get(1));
|
||||
|
|
@ -67,9 +69,17 @@ public class CreateTokenEffect extends OneShotEffect {
|
|||
this.tapped = effect.tapped;
|
||||
this.attacking = effect.attacking;
|
||||
this.lastAddedTokenIds.addAll(effect.lastAddedTokenIds);
|
||||
this.counterType = effect.counterType;
|
||||
this.numberOfCounters = effect.numberOfCounters;
|
||||
this.additionalRules = effect.additionalRules;
|
||||
}
|
||||
|
||||
public CreateTokenEffect entersWithCounters(CounterType counterType, DynamicValue numberOfCounters) {
|
||||
this.counterType = counterType;
|
||||
this.numberOfCounters = numberOfCounters;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CreateTokenEffect copy() {
|
||||
return new CreateTokenEffect(this);
|
||||
|
|
@ -80,6 +90,15 @@ public class CreateTokenEffect extends OneShotEffect {
|
|||
int value = amount.calculate(game, source, this);
|
||||
token.putOntoBattlefield(value, game, source, source.getControllerId(), tapped, attacking);
|
||||
this.lastAddedTokenIds = token.getLastAddedTokenIds();
|
||||
// TODO: Workaround to add counters to all created tokens, necessary for correct interactions with cards like Chatterfang, Squirrel General and Ochre Jelly / Printlifter Ooze. See #10786
|
||||
if (counterType != null) {
|
||||
for (UUID tokenId : lastAddedTokenIds) {
|
||||
Permanent tokenPermanent = game.getPermanent(tokenId);
|
||||
if (tokenPermanent != null) {
|
||||
tokenPermanent.addCounters(counterType.createInstance(numberOfCounters.calculate(game, source, this)), source.getControllerId(), source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public class CreateTokenTargetEffect extends OneShotEffect {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
int value = amount.calculate(game, source, this);
|
||||
if (value > 0) {
|
||||
return token.putOntoBattlefield(value, game, source, targetPointer.getFirst(game, source), tapped, attacking, (UUID) getValue("playerToAttack"));
|
||||
return token.putOntoBattlefield(value, game, source, getTargetPointer().getFirst(game, source), tapped, attacking, (UUID) getValue("playerToAttack"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public class DamageAllControlledTargetEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayerOrPlaneswalkerController(targetPointer.getFirst(game, source));
|
||||
Player player = game.getPlayerOrPlaneswalkerController(getTargetPointer().getFirst(game, source));
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public class DamageWithExcessEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source));
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
if (permanent == null || sourceObject == null) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public class DestroyTargetEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
int affectedTargets = 0;
|
||||
for (UUID permanentId : targetPointer.getTargets(game, source)) {
|
||||
for (UUID permanentId : getTargetPointer().getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(permanentId);
|
||||
if (permanent != null
|
||||
&& permanent.isPhasedIn()
|
||||
|
|
|
|||
|
|
@ -46,8 +46,7 @@ public class DetainAllEffect extends OneShotEffect {
|
|||
if (!game.isSimulation()) {
|
||||
game.informPlayers("Detained permanent: " + permanent.getName());
|
||||
}
|
||||
FixedTarget fixedTarget = new FixedTarget(permanent, game);
|
||||
detainedObjects.add(fixedTarget);
|
||||
detainedObjects.add(new FixedTarget(permanent, game));
|
||||
}
|
||||
|
||||
game.addEffect(new DetainAllRestrictionEffect(detainedObjects), source);
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ class DetainRestrictionEffect extends RestrictionEffect {
|
|||
|
||||
@Override
|
||||
public boolean applies(Permanent permanent, Ability source, Game game) {
|
||||
return this.targetPointer.getTargets(game, source).contains(permanent.getId());
|
||||
return this.getTargetPointer().getTargets(game, source).contains(permanent.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public class DoIfClashWonEffect extends OneShotEffect {
|
|||
executingEffect.setTargetPointer(new FixedTarget(opponent.getId()));
|
||||
}
|
||||
} else {
|
||||
executingEffect.setTargetPointer(this.targetPointer);
|
||||
executingEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
}
|
||||
if (executingEffect instanceof OneShotEffect) {
|
||||
return executingEffect.apply(game, source);
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ public class DoIfCostPaid extends OneShotEffect {
|
|||
private void applyEffects(Game game, Ability source, Effects effects) {
|
||||
if (!effects.isEmpty()) {
|
||||
for (Effect effect : effects) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
if (effect instanceof OneShotEffect) {
|
||||
effect.apply(game, source);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect {
|
|||
// do the effects if nobody paid
|
||||
if (doEffect) {
|
||||
for (Effect effect : executingEffects) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
if (effect instanceof OneShotEffect) {
|
||||
result &= effect.apply(game, source);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ public class DoUnlessControllerPaysEffect extends OneShotEffect {
|
|||
// do the effects if not paid
|
||||
if (doEffect) {
|
||||
for (Effect effect : executingEffects) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
if (effect instanceof OneShotEffect) {
|
||||
result &= effect.apply(game, source);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public class DoUnlessTargetPlayerOrTargetsControllerPaysEffect extends OneShotEf
|
|||
// do the effects if not paid
|
||||
if (doEffect) {
|
||||
for (Effect effect : executingEffects) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
if (effect instanceof OneShotEffect) {
|
||||
result &= effect.apply(game, source);
|
||||
} else {
|
||||
|
|
@ -118,7 +118,7 @@ public class DoUnlessTargetPlayerOrTargetsControllerPaysEffect extends OneShotEf
|
|||
}
|
||||
}
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
otherwiseEffect.setTargetPointer(this.getTargetPointer().copy());
|
||||
if (otherwiseEffect instanceof OneShotEffect) {
|
||||
result &= otherwiseEffect.apply(game, source);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public class DoWhenCostPaid extends OneShotEffect {
|
|||
int bookmark = game.bookmarkState();
|
||||
if (cost.pay(source, game, source, player.getId(), false)) {
|
||||
if (ability.getTargets().isEmpty()) {
|
||||
ability.getEffects().setTargetPointer(getTargetPointer());
|
||||
ability.getEffects().setTargetPointer(this.getTargetPointer().copy());
|
||||
}
|
||||
game.fireReflexiveTriggeredAbility(ability, source);
|
||||
player.resetStoredBookmark(game);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public class DontUntapInControllersUntapStepTargetEffect extends ContinuousRuleM
|
|||
if (game.getTurnStepType() != PhaseStep.UNTAP) {
|
||||
return false;
|
||||
}
|
||||
for (UUID targetId : targetPointer.getTargets(game, source)) {
|
||||
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
|
||||
if (!event.getTargetId().equals(targetId)) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* @author PurpleCrowbar
|
||||
*/
|
||||
public class DoubleCountersTargetEffect extends OneShotEffect {
|
||||
|
||||
private final CounterType counterType;
|
||||
|
||||
public DoubleCountersTargetEffect(CounterType counterType) {
|
||||
super(Outcome.Benefit);
|
||||
this.counterType = counterType;
|
||||
staticText = "double the number of " + counterType.getName() + " counters on it";
|
||||
}
|
||||
|
||||
private DoubleCountersTargetEffect(final DoubleCountersTargetEffect effect) {
|
||||
super(effect);
|
||||
this.counterType = effect.counterType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleCountersTargetEffect copy() {
|
||||
return new DoubleCountersTargetEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
return permanent.addCounters(counterType.createInstance(
|
||||
permanent.getCounters(game).getCount(counterType)
|
||||
), source.getControllerId(), source, game);
|
||||
}
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ public class DraftFromSpellbookEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
Set<Card> cards = new HashSet<>();
|
||||
cards.add(cardInfo.getCard());
|
||||
cards.add(cardInfo.createCard());
|
||||
game.loadCards(cards, player.getId());
|
||||
player.moveCards(cards, Zone.HAND, source, game);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public class ExileCardYouChooseTargetOpponentEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Player opponent = game.getPlayer(targetPointer.getFirst(game, source));
|
||||
Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
if (controller == null || opponent == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Cguy7777
|
||||
*/
|
||||
public class ExileCardsFromTopOfLibraryControllerEffect extends OneShotEffect {
|
||||
|
||||
private final int amount;
|
||||
private final boolean toUniqueExileZone;
|
||||
private final boolean faceDown;
|
||||
|
||||
public ExileCardsFromTopOfLibraryControllerEffect(int amount) {
|
||||
this(amount, false);
|
||||
}
|
||||
|
||||
public ExileCardsFromTopOfLibraryControllerEffect(int amount, boolean toUniqueExileZone) {
|
||||
this(amount, toUniqueExileZone, false);
|
||||
}
|
||||
|
||||
public ExileCardsFromTopOfLibraryControllerEffect(int amount, boolean toUniqueExileZone, boolean faceDown) {
|
||||
this(amount, toUniqueExileZone, faceDown, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param amount number of cards to exile
|
||||
* @param toUniqueExileZone moves the card to a source object dependant
|
||||
* unique exile zone, so another effect of the same source object (e.g.
|
||||
* Theater of Horrors) can identify the card
|
||||
* @param faceDown if true, cards are exiled face down
|
||||
* @param withFaceDownReminderText if true, add the reminder text for exiling one face down card
|
||||
*/
|
||||
public ExileCardsFromTopOfLibraryControllerEffect(int amount, boolean toUniqueExileZone, boolean faceDown, boolean withFaceDownReminderText) {
|
||||
super(Outcome.Exile);
|
||||
this.amount = amount;
|
||||
this.toUniqueExileZone = toUniqueExileZone;
|
||||
this.faceDown = faceDown;
|
||||
|
||||
staticText = "exile the top "
|
||||
+ ((amount > 1) ? CardUtil.numberToText(amount) + " cards" : "card")
|
||||
+ " of your library"
|
||||
+ (faceDown ? " face down" : "")
|
||||
+ (withFaceDownReminderText ? ". <i>(You can't look at it.)</i>" : "");
|
||||
}
|
||||
|
||||
protected ExileCardsFromTopOfLibraryControllerEffect(final ExileCardsFromTopOfLibraryControllerEffect effect) {
|
||||
super(effect);
|
||||
this.amount = effect.amount;
|
||||
this.toUniqueExileZone = effect.toUniqueExileZone;
|
||||
this.faceDown = effect.faceDown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UUID exileZoneId = null;
|
||||
String exileZoneName = "";
|
||||
if (toUniqueExileZone) {
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
if (sourceObject == null) {
|
||||
return false;
|
||||
}
|
||||
exileZoneId = CardUtil.getExileZoneId(game, source);
|
||||
exileZoneName = CardUtil.createObjectRealtedWindowTitle(source, game, null);
|
||||
}
|
||||
|
||||
Set<Card> cards = controller.getLibrary().getTopCards(game, amount);
|
||||
if (cards.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean exiledSuccessfully = false;
|
||||
for (Card card : cards) {
|
||||
card.setFaceDown(faceDown, game);
|
||||
exiledSuccessfully |= controller.moveCardsToExile(card, source, game, !faceDown, exileZoneId, exileZoneName);
|
||||
card.setFaceDown(faceDown, game);
|
||||
}
|
||||
return exiledSuccessfully;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExileCardsFromTopOfLibraryControllerEffect copy() {
|
||||
return new ExileCardsFromTopOfLibraryControllerEffect(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ public class ExileFromZoneTargetEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(targetPointer.getFirst(game, source));
|
||||
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import mage.players.Player;
|
|||
import mage.target.targetpointer.FixedTargets;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
|
@ -74,7 +75,7 @@ public class ExileReturnBattlefieldNextEndStepTargetEffect extends OneShotEffect
|
|||
Effect effect = yourControl
|
||||
? new ReturnToBattlefieldUnderYourControlTargetEffect(exiledOnly)
|
||||
: new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, exiledOnly);
|
||||
effect.setTargetPointer(new FixedTargets(new CardsImpl(toExile), game));
|
||||
effect.setTargetPointer(new FixedTargets(toExile, game));
|
||||
game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package mage.abilities.effects.common;
|
|||
|
||||
import mage.ApprovingObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
|
|
@ -52,7 +51,7 @@ public class ExileTargetCardCopyAndCastEffect extends OneShotEffect {
|
|||
player.moveCards(card, Zone.EXILED, source, game);
|
||||
Card cardCopy = game.copyCard(card, source, source.getControllerId());
|
||||
if (optional && !player.chooseUse(outcome, "Cast copy of " +
|
||||
card.getName() + " without paying its mana cost?", source, game)) {
|
||||
card.getName() + (this.noMana ? " without paying its mana cost?" : "?" ), source, game)) {
|
||||
return true;
|
||||
}
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + cardCopy.getId(), Boolean.TRUE);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public class ExileTargetEffect extends OneShotEffect {
|
|||
private String exileZone = null;
|
||||
private UUID exileId = null;
|
||||
private boolean toSourceExileZone = false; // exile the targets to a source object specific exile zone (takes care of zone change counter)
|
||||
private boolean withName = true;
|
||||
private boolean withName = true; // for face down - allows to hide card name in game logs before real face down apply
|
||||
|
||||
public ExileTargetEffect(String effectText) {
|
||||
this();
|
||||
|
|
@ -75,6 +75,7 @@ public class ExileTargetEffect extends OneShotEffect {
|
|||
return this;
|
||||
}
|
||||
|
||||
// TODO: replace to withHiddenName and instructions to use
|
||||
public void setWithName(boolean withName) {
|
||||
this.withName = withName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,14 +55,14 @@ public class ExileTargetForSourceEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
Set<UUID> objectsToMove = new LinkedHashSet<>();
|
||||
if (this.targetPointer instanceof FirstTargetPointer
|
||||
if (this.getTargetPointer() instanceof FirstTargetPointer
|
||||
&& source.getTargets().size() > 1) {
|
||||
for (Target target : source.getTargets()) {
|
||||
objectsToMove.addAll(target.getTargets());
|
||||
}
|
||||
} else {
|
||||
if (this.targetPointer != null && !this.targetPointer.getTargets(game, source).isEmpty()) {
|
||||
objectsToMove.addAll(this.targetPointer.getTargets(game, source));
|
||||
if (!this.getTargetPointer().getTargets(game, source).isEmpty()) {
|
||||
objectsToMove.addAll(this.getTargetPointer().getTargets(game, source));
|
||||
} else {
|
||||
// issue with Madness keyword #6889
|
||||
UUID fixedTargetId = null;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
|
|
@ -14,7 +15,9 @@ import mage.players.Player;
|
|||
import mage.target.targetpointer.FixedTargets;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ExileTopXMayPlayUntilEffect extends OneShotEffect {
|
||||
|
||||
|
|
|
|||
|
|
@ -56,9 +56,7 @@ public class ExileUntilSourceLeavesEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
ExileTargetEffect effect = new ExileTargetEffect(CardUtil.getCardExileZoneId(game, source), permanent.getIdName());
|
||||
if (targetPointer != null) { // Grasping Giant
|
||||
effect.setTargetPointer(targetPointer);
|
||||
}
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
if (effect.apply(game, source)) {
|
||||
game.addDelayedTriggeredAbility(new OnLeaveReturnExiledAbility(returnToZone), source);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ public class FlipCoinEffect extends OneShotEffect {
|
|||
}
|
||||
boolean result = true;
|
||||
for (Effect effect : controller.flipCoin(source, game, true) ? executingEffectsWon : executingEffectsLost) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
if (effect instanceof OneShotEffect) {
|
||||
result &= effect.apply(game, source);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public class GainLifeTargetEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (UUID playerId : targetPointer.getTargets(game, source)) {
|
||||
for (UUID playerId : getTargetPointer().getTargets(game, source)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
player.gainLife(life.calculate(game, source, this), game, source);
|
||||
|
|
|
|||
|
|
@ -32,11 +32,11 @@ public class ImprintTargetEffect extends OneShotEffect {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
|
||||
if (sourcePermanent != null) {
|
||||
Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source));
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (permanent != null) {
|
||||
sourcePermanent.imprint(permanent.getId(), game);
|
||||
} else {
|
||||
Card card = game.getCard(targetPointer.getFirst(game, source));
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (card != null) {
|
||||
sourcePermanent.imprint(card.getId(), game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,3 @@
|
|||
/*
|
||||
*
|
||||
* 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.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import mage.game.Game;
|
|||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* TODO: delete, there are already end turn code with control reset
|
||||
* @author nantuko
|
||||
*/
|
||||
public class LoseControlOnOtherPlayersControllerEffect extends OneShotEffect {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public class LoseHalfLifeTargetEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(targetPointer.getFirst(game, source));
|
||||
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
if (player != null) {
|
||||
Integer amount = (int) Math.ceil(player.getLife() / 2f);
|
||||
if (amount > 0) {
|
||||
|
|
|
|||
|
|
@ -42,11 +42,11 @@ public class LoseLifeTargetControllerEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
MageObject targetCard = targetPointer.getFirstTargetPermanentOrLKI(game, source);
|
||||
MageObject targetCard = getTargetPointer().getFirstTargetPermanentOrLKI(game, source);
|
||||
|
||||
// if target is a countered spell
|
||||
if (targetCard == null) {
|
||||
targetCard = game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.STACK);
|
||||
targetCard = game.getLastKnownInformation(getTargetPointer().getFirst(game, source), Zone.STACK);
|
||||
}
|
||||
|
||||
if (targetCard != null) {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class LoseLifeTargetEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
boolean applied = false;
|
||||
for (UUID playerId : targetPointer.getTargets(game, source)) {
|
||||
for (UUID playerId : getTargetPointer().getTargets(game, source)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null
|
||||
&& player.loseLife(amount.calculate(game, source, this), game, source, false) > 0) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ public class MayTapOrUntapTargetEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent target = game.getPermanent(targetPointer.getFirst(game, source));
|
||||
Permanent target = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (target != null && player != null) {
|
||||
if (target.isTapped()) {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ public class MeldEffect extends OneShotEffect {
|
|||
if (cardInfoList.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
MeldCard meldCard = (MeldCard) cardInfoList.get(0).getCard().copy();
|
||||
MeldCard meldCard = (MeldCard) cardInfoList.get(0).createCard().copy();
|
||||
meldCard.setOwnerId(controller.getId());
|
||||
meldCard.setTopHalfCard(meldWithCard, game);
|
||||
meldCard.setBottomHalfCard(sourceCard, game);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ public class MillCardsTargetEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(targetPointer.getFirst(game, source));
|
||||
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
if (player != null) {
|
||||
player.millCards(numberCards.calculate(game, source, this), source, game);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ public class PreventAllDamageFromChosenSourceToYouEffect extends PreventionEffec
|
|||
|
||||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
|
||||
// be sure to note the target source's zcc, etc, if able.
|
||||
if (targetSource.getFirstTarget() != null) {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ public class PreventDamageBySourceEffect extends PreventionEffectImpl {
|
|||
|
||||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
|
||||
mageObjectReference = new MageObjectReference(target.getFirstTarget(), game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public class PreventDamageToTargetEffect extends PreventionEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return !this.used && super.applies(event, source, game) && event.getTargetId().equals(targetPointer.getFirst(game, source));
|
||||
return !this.used && super.applies(event, source, game) && event.getTargetId().equals(getTargetPointer().getFirst(game, source));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public class PreventNextDamageFromChosenSourceToTargetEffect extends PreventionE
|
|||
|
||||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +57,7 @@ public class PreventNextDamageFromChosenSourceToTargetEffect extends PreventionE
|
|||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (!this.used && super.applies(event, source, game)) {
|
||||
if (event.getTargetId().equals(targetPointer.getFirst(game, source)) && event.getSourceId().equals(targetSource.getFirstTarget())) {
|
||||
if (event.getTargetId().equals(getTargetPointer().getFirst(game, source)) && event.getSourceId().equals(targetSource.getFirstTarget())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public class PreventNextDamageFromChosenSourceToYouEffect extends PreventionEffe
|
|||
|
||||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public class PutOnLibraryTargetEffect extends OneShotEffect {
|
|||
if (controller != null) {
|
||||
List<Card> cards = new ArrayList<>();
|
||||
List<Permanent> permanents = new ArrayList<>();
|
||||
for (UUID targetId : targetPointer.getTargets(game, source)) {
|
||||
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
|
||||
switch (game.getState().getZone(targetId)) {
|
||||
case BATTLEFIELD:
|
||||
Permanent permanent = game.getPermanent(targetId);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.RedirectionEffect;
|
||||
import mage.constants.Duration;
|
||||
import mage.game.Game;
|
||||
|
|
@ -14,7 +15,6 @@ public class RedirectDamageFromSourceToTargetEffect extends RedirectionEffect {
|
|||
|
||||
public RedirectDamageFromSourceToTargetEffect(Duration duration, int amountToRedirect, UsageType usageType) {
|
||||
super(duration, amountToRedirect, usageType);
|
||||
staticText = "The next " + amountToRedirect + " damage that would be dealt to {this} this turn is dealt to target creature you control instead.";
|
||||
}
|
||||
|
||||
protected RedirectDamageFromSourceToTargetEffect(final RedirectDamageFromSourceToTargetEffect effect) {
|
||||
|
|
@ -39,4 +39,14 @@ public class RedirectDamageFromSourceToTargetEffect extends RedirectionEffect {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
return "the next " + amountToRedirect + " damage that would be dealt to {this} this turn is dealt to "
|
||||
+ getTargetPointer().describeTargets(mode.getTargets(), "that creature")
|
||||
+ " instead";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public class RegenerateSourceWithReflexiveEffect extends RegenerateSourceEffect
|
|||
if (super.replaceEvent(event, source, game)) {
|
||||
if (this.setReflexiveTarget) {
|
||||
reflexive.getEffects().setTargetPointer(
|
||||
new FixedTarget(targetPointer.getFirst(game, source), game)
|
||||
new FixedTarget(getTargetPointer().getFirst(game, source), game)
|
||||
);
|
||||
}
|
||||
game.fireReflexiveTriggeredAbility(reflexive, source);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public class RegenerateTargetEffect extends ReplacementEffectImpl {
|
|||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
RegenerateSourceEffect.initRegenerationShieldInfo(game, source, targetPointer.getFirst(game, source));
|
||||
RegenerateSourceEffect.initRegenerationShieldInfo(game, source, getTargetPointer().getFirst(game, source));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -37,7 +37,7 @@ public class RegenerateTargetEffect extends ReplacementEffectImpl {
|
|||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
//20110204 - 701.11
|
||||
Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source));
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (permanent != null && permanent.regenerate(source, game)) {
|
||||
this.used = true;
|
||||
return true;
|
||||
|
|
@ -53,7 +53,7 @@ public class RegenerateTargetEffect extends ReplacementEffectImpl {
|
|||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
//20110204 - 701.11c - event.getAmount() is used to signal if regeneration is allowed
|
||||
return event.getAmount() == 0 && event.getTargetId().equals(targetPointer.getFirst(game, source)) && !this.used;
|
||||
return event.getAmount() == 0 && event.getTargetId().equals(getTargetPointer().getFirst(game, source)) && !this.used;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
|
|||
private final Zone returnToZone;
|
||||
private boolean pluralCards;
|
||||
private boolean pluralOwners;
|
||||
private boolean putPhrasing;
|
||||
|
||||
/**
|
||||
* @param zone Zone the card should return to
|
||||
|
|
@ -31,6 +32,7 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
|
|||
this.returnToZone = zone;
|
||||
this.pluralCards = false;
|
||||
this.pluralOwners = false;
|
||||
this.putPhrasing = false;
|
||||
updateText();
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +41,7 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
|
|||
this.returnToZone = effect.returnToZone;
|
||||
this.pluralCards = effect.pluralCards;
|
||||
this.pluralOwners = effect.pluralOwners;
|
||||
this.putPhrasing = effect.putPhrasing;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -77,16 +80,22 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
|
|||
return true;
|
||||
}
|
||||
|
||||
public ReturnFromExileForSourceEffect withText(boolean pluralCards, boolean pluralOwners) {
|
||||
public ReturnFromExileForSourceEffect withText(boolean pluralCards, boolean pluralOwners, boolean putPhrasing) {
|
||||
this.pluralCards = pluralCards;
|
||||
this.pluralOwners = pluralOwners;
|
||||
this.putPhrasing = putPhrasing;
|
||||
updateText();
|
||||
return this;
|
||||
}
|
||||
|
||||
private void updateText() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("return the exiled ").append(pluralCards ? "cards" : "card").append(" to ");
|
||||
if (putPhrasing) {
|
||||
sb.append("put ").append(pluralCards ? "all cards " : "the card ").append("exiled with {this} ");
|
||||
sb.append(returnToZone == Zone.BATTLEFIELD ? "onto " : "into ");
|
||||
} else {
|
||||
sb.append("return the exiled ").append(pluralCards ? "cards" : "card").append(" to ");
|
||||
}
|
||||
if (returnToZone == Zone.BATTLEFIELD) {
|
||||
sb.append("the battlefield under ");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,8 @@ public class ReturnFromGraveyardToBattlefieldTargetEffect extends OneShotEffect
|
|||
|
||||
private final boolean tapped;
|
||||
private final boolean attacking;
|
||||
// If true, creatures are returned to their owner's control.
|
||||
// If false, creatures are returned under the effect's controller control.
|
||||
private final boolean underOwnerControl;
|
||||
|
||||
// Targets are returned under the control of the effect controller (e.g. "under your control")
|
||||
|
||||
public ReturnFromGraveyardToBattlefieldTargetEffect() {
|
||||
this(false);
|
||||
|
|
@ -34,22 +33,17 @@ public class ReturnFromGraveyardToBattlefieldTargetEffect extends OneShotEffect
|
|||
public ReturnFromGraveyardToBattlefieldTargetEffect(boolean tapped) {
|
||||
this(tapped, false);
|
||||
}
|
||||
public ReturnFromGraveyardToBattlefieldTargetEffect(boolean tapped, boolean attacking) {
|
||||
this(tapped, attacking, false);
|
||||
}
|
||||
|
||||
public ReturnFromGraveyardToBattlefieldTargetEffect(boolean tapped, boolean attacking, boolean underOwnerControl) {
|
||||
public ReturnFromGraveyardToBattlefieldTargetEffect(boolean tapped, boolean attacking) {
|
||||
super(Outcome.PutCreatureInPlay);
|
||||
this.tapped = tapped;
|
||||
this.attacking = attacking;
|
||||
this.underOwnerControl = underOwnerControl;
|
||||
}
|
||||
|
||||
protected ReturnFromGraveyardToBattlefieldTargetEffect(final ReturnFromGraveyardToBattlefieldTargetEffect effect) {
|
||||
super(effect);
|
||||
this.tapped = effect.tapped;
|
||||
this.attacking = effect.attacking;
|
||||
this.underOwnerControl = effect.underOwnerControl;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -68,7 +62,7 @@ public class ReturnFromGraveyardToBattlefieldTargetEffect extends OneShotEffect
|
|||
cardsToMove.add(card);
|
||||
}
|
||||
}
|
||||
controller.moveCards(cardsToMove, Zone.BATTLEFIELD, source, game, tapped, false, underOwnerControl, null);
|
||||
controller.moveCards(cardsToMove, Zone.BATTLEFIELD, source, game, tapped, false, false, null);
|
||||
if (attacking) {
|
||||
for (Card card : cardsToMove) {
|
||||
game.getCombat().addAttackingCreature(card.getId(), game);
|
||||
|
|
@ -119,12 +113,7 @@ public class ReturnFromGraveyardToBattlefieldTargetEffect extends OneShotEffect
|
|||
sb.append(" attacking");
|
||||
}
|
||||
if (!yourGrave) {
|
||||
if (underOwnerControl) {
|
||||
sb.append("under their owner's control");
|
||||
}
|
||||
else {
|
||||
sb.append(" under your control");
|
||||
}
|
||||
sb.append(" under your control");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,9 @@ package mage.abilities.effects.common;
|
|||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.counters.Counter;
|
||||
import mage.counters.Counters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.EntersTheBattlefieldEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -19,27 +13,24 @@ import java.util.UUID;
|
|||
*/
|
||||
public class ReturnFromGraveyardToBattlefieldWithCounterTargetEffect extends ReturnFromGraveyardToBattlefieldTargetEffect {
|
||||
|
||||
private final Counter counter;
|
||||
private final boolean additional;
|
||||
private final Counters counters;
|
||||
private final String counterText;
|
||||
|
||||
public ReturnFromGraveyardToBattlefieldWithCounterTargetEffect(Counter counter) {
|
||||
this(counter, false);
|
||||
}
|
||||
|
||||
public ReturnFromGraveyardToBattlefieldWithCounterTargetEffect(Counter counter, boolean additional) {
|
||||
this(counter, additional, false);
|
||||
}
|
||||
|
||||
public ReturnFromGraveyardToBattlefieldWithCounterTargetEffect(Counter counter, boolean additional, boolean underOwnerControl) {
|
||||
super(false, false, underOwnerControl);
|
||||
this.counter = counter;
|
||||
this.additional = additional;
|
||||
super(false);
|
||||
this.counters = new Counters();
|
||||
this.counters.addCounter(counter);
|
||||
this.counterText = makeText(counter, additional);
|
||||
}
|
||||
|
||||
protected ReturnFromGraveyardToBattlefieldWithCounterTargetEffect(final ReturnFromGraveyardToBattlefieldWithCounterTargetEffect effect) {
|
||||
super(effect);
|
||||
this.counter = effect.counter.copy();
|
||||
this.additional = effect.additional;
|
||||
this.counters = effect.counters.copy();
|
||||
this.counterText = effect.counterText;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -50,20 +41,13 @@ public class ReturnFromGraveyardToBattlefieldWithCounterTargetEffect extends Ret
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
|
||||
AddCounterTargetReplacementEffect counterEffect = new AddCounterTargetReplacementEffect(counter);
|
||||
counterEffect.setTargetPointer(new FixedTarget(targetId, game));
|
||||
game.addEffect(counterEffect, source);
|
||||
game.setEnterWithCounters(targetId, counters.copy());
|
||||
}
|
||||
return super.apply(game, source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(super.getText(mode));
|
||||
sb.append(" with ");
|
||||
private String makeText(Counter counter, boolean additional) {
|
||||
StringBuilder sb = new StringBuilder(" with ");
|
||||
if (additional) {
|
||||
if (counter.getCount() == 1) {
|
||||
sb.append("an");
|
||||
|
|
@ -82,52 +66,14 @@ public class ReturnFromGraveyardToBattlefieldWithCounterTargetEffect extends Ret
|
|||
if (counter.getCount() != 1) {
|
||||
sb.append('s');
|
||||
}
|
||||
if (targetPointer.isPlural(mode.getTargets())) {
|
||||
sb.append(" on them");
|
||||
} else {
|
||||
sb.append(" on it");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class AddCounterTargetReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
private final Counter counter;
|
||||
|
||||
public AddCounterTargetReplacementEffect(Counter counter) {
|
||||
super(Duration.EndOfStep, Outcome.BoostCreature);
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
private AddCounterTargetReplacementEffect(final AddCounterTargetReplacementEffect effect) {
|
||||
super(effect);
|
||||
this.counter = effect.counter.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddCounterTargetReplacementEffect copy() {
|
||||
return new AddCounterTargetReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return getTargetPointer().getTargets(game, source).contains(event.getTargetId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget();
|
||||
if (creature == null) {
|
||||
return false;
|
||||
public String getText(Mode mode) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
creature.addCounters(counter.copy(), source.getControllerId(), source, game, event.getAppliedEffects());
|
||||
discard();
|
||||
return false;
|
||||
return super.getText(mode) + counterText + (getTargetPointer().isPlural(mode.getTargets()) ? " on them" : " on it");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public class ReturnToHandChosenControlledPermanentEffect extends ReturnToHandCho
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
this.targetPointer = new FixedTarget(source.getControllerId());
|
||||
this.setTargetPointer(new FixedTarget(source.getControllerId()));
|
||||
return super.apply(game, source);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public class ReturnToHandTargetEffect extends OneShotEffect {
|
|||
}
|
||||
List<UUID> copyIds = new ArrayList<>();
|
||||
Set<Card> cards = new LinkedHashSet<>();
|
||||
for (UUID targetId : targetPointer.getTargets(game, source)) {
|
||||
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
|
||||
MageObject mageObject = game.getObject(targetId);
|
||||
if (mageObject != null) {
|
||||
if (mageObject instanceof Spell
|
||||
|
|
|
|||
|
|
@ -107,7 +107,10 @@ public class RevealAndSeparatePilesEffect extends OneShotEffect {
|
|||
Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, toReveal));
|
||||
controller.revealCards(source, cards, game);
|
||||
|
||||
Player separatingPlayer = this.getExecutingPlayer(controller, game, source, playerWhoSeparates, "separate the revealed cards");
|
||||
Player separatingPlayer = getExecutingPlayer(controller, game, source, playerWhoSeparates, "separate the revealed cards");
|
||||
if (separatingPlayer == null) {
|
||||
return false;
|
||||
}
|
||||
TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, filter);
|
||||
List<Card> pile1 = new ArrayList<>();
|
||||
separatingPlayer.choose(Outcome.Neutral, cards, target, source, game);
|
||||
|
|
@ -117,10 +120,12 @@ public class RevealAndSeparatePilesEffect extends OneShotEffect {
|
|||
.filter(Objects::nonNull)
|
||||
.forEach(pile1::add);
|
||||
cards.removeIf(target.getTargets()::contains);
|
||||
List<Card> pile2 = new ArrayList<>();
|
||||
pile2.addAll(cards.getCards(game));
|
||||
List<Card> pile2 = new ArrayList<>(cards.getCards(game));
|
||||
|
||||
Player choosingPlayer = this.getExecutingPlayer(controller, game, source, playerWhoChooses, "choose the piles");
|
||||
Player choosingPlayer = getExecutingPlayer(controller, game, source, playerWhoChooses, "choose the piles");
|
||||
if (choosingPlayer == null) {
|
||||
return false;
|
||||
}
|
||||
boolean choice = choosingPlayer.choosePile(outcome, "Choose a pile to put into hand.", pile1, pile2, game);
|
||||
|
||||
Zone pile1Zone = choice ? Zone.HAND : targetZone;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class SacrificeControllerEffect extends SacrificeEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
this.targetPointer = new FixedTarget(source.getControllerId());
|
||||
this.setTargetPointer(new FixedTarget(source.getControllerId()));
|
||||
return super.apply(game, source);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public class SacrificeEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
boolean applied = false;
|
||||
for (UUID playerId : targetPointer.getTargets(game, source)) {
|
||||
for (UUID playerId : getTargetPointer().getTargets(game, source)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player == null) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public class SacrificeTargetEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
int affectedTargets = 0;
|
||||
for (UUID permanentId : targetPointer.getTargets(game, source)) {
|
||||
for (UUID permanentId : getTargetPointer().getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(permanentId);
|
||||
if (permanent != null && (playerIdThatHasToSacrifice == null || playerIdThatHasToSacrifice.equals(permanent.getControllerId()))) {
|
||||
permanent.sacrifice(source, game);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ public class SetPlayerLifeTargetEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(targetPointer.getFirst(game, source));
|
||||
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
if (player != null) {
|
||||
player.setLife(amount.calculate(game, source, this), game, source);
|
||||
return true;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue