mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
Merge pull request #11185 from xenohedron/becomestarget
Rework "whenever ... becomes the target of ..., " abilities
This commit is contained in:
commit
1a13dcd091
114 changed files with 1780 additions and 2865 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Mammoth’s triggered ability will trigger only once.
|
||||
// However, if a spell or ability an opponent controls targets multiple permanents you control,
|
||||
// Battle Mammoth’s 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
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() + ", ");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.filter.predicate.other;
|
||||
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.StackObject;
|
||||
|
||||
/**
|
||||
* Needed for "becomes the target of an Aura spell" to work correctly with e.g. Disturb, Bestow
|
||||
*
|
||||
* @author xenohedron
|
||||
*/
|
||||
public enum AuraSpellPredicate implements Predicate<StackObject> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(StackObject input, Game game) {
|
||||
if (!(input instanceof Spell)) {
|
||||
return false;
|
||||
}
|
||||
SpellAbility spellAbility = ((Spell) input).getSpellAbility();
|
||||
return spellAbility != null && spellAbility.getCharacteristics(game).hasSubtype(SubType.AURA, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "an Aura spell";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,11 @@
|
|||
|
||||
package mage.game.permanent.token;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.BecomesTargetSourceTriggeredAbility;
|
||||
import mage.abilities.effects.common.SacrificeSourceEffect;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.common.SacrificeSourceEffect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
|
|
@ -26,10 +21,11 @@ public final class JaceCunningCastawayIllusionToken extends TokenImpl {
|
|||
power = new MageInt(2);
|
||||
toughness = new MageInt(2);
|
||||
|
||||
this.addAbility(new IllusionTokenTriggeredAbility());
|
||||
this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"), StaticFilters.FILTER_SPELL_A)
|
||||
.setTriggerPhrase("When this creature becomes the target of a spell, "));
|
||||
}
|
||||
|
||||
protected JaceCunningCastawayIllusionToken(final JaceCunningCastawayIllusionToken token) {
|
||||
private JaceCunningCastawayIllusionToken(final JaceCunningCastawayIllusionToken token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
|
|
@ -38,39 +34,3 @@ public final class JaceCunningCastawayIllusionToken extends TokenImpl {
|
|||
return new JaceCunningCastawayIllusionToken(this);
|
||||
}
|
||||
}
|
||||
|
||||
class IllusionTokenTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public IllusionTokenTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new SacrificeSourceEffect(), false);
|
||||
}
|
||||
|
||||
protected IllusionTokenTriggeredAbility(final IllusionTokenTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IllusionTokenTriggeredAbility copy() {
|
||||
return new IllusionTokenTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.TARGETED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
MageObject eventSourceObject = game.getObject(event.getSourceId());
|
||||
if (event.getTargetId().equals(this.getSourceId()) && eventSourceObject instanceof Spell) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "When this creature becomes the target of a spell, sacrifice it.";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import mage.game.permanent.PermanentMeld;
|
|||
import mage.game.permanent.PermanentToken;
|
||||
import mage.game.permanent.token.Token;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.TargetCard;
|
||||
|
|
@ -1009,6 +1010,63 @@ public final class CardUtil {
|
|||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* For finding the spell or ability on the stack for "becomes the target" triggers.
|
||||
* @param event the GameEvent.EventType.TARGETED from checkTrigger() or watch()
|
||||
* @param game the Game from checkTrigger() or watch()
|
||||
* @return the StackObject which targeted the source, or null if not found
|
||||
*/
|
||||
public static StackObject getTargetingStackObject(GameEvent event, Game game) {
|
||||
// In case of multiple simultaneous triggered abilities from the same source,
|
||||
// need to get the actual one that targeted, see #8026, #8378
|
||||
// Also avoids triggering on cancelled selections, see #8802
|
||||
for (StackObject stackObject : game.getStack()) {
|
||||
Ability stackAbility = stackObject.getStackAbility();
|
||||
if (stackAbility == null || !stackAbility.getSourceId().equals(event.getSourceId())) {
|
||||
continue;
|
||||
}
|
||||
for (Target target : stackAbility.getTargets()) {
|
||||
if (target.getTargets().contains(event.getTargetId())) {
|
||||
return stackObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* For ensuring that spells/abilities that target the same object twice only trigger each "becomes the target" ability once.
|
||||
* If this is the first attempt at triggering for a given ability targeting a given object,
|
||||
* this method records that in the game state for later checks by this same method.
|
||||
* @param checkingReference must be unique for each usage (this.id.toString() of the TriggeredAbility, or this.getKey() of the watcher)
|
||||
* @param targetingObject from getTargetingStackObject
|
||||
* @param event the GameEvent.EventType.TARGETED from checkTrigger() or watch()
|
||||
* @param game the Game from checkTrigger() or watch()
|
||||
* @return true if already triggered/watched, false if this is the first/only trigger/watch
|
||||
*/
|
||||
public static boolean checkTargetedEventAlreadyUsed(String checkingReference, StackObject targetingObject, GameEvent event, Game game) {
|
||||
String stateKey = "targetedMap" + checkingReference;
|
||||
// If a spell or ability an opponent controls targets a single permanent you control more than once,
|
||||
// Battle Mammoth's triggered ability will trigger only once.
|
||||
// However, if a spell or ability an opponent controls targets multiple permanents you control,
|
||||
// Battle Mammoth's triggered ability will trigger once for each of those permanents. (2021-02-05)
|
||||
Map<UUID, Set<UUID>> targetMap = (Map<UUID, Set<UUID>>) game.getState().getValue(stateKey);
|
||||
// targetMap: key - targetId; value - Set of stackObject Ids
|
||||
if (targetMap == null) {
|
||||
targetMap = new HashMap<>();
|
||||
} else {
|
||||
targetMap = new HashMap<>(targetMap); // must have new object reference if saved back to game state
|
||||
}
|
||||
Set<UUID> targetingObjects = targetMap.computeIfAbsent(event.getTargetId(), k -> new HashSet<>());
|
||||
if (!targetingObjects.add(targetingObject.getId())) {
|
||||
return true; // The trigger/watcher already recorded that target of the stack object
|
||||
}
|
||||
// Otherwise, store this combination of trigger/watcher + target + stack object
|
||||
targetMap.put(event.getTargetId(), targetingObjects);
|
||||
game.getState().setValue(stateKey, targetMap);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put card to battlefield without resolve (for cheats and tests only)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
|
||||
|
||||
package mage.watchers.common;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import mage.MageObjectReference;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.util.CardUtil;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
|
|
@ -25,24 +26,23 @@ public class NumberOfTimesPermanentTargetedATurnWatcher extends Watcher {
|
|||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.TARGETED) {
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
if (permanent != null) {
|
||||
MageObjectReference mor = new MageObjectReference(permanent, game);
|
||||
int amount = 0;
|
||||
if (permanentsTargeted.containsKey(mor)) {
|
||||
amount = permanentsTargeted.get(mor);
|
||||
}
|
||||
permanentsTargeted.put(mor, ++amount);
|
||||
}
|
||||
if (event.getType() != GameEvent.EventType.TARGETED) {
|
||||
return;
|
||||
}
|
||||
StackObject targetingObject = CardUtil.getTargetingStackObject(event, game);
|
||||
if (targetingObject == null || CardUtil.checkTargetedEventAlreadyUsed(this.getKey(), targetingObject, event, game)) {
|
||||
return;
|
||||
}
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
if (permanent != null) {
|
||||
MageObjectReference mor = new MageObjectReference(permanent, game);
|
||||
int nTimes = permanentsTargeted.getOrDefault(mor, 0);
|
||||
permanentsTargeted.put(mor, nTimes + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean notMoreThanOnceTargetedThisTurn(Permanent creature, Game game) {
|
||||
if (permanentsTargeted.containsKey(new MageObjectReference(creature, game))) {
|
||||
return permanentsTargeted.get(new MageObjectReference(creature, game)) < 2;
|
||||
}
|
||||
return true;
|
||||
public int numTimesTargetedThisTurn(Permanent permanent, Game game) {
|
||||
return permanentsTargeted.getOrDefault(new MageObjectReference(permanent, game), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue