Merge branch 'master' of https://github.com/magefree/mage.git into MorphRework_v2

# Conflicts:
#	Mage.Sets/src/mage/cards/b/Banefire.java
This commit is contained in:
Steven Knipe 2023-10-01 22:59:27 -07:00
commit 26af2602e4
484 changed files with 6960 additions and 5629 deletions

View file

@ -1480,6 +1480,13 @@ public abstract class AbilityImpl implements Ability {
}
public AbilityImpl copyWithZone(Zone zone) {
if (this instanceof MageSingleton) {
// not safe to change zone for singletons
// in theory there could be some sort of wrapper to effectively change
// the zone here, but currently no use of copyWithZone actually needs
// to change the zone of any existing singleton abilities
return this;
}
AbilityImpl copy = ((AbilityImpl)this.copy());
copy.zone = zone;
copy.newId();

View file

@ -17,10 +17,6 @@ public interface TriggeredAbility extends Ability {
* This check for the relevant event types is called at first to prevent
* further actions if the current event is ignored from this triggered
* ability
*
* @param event
* @param game
* @return
*/
boolean checkEventType(GameEvent event, Game game);
@ -30,10 +26,6 @@ public interface TriggeredAbility extends Ability {
* multiple times. Because some abilities call this to check if an ability
* is relevant (e.g. Torpor Orb), so the method is called multiple times for
* the same event.
*
* @param event
* @param game
* @return
*/
boolean checkTrigger(GameEvent event, Game game);
@ -45,7 +37,10 @@ public interface TriggeredAbility extends Ability {
TriggeredAbility setDoOnlyOnceEachTurn(boolean doOnlyOnce);
TriggeredAbility setReplaceRuleText(boolean replaceRuleText);
/**
* if true, replaces "{this}" with "it" in the effect text
*/
TriggeredAbility withRuleTextReplacement(boolean replaceRuleText);
boolean checkInterveningIfClause(Game game);

View file

@ -27,7 +27,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
protected boolean leavesTheBattlefieldTrigger;
private boolean triggersOnceEachTurn = false;
private boolean doOnlyOnceEachTurn = false;
protected boolean replaceRuleText = true;
protected boolean replaceRuleText = false; // if true, replace "{this}" with "it" in effect text
private GameEvent triggerEvent = null;
private String triggerPhrase = null;
@ -131,7 +131,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
}
@Override
public TriggeredAbility setReplaceRuleText(boolean replaceRuleText) {
public TriggeredAbility withRuleTextReplacement(boolean replaceRuleText) {
this.replaceRuleText = replaceRuleText;
return this;
}
@ -220,18 +220,8 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
superRule = superRule.replaceFirst(" (become|block|deal|discard|gain|get|lose|mill|sacrifice)s? ", " $1 ");
}
}
if (replaceRuleText
&& triggerPhrase != null
&& triggerPhrase.contains("{this}")
&& !triggerPhrase.contains("other")
&& !triggerPhrase.contains(" of a ")
&& !triggerPhrase.contains(" by a ")
&& !triggerPhrase.contains(" to a ")
&& !triggerPhrase.contains(" blocks a ")
&& (superRule.startsWith("{this}")
|| superRule.startsWith("sacrifice {this}")
)) {
superRule = superRule.replace("{this} ", "it ");
if (replaceRuleText && triggerPhrase != null) {
superRule = superRule.replaceFirst("^(sacrifice )?\\{this\\}", "$1it");
}
sb.append(superRule);
if (triggersOnceEachTurn) {

View file

@ -16,6 +16,7 @@ public class AttacksAloneSourceTriggeredAbility extends TriggeredAbilityImpl {
public AttacksAloneSourceTriggeredAbility(Effect effect) {
super(Zone.BATTLEFIELD, effect);
setTriggerPhrase("Whenever {this} attacks alone, ");
this.replaceRuleText = true; // default true to replace "{this}" with "it"
}
protected AttacksAloneSourceTriggeredAbility(final AttacksAloneSourceTriggeredAbility ability) {

View file

@ -24,6 +24,7 @@ public class AttacksAndIsNotBlockedTriggeredAbility extends TriggeredAbilityImpl
super(Zone.BATTLEFIELD, effect, optional);
this.setTargetPointer = setTargetPointer;
setTriggerPhrase("Whenever {this} attacks and isn't blocked, ");
this.replaceRuleText = true; // default true to replace "{this}" with "it"
}
protected AttacksAndIsNotBlockedTriggeredAbility(final AttacksAndIsNotBlockedTriggeredAbility ability) {

View file

@ -16,6 +16,7 @@ public class AttacksOrBlocksTriggeredAbility extends TriggeredAbilityImpl {
} else {
setTriggerPhrase("Whenever {this} attacks or blocks, ");
}
this.replaceRuleText = true; // default true to replace "{this}" with "it"
}
protected AttacksOrBlocksTriggeredAbility(final AttacksOrBlocksTriggeredAbility ability) {

View file

@ -35,6 +35,7 @@ public class AttacksTriggeredAbility extends TriggeredAbilityImpl {
this.text = text;
this.setTargetPointer = setTargetPointer;
setTriggerPhrase("Whenever {this} attacks, ");
this.replaceRuleText = true; // default true to replace "{this}" with "it"
}
protected AttacksTriggeredAbility(final AttacksTriggeredAbility ability) {

View file

@ -22,6 +22,7 @@ public class BecomesBlockedSourceTriggeredAbility extends TriggeredAbilityImpl {
super(Zone.BATTLEFIELD, effect, optional);
this.setTargetPointer = setTargetPointer;
setTriggerPhrase("Whenever {this} becomes blocked, ");
this.replaceRuleText = true; // default true to replace "{this}" with "it"
}
protected BecomesBlockedSourceTriggeredAbility(final BecomesBlockedSourceTriggeredAbility ability) {

View file

@ -19,6 +19,7 @@ public class BecomesTappedSourceTriggeredAbility extends TriggeredAbilityImpl {
public BecomesTappedSourceTriggeredAbility(Effect effect, boolean isOptional) {
super(Zone.BATTLEFIELD, effect, isOptional);
setTriggerPhrase("Whenever {this} becomes tapped, ");
this.replaceRuleText = true; // default true to replace "{this}" with "it"
}
protected BecomesTappedSourceTriggeredAbility(final BecomesTappedSourceTriggeredAbility ability) {

View file

@ -0,0 +1,97 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.FilterStackObject;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.StackObject;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
/**
* @author xenohedron
*/
public class BecomesTargetAnyTriggeredAbility extends TriggeredAbilityImpl {
private final FilterPermanent filterTarget;
private final FilterStackObject filterStack;
private final SetTargetPointer setTargetPointer;
public BecomesTargetAnyTriggeredAbility(Effect effect, FilterPermanent filterTarget) {
this(effect, filterTarget, StaticFilters.FILTER_SPELL_OR_ABILITY_A);
}
/**
* "Whenever [a filterTarget] becomes the target of [a filterStack], [effect]"
* @param effect defaults to SetTargetPointer.PERMANENT
* @param filterTarget permanents to check being targetted
* @param filterStack spells/abilities to check targeting
*/
public BecomesTargetAnyTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack) {
this(effect, filterTarget, filterStack, SetTargetPointer.PERMANENT, false);
}
public BecomesTargetAnyTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack,
SetTargetPointer setTargetPointer, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
this.filterTarget = filterTarget;
this.filterStack = filterStack;
this.setTargetPointer = setTargetPointer;
setTriggerPhrase("Whenever " + filterTarget.getMessage() + " becomes the target of "
+ filterStack.getMessage() + ", ");
}
protected BecomesTargetAnyTriggeredAbility(final BecomesTargetAnyTriggeredAbility ability) {
super(ability);
this.filterTarget = ability.filterTarget;
this.filterStack = ability.filterStack;
this.setTargetPointer = ability.setTargetPointer;
}
@Override
public BecomesTargetAnyTriggeredAbility copy() {
return new BecomesTargetAnyTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGETED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent == null || !filterTarget.match(permanent, getControllerId(), this, game)) {
return false;
}
StackObject targetingObject = CardUtil.getTargetingStackObject(event, game);
if (targetingObject == null || !filterStack.match(targetingObject, getControllerId(), this, game)) {
return false;
}
if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) {
return false;
}
switch (setTargetPointer) {
case PERMANENT:
this.getAllEffects().setTargetPointer(new FixedTarget(permanent.getId(), game));
break;
case PLAYER:
this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getControllerId(), game));
break;
case SPELL:
this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId()));
break;
case NONE:
break;
default:
throw new IllegalArgumentException("Unsupported SetTargetPointer in BecomesTargetAnyTriggeredAbility");
}
return true;
}
}

View file

@ -2,6 +2,7 @@ package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterStackObject;
import mage.filter.StaticFilters;
@ -9,6 +10,8 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.StackObject;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
/**
* @author LoneFox
@ -16,24 +19,23 @@ import mage.game.permanent.Permanent;
public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl {
private final FilterStackObject filter;
private final SetTargetPointer setTargetPointer;
public BecomesTargetAttachedTriggeredAbility(Effect effect) {
this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A);
this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.NONE, false);
}
public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter) {
this(effect, filter, "creature");
}
public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter, String enchantType) {
super(Zone.BATTLEFIELD, effect);
this.filter = filter.copy();
setTriggerPhrase("When enchanted " + enchantType + " becomes the target of " + filter.getMessage() + ", ");
public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
this.filter = filter;
this.setTargetPointer = setTargetPointer;
setTriggerPhrase("When enchanted creature becomes the target of " + filter.getMessage() + ", ");
}
protected BecomesTargetAttachedTriggeredAbility(final BecomesTargetAttachedTriggeredAbility ability) {
super(ability);
this.filter = ability.filter.copy();
this.filter = ability.filter;
this.setTargetPointer = ability.setTargetPointer;
}
@Override
@ -49,13 +51,28 @@ public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent enchantment = game.getPermanent(sourceId);
StackObject sourceObject = game.getStack().getStackObject(event.getSourceId());
if (enchantment != null && enchantment.getAttachedTo() != null) {
if (event.getTargetId().equals(enchantment.getAttachedTo())
&& filter.match(sourceObject, getControllerId(), this, game)) {
return true;
}
if (enchantment == null || enchantment.getAttachedTo() == null || !event.getTargetId().equals(enchantment.getAttachedTo())) {
return false;
}
return false;
StackObject targetingObject = CardUtil.getTargetingStackObject(event, game);
if (targetingObject == null || !filter.match(targetingObject, getControllerId(), this, game)) {
return false;
}
if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) {
return false;
}
switch (setTargetPointer) {
case PLAYER:
this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getControllerId(), game));
break;
case SPELL:
this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId()));
break;
case NONE:
break;
default:
throw new IllegalArgumentException("Unsupported SetTargetPointer in BecomesTargetAttachedTriggeredAbility");
}
return true;
}
}

View file

@ -1,45 +0,0 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.game.events.GameEvent;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.stack.Spell;
/**
* @author jeffwadsworth
*/
public class BecomesTargetControllerSpellTriggeredAbility extends TriggeredAbilityImpl {
public BecomesTargetControllerSpellTriggeredAbility(Effect effect, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
setTriggerPhrase("When you become the target of a spell, ");
}
protected BecomesTargetControllerSpellTriggeredAbility(final BecomesTargetControllerSpellTriggeredAbility ability) {
super(ability);
}
@Override
public BecomesTargetControllerSpellTriggeredAbility copy() {
return new BecomesTargetControllerSpellTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGETED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(controllerId)) {
if (game.getObject(event.getSourceId()) instanceof Spell) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,84 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.SetTargetPointer;
import mage.filter.FilterPermanent;
import mage.filter.FilterStackObject;
import mage.game.events.GameEvent;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.stack.StackObject;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
/**
* @author xenohedron
*/
public class BecomesTargetControllerTriggeredAbility extends TriggeredAbilityImpl {
private final FilterPermanent filterTarget;
private final FilterStackObject filterStack;
private final SetTargetPointer setTargetPointer;
/**
* Note: filterTarget can be null for "whenever you become the target of...";
* if set, then "whenever you or a [filterTarget] becomes the target of..."
*/
public BecomesTargetControllerTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack,
SetTargetPointer setTargetPointer, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
this.filterTarget = filterTarget;
this.filterStack = filterStack;
this.setTargetPointer = setTargetPointer;
String filterMessage = (filterTarget == null)
? "you become"
: "you or " + filterTarget.getMessage() + " becomes";
setTriggerPhrase("Whenever " + filterMessage + " the target of " + filterStack.getMessage() + ", ");
}
protected BecomesTargetControllerTriggeredAbility(final BecomesTargetControllerTriggeredAbility ability) {
super(ability);
this.filterTarget = ability.filterTarget;
this.filterStack = ability.filterStack;
this.setTargetPointer = ability.setTargetPointer;
}
@Override
public BecomesTargetControllerTriggeredAbility copy() {
return new BecomesTargetControllerTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGETED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!event.getTargetId().equals(getControllerId())) {
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent == null || filterTarget == null || !filterTarget.match(permanent, getControllerId(), this, game)) {
return false;
}
}
StackObject targetingObject = CardUtil.getTargetingStackObject(event, game);
if (targetingObject == null || !filterStack.match(targetingObject, getControllerId(), this, game)) {
return false;
}
if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) {
return false;
}
switch (setTargetPointer) {
case SPELL:
this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId()));
break;
case NONE:
break;
default:
throw new IllegalArgumentException("Unsupported SetTargetPointer in BecomesTargetControllerTriggeredAbility");
}
return true;
}
}

View file

@ -1,83 +0,0 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.StackObject;
import mage.players.Player;
import java.util.*;
/**
* @author weirddan455
*/
public class BecomesTargetOpponentAllTriggeredAbility extends TriggeredAbilityImpl {
private final FilterPermanent filter;
public BecomesTargetOpponentAllTriggeredAbility(Effect effect, boolean optional) {
this(effect, StaticFilters.FILTER_CONTROLLED_A_PERMANENT, optional);
}
public BecomesTargetOpponentAllTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
this.filter = filter;
setTriggerPhrase("Whenever " + filter + " becomes the target of a spell or ability an opponent controls, ");
}
private BecomesTargetOpponentAllTriggeredAbility(final BecomesTargetOpponentAllTriggeredAbility ability) {
super(ability);
this.filter = ability.filter;
}
@Override
public BecomesTargetOpponentAllTriggeredAbility copy() {
return new BecomesTargetOpponentAllTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGETED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
StackObject sourceObject = game.getStack().getStackObject(event.getSourceId());
if (sourceObject == null) {
return false;
}
Player targetter = game.getPlayer(event.getPlayerId());
if (targetter == null || !targetter.hasOpponent(this.controllerId, game)) {
return false;
}
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent == null || !filter.match(permanent, getControllerId(), this, game)) {
return false;
}
// If a spell or ability an opponent controls targets a single permanent you control more than once,
// Battle Mammoths triggered ability will trigger only once.
// However, if a spell or ability an opponent controls targets multiple permanents you control,
// Battle Mammoths triggered ability will trigger once for each of those permanents.
// targetMap - key - targetId, value - Set of stackObject Ids
Map<UUID, Set<UUID>> targetMap = (Map<UUID, Set<UUID>>) game.getState().getValue("targetMap" + this.id);
if (targetMap == null) {
targetMap = new HashMap<>();
}
Set<UUID> sourceObjects = targetMap.get(event.getTargetId());
if (sourceObjects == null) {
sourceObjects = new HashSet<>();
}
if (!sourceObjects.add(sourceObject.getId())) {
return false;
}
targetMap.put(event.getTargetId(), sourceObjects);
game.getState().setValue("targetMap" + this.id, targetMap);
return true;
}
}

View file

@ -0,0 +1,41 @@
package mage.abilities.common;
import mage.abilities.effects.Effect;
import mage.constants.SetTargetPointer;
import mage.filter.FilterStackObject;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher;
/**
* @author xenohedron
*/
public class BecomesTargetSourceFirstTimeTriggeredAbility extends BecomesTargetSourceTriggeredAbility {
public BecomesTargetSourceFirstTimeTriggeredAbility(Effect effect, FilterStackObject filter,
SetTargetPointer setTargetPointer, boolean optional) {
super(effect, filter, setTargetPointer, optional);
this.addWatcher(new NumberOfTimesPermanentTargetedATurnWatcher());
setTriggerPhrase("Whenever {this} becomes the target of " + filter.getMessage() + " for the first time each turn, ");
}
protected BecomesTargetSourceFirstTimeTriggeredAbility(final BecomesTargetSourceFirstTimeTriggeredAbility ability) {
super(ability);
}
@Override
public BecomesTargetSourceFirstTimeTriggeredAbility copy() {
return new BecomesTargetSourceFirstTimeTriggeredAbility(this);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanent(event.getTargetId());
NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class);
if (permanent == null || watcher == null || watcher.numTimesTargetedThisTurn(permanent, game) > 1) {
return false;
}
return super.checkTrigger(event, game);
}
}

View file

@ -0,0 +1,89 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ExileSourceEffect;
import mage.abilities.effects.common.ReturnToHandSourceEffect;
import mage.abilities.effects.common.SacrificeSourceEffect;
import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterStackObject;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.StackObject;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
/**
* @author North
*/
public class BecomesTargetSourceTriggeredAbility extends TriggeredAbilityImpl {
private final FilterStackObject filter;
private final SetTargetPointer setTargetPointer;
public BecomesTargetSourceTriggeredAbility(Effect effect) {
this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A);
}
public BecomesTargetSourceTriggeredAbility(Effect effect, FilterStackObject filter) {
this(effect, filter, SetTargetPointer.NONE, false);
}
public BecomesTargetSourceTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
this.filter = filter;
this.setTargetPointer = setTargetPointer;
boolean textWhen = (effect instanceof SacrificeSourceEffect
|| effect instanceof ReturnToHandSourceEffect
|| effect instanceof ShuffleIntoLibrarySourceEffect
|| effect instanceof ExileSourceEffect);
setTriggerPhrase((textWhen ? "When" : "Whenever") + " {this} becomes the target of " + filter.getMessage() + ", ");
this.replaceRuleText = true; // default true to replace "{this}" with "it"
}
protected BecomesTargetSourceTriggeredAbility(final BecomesTargetSourceTriggeredAbility ability) {
super(ability);
this.filter = ability.filter;
this.setTargetPointer = ability.setTargetPointer;
}
@Override
public BecomesTargetSourceTriggeredAbility copy() {
return new BecomesTargetSourceTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGETED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!event.getTargetId().equals(getSourceId())) {
return false;
}
StackObject targetingObject = CardUtil.getTargetingStackObject(event, game);
if (targetingObject == null || !filter.match(targetingObject, getControllerId(), this, game)) {
return false;
}
if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) {
return false;
}
switch (setTargetPointer) {
case PLAYER:
this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getControllerId(), game));
break;
case SPELL:
this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId()));
break;
case NONE:
break;
default:
throw new IllegalArgumentException("Unsupported SetTargetPointer in BecomesTargetSourceTriggeredAbility");
}
return true;
}
}

View file

@ -1,73 +0,0 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.FilterStackObject;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.StackObject;
import mage.target.targetpointer.FixedTarget;
/**
* @author Susucr
*/
public class BecomesTargetTriggeredAbility extends TriggeredAbilityImpl {
private final FilterPermanent filterTarget;
private final FilterStackObject filterStack;
public BecomesTargetTriggeredAbility(Effect effect, FilterPermanent filterTarget) {
this(effect, filterTarget, StaticFilters.FILTER_SPELL_OR_ABILITY_A);
}
public BecomesTargetTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack) {
this(effect, filterTarget, filterStack, false);
}
public BecomesTargetTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack,
boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
this.filterTarget = filterTarget;
this.filterStack = filterStack;
setTriggerPhrase("Whenever " + filterTarget.getMessage() + " becomes the target of "
+ filterStack.getMessage() + ", ");
}
protected BecomesTargetTriggeredAbility(final BecomesTargetTriggeredAbility ability) {
super(ability);
this.filterTarget = ability.filterTarget;
this.filterStack = ability.filterStack;
}
@Override
public BecomesTargetTriggeredAbility copy() {
return new BecomesTargetTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGETED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
StackObject sourceObject = game.getStack().getStackObject(event.getSourceId());
if (sourceObject == null
|| !filterStack.match(sourceObject, getControllerId(), this, game)) {
return false;
}
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent == null
|| !filterTarget.match(permanent, getControllerId(), this, game)) {
return false;
}
getEffects().setTargetPointer(new FixedTarget(event.getTargetId()));
return true;
}
}

View file

@ -14,13 +14,14 @@ public class CycleTriggeredAbility extends ZoneChangeTriggeredAbility {
public CycleTriggeredAbility(Effect effect, boolean optional) {
super(Zone.ALL, effect, "When you cycle {this}, ", optional);
this.replaceRuleText = true; // default true to replace "{this}" with "it"
}
public CycleTriggeredAbility(Effect effect) {
this(effect, false);
}
public CycleTriggeredAbility(CycleTriggeredAbility ability) {
protected CycleTriggeredAbility(CycleTriggeredAbility ability) {
super(ability);
}

View file

@ -15,6 +15,7 @@ public class DiesSourceTriggeredAbility extends ZoneChangeTriggeredAbility {
public DiesSourceTriggeredAbility(Effect effect, boolean optional) {
super(Zone.BATTLEFIELD, Zone.GRAVEYARD, effect, "When {this} dies, ", optional);
this.replaceRuleText = true; // default true to replace "{this}" with "it"
}
public DiesSourceTriggeredAbility(Effect effect) {

View file

@ -11,27 +11,18 @@ import mage.game.events.GameEvent;
*/
public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl {
protected boolean ignoreRulesGeneration; // use it with custom rules (if you don't want ETB auto-generated text)
protected String etbFlavorWord = null;
public EntersBattlefieldTriggeredAbility(Effect effect) {
this(effect, false);
}
public EntersBattlefieldTriggeredAbility(Effect effect, boolean optional) {
this(effect, optional, false);
}
public EntersBattlefieldTriggeredAbility(Effect effect, boolean optional, boolean ignoreRulesGeneration) {
super(Zone.ALL, effect, optional); // Zone.All because a creature with trigger can be put into play and be sacrificed during the resolution of an effect (discard Obstinate Baloth with Smallpox)
this.ignoreRulesGeneration = ignoreRulesGeneration;
this.replaceRuleText = true; // default true to replace "{this}" with "it"
setTriggerPhrase("When {this} enters the battlefield, ");
}
protected EntersBattlefieldTriggeredAbility(final EntersBattlefieldTriggeredAbility ability) {
super(ability);
this.ignoreRulesGeneration = ability.ignoreRulesGeneration;
this.etbFlavorWord = ability.etbFlavorWord;
}
@Override

View file

@ -12,7 +12,6 @@ public class EntersBattlefieldUntappedTriggeredAbility extends EntersBattlefield
public EntersBattlefieldUntappedTriggeredAbility(Effect effect, boolean optional) {
super(effect, optional);
this.ignoreRulesGeneration = true;
setTriggerPhrase("When {this} enters the battlefield untapped, ");
}
@ -33,4 +32,4 @@ public class EntersBattlefieldUntappedTriggeredAbility extends EntersBattlefield
Permanent permanent = game.getPermanent(event.getTargetId());
return permanent != null && !permanent.isTapped();
}
}
}

View file

@ -1,80 +0,0 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterStackObject;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.StackObject;
import mage.target.targetpointer.FixedTarget;
/**
* @author North
*/
public class SourceBecomesTargetTriggeredAbility extends TriggeredAbilityImpl {
private final FilterStackObject filter;
private final SetTargetPointer setTargetPointer;
public SourceBecomesTargetTriggeredAbility(Effect effect) {
this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A);
}
public SourceBecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter) {
this(effect, filter, SetTargetPointer.NONE);
}
public SourceBecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer) {
this(effect, filter, setTargetPointer, false);
}
public SourceBecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
this.filter = filter;
this.setTargetPointer = setTargetPointer;
setTriggerPhrase("When {this} becomes the target of " + filter.getMessage() + ", ");
}
protected SourceBecomesTargetTriggeredAbility(final SourceBecomesTargetTriggeredAbility ability) {
super(ability);
this.filter = ability.filter;
this.setTargetPointer = ability.setTargetPointer;
}
@Override
public SourceBecomesTargetTriggeredAbility copy() {
return new SourceBecomesTargetTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGETED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
StackObject sourceObject = game.getStack().getStackObject(event.getSourceId());
if (!event.getTargetId().equals(getSourceId())
|| !filter.match(sourceObject, getControllerId(), this, game)) {
return false;
}
switch (setTargetPointer) {
case PLAYER:
this.getEffects().stream()
.forEach(effect -> effect.setTargetPointer(
new FixedTarget(sourceObject.getControllerId(), game)
));
break;
case SPELL:
this.getEffects().stream()
.forEach(effect -> effect.setTargetPointer(
new FixedTarget(sourceObject.getId(), game)
));
break;
}
return true;
}
}

View file

@ -1,82 +0,0 @@
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;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/**
* "Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls,"
* AND
* "Whenever you become the target of a spell or ability an opponent controls,"
*
* @author Alex-Vasile
*/
public class TargetOfOpponentsSpellOrAbilityTriggeredAbility extends TriggeredAbilityImpl {
private boolean onlyController = false;
public TargetOfOpponentsSpellOrAbilityTriggeredAbility(Effect effect) {
this(effect, false, false);
}
public TargetOfOpponentsSpellOrAbilityTriggeredAbility(Effect effect, boolean optional, boolean onlyController) {
super(Zone.BATTLEFIELD, effect, optional);
this.onlyController = onlyController;
if (this.onlyController) {
setTriggerPhrase("Whenever you become the target of a spell or ability an opponent controls, ");
} else {
setTriggerPhrase("Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, ");
}
}
private TargetOfOpponentsSpellOrAbilityTriggeredAbility(final TargetOfOpponentsSpellOrAbilityTriggeredAbility ability) {
super(ability);
this.onlyController = ability.onlyController;
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGETED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Player controller = game.getPlayer(this.getControllerId());
Player targetter = game.getPlayer(event.getPlayerId());
if (controller == null || targetter == null || controller.getId().equals(targetter.getId())) {
return false;
}
// Check if player was targeted
if (controller.getId().equals(event.getTargetId())) {
// Add target for effects which need it (e.g. the counter effect from AmuletOfSafekeeping)
this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId()));
return true;
}
// If only the controller is
if (this.onlyController) {
return false;
}
// Check if permanent was targeted
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent == null || !controller.getId().equals(permanent.getControllerId())) {
return false;
}
// Add target for effects which need it (e.g. the counter effect from AmuletOfSafekeeping)
this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId()));
return true;
}
@Override
public TargetOfOpponentsSpellOrAbilityTriggeredAbility copy() {
return new TargetOfOpponentsSpellOrAbilityTriggeredAbility(this);
}
}

View file

@ -5,7 +5,6 @@ import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.target.targetpointer.FixedTarget;
/**
@ -14,7 +13,7 @@ import mage.target.targetpointer.FixedTarget;
public class TurnedFaceUpSourceTriggeredAbility extends TriggeredAbilityImpl {
private boolean setTargetPointer;
private final boolean setTargetPointer;
public TurnedFaceUpSourceTriggeredAbility(Effect effect) {
this(effect, false);
@ -30,6 +29,7 @@ public class TurnedFaceUpSourceTriggeredAbility extends TriggeredAbilityImpl {
this.setWorksFaceDown(true);
this.setTargetPointer = setTargetPointer;
setTriggerPhrase("When {this} is turned face up, ");
this.replaceRuleText = true;
}
protected TurnedFaceUpSourceTriggeredAbility(final TurnedFaceUpSourceTriggeredAbility ability) {

View file

@ -19,6 +19,9 @@ public class AttachedToMatchesFilterCondition implements Condition {
public AttachedToMatchesFilterCondition(FilterPermanent filter) {
this.filter = filter;
if (filter == null) {
throw new IllegalStateException("Wrong code usage. Filter must be non-nullable.");
}
}
@Override

View file

@ -10,6 +10,10 @@ import mage.game.Game;
/**
* This condition always returns false outside of the combat phase.
*
* The name is bad, but "No Source" is refering to the fact this is to be used outside
* a "Whenever ~ attacks," or similar, where the "defending player" takes another
* meaning. {@link DefendingPlayerControlsNoSourcePredicate} for more info.
*
* @author Susucr
*/
public class DefendingPlayerControlsNoSourceCondition implements Condition {

View file

@ -66,11 +66,6 @@ public class ConditionalContinuousRuleModifyingEffect extends ContinuousRuleModi
return effect.isDiscarded() || (otherwiseEffect != null && otherwiseEffect.isDiscarded());
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
if (effect.checksEventType(event, game)) {

View file

@ -1,5 +1,3 @@
package mage.abilities.effects;
import mage.abilities.Ability;

View file

@ -59,24 +59,11 @@ public abstract class ContinuousRuleModifyingEffectImpl extends ContinuousEffect
this.messageToGameLog = effect.messageToGameLog;
}
/**
* An early check for the event types this effect applies to. This check was
* added to speed up event handling. Once all existing
* ContinuousRuleModifiyingEffects have implemented this method, the method
* should be changed to abstract here or removed.
*
* @param event
* @param game
* @return
*/
@Override
public boolean checksEventType(GameEvent event, Game game) {
return true;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
final public boolean apply(Game game, Ability source) {
// not used in rule modifying effects because it allows or disallows related event only without data
// modification or choose dialogs (see applies method)
throw new IllegalStateException("Wrong code usage. Rules modifying effects don't use apply methods");
}
@Override

View file

@ -29,11 +29,6 @@ public class CantActivateAbilitiesAttachedEffect extends ContinuousRuleModifying
return new CantActivateAbilitiesAttachedEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY;

View file

@ -45,11 +45,6 @@ public class CantBeCounteredControlledEffect extends ContinuousRuleModifyingEffe
return new CantBeCounteredControlledEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.COUNTER;

View file

@ -32,11 +32,6 @@ public class CantBeCounteredSourceEffect extends ContinuousRuleModifyingEffectIm
return new CantBeCounteredSourceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public String getInfoMessage(Ability source, GameEvent event, Game game) {
StackObject stackObject = game.getStack().getStackObject(event.getTargetId());

View file

@ -31,11 +31,6 @@ public class CantBeRegeneratedSourceEffect extends ContinuousRuleModifyingEffect
return new CantBeRegeneratedSourceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.REGENERATE;

View file

@ -27,11 +27,6 @@ public class CantBeRegeneratedTargetEffect extends ContinuousRuleModifyingEffect
return new CantBeRegeneratedTargetEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.REGENERATE;

View file

@ -49,11 +49,6 @@ public class CantBeTargetedAllEffect extends ContinuousRuleModifyingEffectImpl {
return new CantBeTargetedAllEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGET;

View file

@ -44,11 +44,6 @@ public class CantBeTargetedAttachedEffect extends ContinuousRuleModifyingEffectI
return new CantBeTargetedAttachedEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGET;

View file

@ -30,11 +30,6 @@ public class CantBeTargetedCardsGraveyardsEffect extends ContinuousRuleModifying
return new CantBeTargetedCardsGraveyardsEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGET;
@ -46,7 +41,7 @@ public class CantBeTargetedCardsGraveyardsEffect extends ContinuousRuleModifying
StackObject stackObject = game.getStack().getStackObject(event.getSourceId());
if (targetCard != null && stackObject != null) {
Zone zone = game.getState().getZone(targetCard.getId());
if (zone != null && zone == Zone.GRAVEYARD) {
if (zone == Zone.GRAVEYARD) {
return true;
}
}

View file

@ -36,11 +36,6 @@ public class CantBeTargetedSourceEffect extends ContinuousRuleModifyingEffectImp
return new CantBeTargetedSourceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGET;

View file

@ -39,11 +39,6 @@ public class CantBeTargetedTargetEffect extends ContinuousRuleModifyingEffectImp
return new CantBeTargetedTargetEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGET;

View file

@ -47,7 +47,7 @@ public class ChooseModeEffect extends OneShotEffect {
if (sourcePermanent == null) {
sourcePermanent = game.getPermanentEntering(source.getSourceId());
}
if (controller != null) {
if (controller != null && sourcePermanent != null) {
Choice choice = new ChoiceImpl(true);
choice.setMessage(choiceMessage);
choice.getChoices().addAll(modes);

View file

@ -40,7 +40,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
private final List<Permanent> addedTokenPermanents;
private final List<Ability> additionalAbilities;
private final CardType additionalCardType;
private SubType additionalSubType;
private final List<SubType> additionalSubTypes = new ArrayList<>();
private final UUID attackedPlayer;
private UUID attachedTo = null;
private final boolean attacking;
@ -133,7 +133,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
this.addedTokenPermanents = new ArrayList<>(effect.addedTokenPermanents);
this.additionalAbilities = new ArrayList<>(effect.additionalAbilities);
this.additionalCardType = effect.additionalCardType;
this.additionalSubType = effect.additionalSubType;
this.additionalSubTypes.addAll(effect.additionalSubTypes);
this.attackedPlayer = effect.attackedPlayer;
this.attachedTo = effect.attachedTo;
this.attacking = effect.attacking;
@ -236,8 +236,10 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
token.removeAllCreatureTypes();
token.addSubType(onlySubType);
}
if (additionalSubType != null) {
token.addSubType(additionalSubType);
if (!additionalSubTypes.isEmpty()) {
for (SubType additionalSubType : additionalSubTypes) {
token.addSubType(additionalSubType);
}
}
if (color != null) {
token.setColor(color);
@ -322,8 +324,8 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
return addedTokenPermanents;
}
public CreateTokenCopyTargetEffect setAdditionalSubType(SubType additionalSubType) {
this.additionalSubType = additionalSubType;
public CreateTokenCopyTargetEffect withAdditionalSubType(SubType additionalSubType) {
this.additionalSubTypes.add(additionalSubType);
return this;
}

View file

@ -1,12 +1,10 @@
package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
@ -40,12 +38,7 @@ public class DamageAttachedControllerEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
// In the case that the enchantment is blinked
Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
if (enchantment == null) {
// It was not blinked, use the standard method
enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId());
}
Permanent enchantment = source.getSourcePermanentOrLKI(game);
if (enchantment == null) {
return false;
}

View file

@ -1,12 +1,10 @@
package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
@ -47,12 +45,7 @@ public class DamageAttachedEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
// In the case that the enchantment is blinked
Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
if (enchantment == null) {
// It was not blinked, use the standard method
enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId());
}
Permanent enchantment = source.getSourcePermanentOrLKI(game);
if (enchantment == null) {
return false;
}

View file

@ -28,11 +28,6 @@ public class DontUntapInControllersNextUntapStepSourceEffect extends ContinuousR
return new DontUntapInControllersNextUntapStepSourceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public String getInfoMessage(Ability source, GameEvent event, Game game) {
MageObject mageObject = game.getObject(source);
@ -44,7 +39,8 @@ public class DontUntapInControllersNextUntapStepSourceEffect extends ContinuousR
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.UNTAP_STEP || event.getType() == GameEvent.EventType.UNTAP;
return event.getType() == GameEvent.EventType.UNTAP_STEP
|| event.getType() == GameEvent.EventType.UNTAP;
}
@Override

View file

@ -71,11 +71,6 @@ public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousR
return new DontUntapInControllersNextUntapStepTargetEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public String getInfoMessage(Ability source, GameEvent event, Game game) {
MageObject mageObject = game.getObject(source);
@ -88,7 +83,8 @@ public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousR
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.UNTAP_STEP || event.getType() == GameEvent.EventType.UNTAP;
return event.getType() == GameEvent.EventType.UNTAP_STEP
|| event.getType() == GameEvent.EventType.UNTAP;
}
@Override

View file

@ -41,11 +41,6 @@ public class DontUntapInControllersUntapStepAllEffect extends ContinuousRuleModi
return new DontUntapInControllersUntapStepAllEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.UNTAP;

View file

@ -27,11 +27,6 @@ public class DontUntapInControllersUntapStepEnchantedEffect extends ContinuousRu
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public DontUntapInControllersUntapStepEnchantedEffect copy() {
return new DontUntapInControllersUntapStepEnchantedEffect(this);

View file

@ -34,11 +34,6 @@ public class DontUntapInControllersUntapStepSourceEffect extends ContinuousRuleM
return new DontUntapInControllersUntapStepSourceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.UNTAP;

View file

@ -38,11 +38,6 @@ public class DontUntapInControllersUntapStepTargetEffect extends ContinuousRuleM
return new DontUntapInControllersUntapStepTargetEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.UNTAP;

View file

@ -46,11 +46,6 @@ public class DontUntapInPlayersNextUntapStepAllEffect extends ContinuousRuleModi
return new DontUntapInPlayersNextUntapStepAllEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public String getInfoMessage(Ability source, GameEvent event, Game game) {
MageObject mageObject = game.getObject(source);
@ -63,7 +58,8 @@ public class DontUntapInPlayersNextUntapStepAllEffect extends ContinuousRuleModi
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.UNTAP_STEP || event.getType() == GameEvent.EventType.UNTAP;
return event.getType() == GameEvent.EventType.UNTAP_STEP
|| event.getType() == GameEvent.EventType.UNTAP;
}
@Override

View file

@ -75,11 +75,6 @@ class EpicReplacementEffect extends ContinuousRuleModifyingEffectImpl {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public EpicReplacementEffect copy() {
return new EpicReplacementEffect(this);

View file

@ -3,7 +3,6 @@ package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
@ -31,12 +30,7 @@ public class ExileAttachedEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
// The LKI must be used for this step. 608.2g
// In the case that the enchantment is blinked
Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
if (enchantment == null) {
// It was not blinked, use the standard method
enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId());
}
Permanent enchantment = source.getSourcePermanentOrLKI(game);
if (controller != null
&& enchantment != null
&& enchantment.getAttachedTo() != null) {

View file

@ -3,7 +3,6 @@ package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
@ -28,12 +27,7 @@ public class PhaseOutAttachedEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
// In the case that the enchantment is blinked
Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
if (enchantment == null) {
// It was not blinked, use the standard method
enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId());
}
Permanent enchantment = source.getSourcePermanentOrLKI(game);
if (enchantment != null) {
Permanent enchanted = game.getPermanent(enchantment.getAttachedTo());
if (enchanted != null) {

View file

@ -10,8 +10,6 @@ import mage.constants.Zone;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.Target;
import mage.util.CardUtil;
import java.util.*;
@ -63,7 +61,7 @@ public class ReturnToHandTargetEffect extends OneShotEffect {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
return "return " + getTargetPointer().describeTargets(mode.getTargets(), "") +
return "return " + getTargetPointer().describeTargets(mode.getTargets(), "that creature") +
(getTargetPointer().isPlural(mode.getTargets()) ? " to their owners' hands" : " to its owner's hand");
}
}

View file

@ -58,11 +58,15 @@ public class SkipUntapStepEffect extends ContinuousRuleModifyingEffectImpl {
return new SkipUntapStepEffect(this);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.UNTAP_STEP;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
return event.getType() == GameEvent.EventType.UNTAP_STEP
&& controller != null
return controller != null
&& game.getState().getPlayersInRange(controller.getId(), game).contains(event.getPlayerId());
}
}

View file

@ -26,7 +26,7 @@ public class TapEnchantedEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
Permanent permanent = source.getSourcePermanentOrLKI(game);
if (permanent != null) {
Permanent attach = game.getPermanent(permanent.getAttachedTo());
if (attach != null) {

View file

@ -28,7 +28,7 @@ public class UntapAttachedEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
Permanent permanent = source.getSourcePermanentOrLKI(game);
if (permanent != null) {
Permanent attach = game.getPermanent(permanent.getAttachedTo());
if (attach != null) {

View file

@ -28,11 +28,6 @@ public class ChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl {
return new ChooseBlockersEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS;

View file

@ -33,11 +33,6 @@ public class CantCastMoreThanOneSpellEffect extends ContinuousRuleModifyingEffec
return new CantCastMoreThanOneSpellEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.CAST_SPELL;

View file

@ -23,11 +23,6 @@ public class DamageCantBePreventedEffect extends ContinuousRuleModifyingEffectIm
return new DamageCantBePreventedEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.PREVENT_DAMAGE;

View file

@ -12,8 +12,6 @@ import mage.constants.SubLayer;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.util.CardUtil;
import java.util.UUID;
@ -128,31 +126,10 @@ public class GainControlTargetEffect extends ContinuousEffectImpl {
@Override
public String getText(Mode mode) {
if (!staticText.isEmpty()) {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
if (mode.getTargets().isEmpty()) {
return "gain control of target permanent";
}
Target target = mode.getTargets().get(0);
StringBuilder sb = new StringBuilder("gain control of ");
if (target.getMaxNumberOfTargets() > 1) {
if (target.getMinNumberOfTargets() == 0) {
sb.append("up to ");
}
sb.append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(" target ");
} else if (!target.getTargetName().startsWith("another")) {
if (target.getMinNumberOfTargets() == 0) {
sb.append("up to one ");
}
sb.append("target ");
}
sb.append(mode.getTargets().get(0).getTargetName());
if (!duration.toString().isEmpty() && duration != Duration.EndOfGame) {
sb.append(' ').append(duration.toString());
}
return sb.toString();
return "gain control of " + getTargetPointer().describeTargets(mode.getTargets(), "that creature")
+ (duration.toString().isEmpty() || duration == Duration.EndOfGame ? "" : " " + duration.toString());
}
}

View file

@ -30,11 +30,6 @@ public class CantCastOrActivateOpponentsYourTurnEffect extends ContinuousRuleMod
return new CantCastOrActivateOpponentsYourTurnEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public String getInfoMessage(Ability source, GameEvent event, Game game) {
Player activePlayer = game.getPlayer(game.getActivePlayerId());

View file

@ -32,11 +32,6 @@ public class CantRegenerateTargetEffect extends ContinuousRuleModifyingEffectImp
return new CantRegenerateTargetEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.REGENERATE;

View file

@ -39,11 +39,6 @@ public class CastOnlyIfYouHaveCastAnotherSpellEffect extends ContinuousRuleModif
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public CastOnlyIfYouHaveCastAnotherSpellEffect copy() {
return new CastOnlyIfYouHaveCastAnotherSpellEffect(this);

View file

@ -1,4 +1,3 @@
package mage.abilities.effects.common.ruleModifying;
import mage.abilities.Ability;
@ -7,17 +6,25 @@ import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
/**
* @author TheElk801
* @author TheElk801, xanderhall
*/
public class CombatDamageByToughnessAllEffect extends ContinuousEffectImpl {
private final FilterCreaturePermanent filter;
public CombatDamageByToughnessAllEffect() {
this(StaticFilters.FILTER_PERMANENT_CREATURE);
}
/**
* "Each [] assigns combat damage equal to its toughness rather than its power"
* @param filter Warning: ObjectSourcePlayer predicates will be ignored
*/
public CombatDamageByToughnessAllEffect(FilterCreaturePermanent filter) {
this(filter, Duration.WhileOnBattlefield);
}
@ -25,9 +32,8 @@ public class CombatDamageByToughnessAllEffect extends ContinuousEffectImpl {
public CombatDamageByToughnessAllEffect(FilterCreaturePermanent filter, Duration duration) {
super(duration, Layer.RulesEffects, SubLayer.NA, Outcome.Neutral);
this.filter = filter;
this.staticText = filter.getMessage() + " assigns combat damage equal to its toughness rather than its power";
this.staticText = "each " + filter.getMessage() + " assigns combat damage equal to its toughness rather than its power";
}
private CombatDamageByToughnessAllEffect(final CombatDamageByToughnessAllEffect effect) {
super(effect);

View file

@ -0,0 +1,59 @@
package mage.abilities.effects.common.ruleModifying;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
/**
* @author TheElk801, xenohedron
*/
public class CombatDamageByToughnessControlledEffect extends ContinuousEffectImpl {
private final FilterCreaturePermanent filter;
public CombatDamageByToughnessControlledEffect() {
this(StaticFilters.FILTER_PERMANENT_CREATURE);
}
/**
* "Each [] you control assigns combat damage equal to its toughness rather than its power"
* @param filter Warning: ObjectSourcePlayer predicates will be ignored
*/
public CombatDamageByToughnessControlledEffect(FilterCreaturePermanent filter) {
this(filter, Duration.WhileOnBattlefield);
}
public CombatDamageByToughnessControlledEffect(FilterCreaturePermanent filter, Duration duration) {
super(duration, Layer.RulesEffects, SubLayer.NA, Outcome.Neutral);
this.filter = filter;
this.staticText = "each " + filter.getMessage()
+ (filter.getMessage().contains("you control") ? "" : " you control")
+ " assigns combat damage equal to its toughness rather than its power";
}
private CombatDamageByToughnessControlledEffect(final CombatDamageByToughnessControlledEffect effect) {
super(effect);
this.filter = effect.filter;
}
@Override
public CombatDamageByToughnessControlledEffect copy() {
return new CombatDamageByToughnessControlledEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
FilterCreaturePermanent filterPermanent = filter.copy();
filterPermanent.add(new ControllerIdPredicate(source.getControllerId()));
game.getCombat().setUseToughnessForDamage(true);
game.getCombat().addUseToughnessForDamageFilter(filterPermanent);
return true;
}
}

View file

@ -104,11 +104,6 @@ class AftermathCantCastFromHand extends ContinuousRuleModifyingEffectImpl {
return new AftermathCantCastFromHand(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.CAST_SPELL;

View file

@ -21,7 +21,7 @@ public class FabricateAbility extends EntersBattlefieldTriggeredAbility {
private final int value;
public FabricateAbility(int value) {
super(new FabricateEffect(value), false, true);
super(new FabricateEffect(value), false);
this.value = value;
}

View file

@ -59,7 +59,8 @@ class SplitSecondEffect extends ContinuousRuleModifyingEffectImpl {
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.CAST_SPELL || event.getType() == GameEvent.EventType.ACTIVATE_ABILITY;
return event.getType() == GameEvent.EventType.CAST_SPELL
|| event.getType() == GameEvent.EventType.ACTIVATE_ABILITY;
}
@Override
@ -76,11 +77,6 @@ class SplitSecondEffect extends ContinuousRuleModifyingEffectImpl {
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public SplitSecondEffect copy() {
return new SplitSecondEffect(this);

View file

@ -11,7 +11,6 @@ import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.StackObject;
import mage.target.Target;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
@ -22,7 +21,6 @@ public class WardAbility extends TriggeredAbilityImpl {
private final Cost cost;
private final DynamicValue genericMana;
private final boolean showAbilityHint;
private final String whereXIs;
@ -74,29 +72,19 @@ public class WardAbility extends TriggeredAbilityImpl {
return event.getType() == GameEvent.EventType.TARGETED;
}
private StackObject getTargetingObject(GameEvent event, Game game) {
for (StackObject stackObject : game.getStack()) {
if (stackObject.getId().equals(event.getSourceId()) || stackObject.getSourceId().equals(event.getSourceId())) {
for (Target target : stackObject.getStackAbility().getTargets()) {
if (target.contains(getSourceId())) {
return stackObject;
}
}
}
}
return null;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!getSourceId().equals(event.getTargetId())) {
return false;
}
StackObject stackObject = getTargetingObject(event, game);
if (stackObject == null || !game.getOpponents(getControllerId()).contains(stackObject.getControllerId())) {
StackObject targetingObject = CardUtil.getTargetingStackObject(event, game);
if (targetingObject == null || !game.getOpponents(getControllerId()).contains(targetingObject.getControllerId())) {
return false;
}
getEffects().setTargetPointer(new FixedTarget(stackObject.getId(), game));
if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) {
return false;
}
getEffects().setTargetPointer(new FixedTarget(targetingObject.getId()));
return true;
}

View file

@ -8,6 +8,7 @@ import mage.abilities.effects.mana.ManaEffect;
import mage.cards.Card;
import mage.choices.Choice;
import mage.constants.ManaType;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterCard;
@ -88,7 +89,7 @@ class AnyColorCardInYourGraveyardManaEffect extends ManaEffect {
choice.setChoice(choice.getChoices().iterator().next());
} else {
Player player = game.getPlayer(source.getControllerId());
if (player == null || !player.choose(outcome, choice, game)) {
if (player == null || !player.choose(Outcome.PutManaInPool, choice, game)) {
return null;
}
}

View file

@ -6,6 +6,7 @@ import mage.abilities.Ability;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.mana.ManaEffect;
import mage.choices.Choice;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
@ -117,7 +118,7 @@ class AnyColorLandsProduceManaEffect extends ManaEffect {
choice.setChoice(choice.getChoices().iterator().next());
} else {
Player player = game.getPlayer(source.getControllerId());
if (player == null || !player.choose(outcome, choice, game)) {
if (player == null || !player.choose(Outcome.PutManaInPool, choice, game)) {
return null;
}
}

View file

@ -7,6 +7,7 @@ import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.mana.ManaEffect;
import mage.choices.Choice;
import mage.constants.ManaType;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
@ -90,7 +91,7 @@ class AnyColorPermanentTypesManaEffect extends ManaEffect {
choice.setChoice(choice.getChoices().iterator().next());
} else {
Player player = game.getPlayer(source.getControllerId());
if (player == null || !player.choose(outcome, choice, game)) {
if (player == null || !player.choose(Outcome.PutManaInPool, choice, game)) {
return null;
}
}

View file

@ -10,6 +10,7 @@ import mage.choices.Choice;
import mage.choices.ChoiceImpl;
import mage.constants.ColoredManaSymbol;
import mage.constants.CommanderCardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterMana;
import mage.game.Game;
@ -132,7 +133,7 @@ class CommanderIdentityManaEffect extends ManaEffect {
if (choice.getChoices().size() == 1) {
choice.setChoice(choice.getChoices().iterator().next());
} else {
if (!controller.choose(outcome, choice, game)) {
if (!controller.choose(Outcome.PutManaInPool, choice, game)) {
return mana;
}
}