[CMR] implemented Opposition Agent and other changes:

* You may play cards and you may spend mana of any color - refactored cards to use same code;
* Library search event allows to change searching controller (gives full game control for another player);
* Library searched event allows to remove founded cards from result;
* Improved library searching effects with Panglacial Wurm's effects;
* Little changes to test framework;
This commit is contained in:
Oleg Agafonov 2020-11-24 23:49:19 +04:00
parent 13fa98ec44
commit c2a636e2b2
22 changed files with 806 additions and 721 deletions

View file

@ -1,11 +1,8 @@
package mage.cards.b;
import mage.MageInt;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.MenaceAbility;
import mage.abilities.keyword.PartnerAbility;
@ -16,9 +13,7 @@ import mage.game.events.DamagedEvent;
import mage.game.events.DamagedPlayerBatchEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.HashSet;
@ -149,79 +144,8 @@ class BreechesBrazenPlundererEffect extends OneShotEffect {
return false;
}
for (Card card : cards.getCards(game)) {
game.addEffect(new BreechesBrazenPlundererCastEffect(new MageObjectReference(card, game)), source);
game.addEffect(new BreechesBrazenPlundererManaEffect().setTargetPointer(new FixedTarget(card, game)), source);
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn);
}
return true;
}
}
class BreechesBrazenPlundererCastEffect extends AsThoughEffectImpl {
private final MageObjectReference mor;
BreechesBrazenPlundererCastEffect(MageObjectReference mor) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
this.mor = mor;
}
private BreechesBrazenPlundererCastEffect(final BreechesBrazenPlundererCastEffect effect) {
super(effect);
this.mor = effect.mor;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public BreechesBrazenPlundererCastEffect copy() {
return new BreechesBrazenPlundererCastEffect(this);
}
@Override
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
if (mor.getCard(game) == null) {
discard();
return false;
}
return mor.refersTo(sourceId, game) && source.isControlledBy(affectedControllerId);
}
}
class BreechesBrazenPlundererManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
BreechesBrazenPlundererManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.EndOfTurn, Outcome.Benefit);
}
private BreechesBrazenPlundererManaEffect(final BreechesBrazenPlundererManaEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public BreechesBrazenPlundererManaEffect copy() {
return new BreechesBrazenPlundererManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
return source.isControlledBy(affectedControllerId)
&& Objects.equals(objectId, fixedTarget.getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}
}

View file

@ -1,24 +1,21 @@
package mage.cards.c;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetOpponent;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID;
/**
@ -94,78 +91,7 @@ class CovetousUrgeEffect extends OneShotEffect {
if (card.getSpellAbility() == null) {
return true;
}
game.addEffect(new CovetousUrgeCastFromExileEffect(new MageObjectReference(card, game)), source);
game.addEffect(new CovetousUrgeSpendAnyManaEffect().setTargetPointer(new FixedTarget(card, game)), source);
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom);
return true;
}
}
class CovetousUrgeCastFromExileEffect extends AsThoughEffectImpl {
private final MageObjectReference mor;
CovetousUrgeCastFromExileEffect(MageObjectReference mor) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
this.mor = mor;
}
private CovetousUrgeCastFromExileEffect(final CovetousUrgeCastFromExileEffect effect) {
super(effect);
this.mor = effect.mor;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public CovetousUrgeCastFromExileEffect copy() {
return new CovetousUrgeCastFromExileEffect(this);
}
@Override
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
if (mor.getCard(game) == null) {
discard();
return false;
}
return mor.refersTo(sourceId, game) && source.isControlledBy(affectedControllerId);
}
}
class CovetousUrgeSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
CovetousUrgeSpendAnyManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.Custom, Outcome.Benefit);
}
private CovetousUrgeSpendAnyManaEffect(final CovetousUrgeSpendAnyManaEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public CovetousUrgeSpendAnyManaEffect copy() {
return new CovetousUrgeSpendAnyManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
return source.isControlledBy(affectedControllerId)
&& Objects.equals(objectId, fixedTarget.getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}

View file

@ -5,11 +5,7 @@ import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.common.SimpleEvasionAbility;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
@ -17,14 +13,10 @@ import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.PowerPredicate;
import mage.game.ExileZone;
import mage.game.Game;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID;
/**
@ -100,13 +92,8 @@ class DaxosOfMeletisEffect extends OneShotEffect {
// Add effects only if the card has a spellAbility (e.g. not for lands).
if (card.getSpellAbility() != null) {
// allow to cast the card
ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn);
effect.setTargetPointer(new FixedTarget(card, game));
game.addEffect(effect, source);
// and you may spend mana as though it were mana of any color to cast it
effect = new DaxosOfMeletisSpendAnyManaEffect();
effect.setTargetPointer(new FixedTarget(card, game));
game.addEffect(effect, source);
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn);
}
}
return true;
@ -115,79 +102,3 @@ class DaxosOfMeletisEffect extends OneShotEffect {
return false;
}
}
class DaxosOfMeletisCastFromExileEffect extends AsThoughEffectImpl {
private UUID cardId;
private UUID exileId;
public DaxosOfMeletisCastFromExileEffect(UUID cardId, UUID exileId) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
staticText = "Until end of turn, you may cast that card and you may spend mana as though it were mana of any color to cast it";
this.cardId = cardId;
this.exileId = exileId;
}
public DaxosOfMeletisCastFromExileEffect(final DaxosOfMeletisCastFromExileEffect effect) {
super(effect);
this.cardId = effect.cardId;
this.exileId = effect.exileId;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public DaxosOfMeletisCastFromExileEffect copy() {
return new DaxosOfMeletisCastFromExileEffect(this);
}
@Override
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
if (sourceId.equals(cardId) && source.isControlledBy(affectedControllerId)) {
ExileZone exileZone = game.getState().getExile().getExileZone(exileId);
return exileZone != null && exileZone.contains(cardId);
}
return false;
}
}
class DaxosOfMeletisSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
public DaxosOfMeletisSpendAnyManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.EndOfTurn, Outcome.Benefit);
staticText = "you may spend mana as though it were mana of any color to cast it";
}
public DaxosOfMeletisSpendAnyManaEffect(final DaxosOfMeletisSpendAnyManaEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public DaxosOfMeletisSpendAnyManaEffect copy() {
return new DaxosOfMeletisSpendAnyManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
return source.isControlledBy(affectedControllerId)
&& Objects.equals(objectId, fixedTarget.getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}

View file

@ -3,8 +3,9 @@ package mage.cards.d;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.*;
import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.keyword.FirstStrikeAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
@ -16,13 +17,11 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.common.TargetCardInOpponentsGraveyard;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID;
/**
@ -94,13 +93,10 @@ class DireFleetDaredevilEffect extends OneShotEffect {
if (controller.moveCards(targetCard, Zone.EXILED, source, game)) {
targetCard = game.getCard(targetCard.getId());
if (targetCard != null) {
ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn);
effect.setTargetPointer(new FixedTarget(targetCard, game));
game.addEffect(effect, source);
effect = new DireFleetDaredevilSpendAnyManaEffect();
effect.setTargetPointer(new FixedTarget(targetCard, game));
game.addEffect(effect, source);
effect = new DireFleetDaredevilReplacementEffect();
// you may play and spend any mana
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, targetCard, Duration.EndOfTurn);
// exile from graveyard
ContinuousEffect effect = new DireFleetDaredevilReplacementEffect();
effect.setTargetPointer(new FixedTarget(targetCard, game));
game.addEffect(effect, source);
return true;
@ -112,44 +108,6 @@ class DireFleetDaredevilEffect extends OneShotEffect {
}
}
class DireFleetDaredevilSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
public DireFleetDaredevilSpendAnyManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.EndOfTurn, Outcome.Benefit);
staticText = "you may spend mana as though it were mana of any color";
}
public DireFleetDaredevilSpendAnyManaEffect(final DireFleetDaredevilSpendAnyManaEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public DireFleetDaredevilSpendAnyManaEffect copy() {
return new DireFleetDaredevilSpendAnyManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
return source.isControlledBy(affectedControllerId)
&& Objects.equals(objectId, ((FixedTarget) getTargetPointer()).getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}
class DireFleetDaredevilReplacementEffect extends ReplacementEffectImpl {
public DireFleetDaredevilReplacementEffect() {

View file

@ -5,7 +5,8 @@ import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.*;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.combat.GoadTargetEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
@ -13,18 +14,15 @@ import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID;
/**
@ -158,11 +156,8 @@ class GrenzoHavocRaiserEffect extends OneShotEffect {
// Add effects only if the card has a spellAbility (e.g. not for lands).
if (card.getSpellAbility() != null) {
// allow to cast the card
game.addEffect(new GrenzoHavocRaiserCastFromExileEffect(card.getId(), exileId), source);
// and you may spend mana as though it were mana of any color to cast it
ContinuousEffect effect = new GrenzoHavocRaiserSpendAnyManaEffect();
effect.setTargetPointer(new FixedTarget(card.getId()));
game.addEffect(effect, source);
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn);
}
}
return true;
@ -170,80 +165,4 @@ class GrenzoHavocRaiserEffect extends OneShotEffect {
}
return false;
}
}
class GrenzoHavocRaiserCastFromExileEffect extends AsThoughEffectImpl {
private UUID cardId;
private UUID exileId;
public GrenzoHavocRaiserCastFromExileEffect(UUID cardId, UUID exileId) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
staticText = "Until end of turn, you may cast that card and you may spend mana as though it were mana of any color to cast it";
this.cardId = cardId;
this.exileId = exileId;
}
public GrenzoHavocRaiserCastFromExileEffect(final GrenzoHavocRaiserCastFromExileEffect effect) {
super(effect);
this.cardId = effect.cardId;
this.exileId = effect.exileId;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public GrenzoHavocRaiserCastFromExileEffect copy() {
return new GrenzoHavocRaiserCastFromExileEffect(this);
}
@Override
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
if (sourceId.equals(cardId) && source.isControlledBy(affectedControllerId)) {
ExileZone exileZone = game.getState().getExile().getExileZone(exileId);
return exileZone != null && exileZone.contains(cardId);
}
return false;
}
}
class GrenzoHavocRaiserSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
public GrenzoHavocRaiserSpendAnyManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.EndOfTurn, Outcome.Benefit);
staticText = "you may spend mana as though it were mana of any color to cast it";
}
public GrenzoHavocRaiserSpendAnyManaEffect(final GrenzoHavocRaiserSpendAnyManaEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public GrenzoHavocRaiserSpendAnyManaEffect copy() {
return new GrenzoHavocRaiserSpendAnyManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
return source.isControlledBy(affectedControllerId)
&& Objects.equals(objectId, ((FixedTarget) getTargetPointer()).getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}
}

View file

@ -4,9 +4,6 @@ import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.cards.CardImpl;
@ -15,16 +12,12 @@ import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID;
/**
@ -99,89 +92,8 @@ class HostageTakerExileEffect extends OneShotEffect {
// move card to exile
UUID exileId = CardUtil.getCardExileZoneId(game, source);
controller.moveCardToExileWithInfo(card, exileId, permanent.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true);
// allow to cast the card
game.addEffect(new HostageTakerCastFromExileEffect(card.getId(), exileId), source);
// and you may spend mana as though it were mana of any color to cast it
ContinuousEffect effect = new HostageTakerSpendAnyManaEffect();
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
// allow to cast the card and you may spend mana as though it were mana of any color to cast it
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom);
return true;
}
}
class HostageTakerCastFromExileEffect extends AsThoughEffectImpl {
private UUID cardId;
private UUID exileId;
HostageTakerCastFromExileEffect(UUID cardId, UUID exileId) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
this.cardId = cardId;
this.exileId = exileId;
}
private HostageTakerCastFromExileEffect(final HostageTakerCastFromExileEffect effect) {
super(effect);
this.cardId = effect.cardId;
this.exileId = effect.exileId;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public HostageTakerCastFromExileEffect copy() {
return new HostageTakerCastFromExileEffect(this);
}
@Override
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
if (!sourceId.equals(cardId) || !source.isControlledBy(affectedControllerId)) {
return false;
}
ExileZone exileZone = game.getState().getExile().getExileZone(exileId);
if (exileZone != null && exileZone.contains(cardId)) {
return true;
}
discard();
return false;
}
}
class HostageTakerSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
HostageTakerSpendAnyManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.Custom, Outcome.Benefit);
}
private HostageTakerSpendAnyManaEffect(final HostageTakerSpendAnyManaEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public HostageTakerSpendAnyManaEffect copy() {
return new HostageTakerSpendAnyManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
return source.isControlledBy(affectedControllerId)
&& Objects.equals(objectId, fixedTarget.getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}
}

View file

@ -1,7 +1,5 @@
package mage.cards.m;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeSourceCost;
@ -15,17 +13,18 @@ import mage.constants.SuperType;
import mage.constants.Zone;
import mage.target.TargetPlayer;
import java.util.UUID;
/**
*
* @author nantuko
*/
public final class Mindslaver extends CardImpl {
public Mindslaver(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{6}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}");
addSuperType(SuperType.LEGENDARY);
// {4}, {tap}, Sacrifice Mindslaver: You control target player during that player's next turn.
// {4}, {T}, Sacrifice Mindslaver: You control target player during that player's next turn.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ControlTargetPlayerNextTurnEffect(), new GenericManaCost(4));
ability.addCost(new TapSourceCost());
ability.addCost(new SacrificeSourceCost());

View file

@ -0,0 +1,128 @@
package mage.cards.o;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.replacement.YouControlYourOpponentsWhileSearchingReplacementEffect;
import mage.abilities.keyword.FlashAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.LibrarySearchedEvent;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author JayDi85
*/
public final class OppositionAgent extends CardImpl {
public OppositionAgent(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.ROGUE);
this.power = new MageInt(3);
this.toughness = new MageInt(2);
// Flash
this.addAbility(FlashAbility.getInstance());
// You control your opponents while theyre searching their libraries.
this.addAbility(new SimpleStaticAbility(new YouControlYourOpponentsWhileSearchingReplacementEffect()));
// While an opponent is searching their library, they exile each card they find. You may play those cards for as long as they remain exiled, and you may spend mana as though it were mana of any color to cast them.
this.addAbility(new SimpleStaticAbility(new OppositionAgentReplacementEffect()));
}
public OppositionAgent(final OppositionAgent card) {
super(card);
}
@Override
public OppositionAgent copy() {
return new OppositionAgent(this);
}
}
class OppositionAgentReplacementEffect extends ReplacementEffectImpl {
public OppositionAgentReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "While an opponent is searching their library, they exile each card they find. You may play "
+ "those cards for as long as they remain exiled, and you may spend mana as though it were mana "
+ "of any color to cast them";
}
OppositionAgentReplacementEffect(final OppositionAgentReplacementEffect effect) {
super(effect);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
LibrarySearchedEvent se = (LibrarySearchedEvent) event;
// opponent is searching their library
if (!event.getTargetId().equals(event.getPlayerId())) {
return false;
}
Player targetPlayer = game.getPlayer(event.getTargetId());
if (targetPlayer == null) {
return false;
}
Set<Card> cardsToExile = se.getSearchedTarget().getTargets().stream()
.map(id -> targetPlayer.getLibrary().getCard(id, game))
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (cardsToExile.isEmpty()) {
return false;
}
// exile each card they find
targetPlayer.moveCards(cardsToExile, Zone.EXILED, source, game);
cardsToExile.removeIf(card -> game.getState().getZone(card.getId()) != Zone.EXILED);
if (cardsToExile.isEmpty()) {
return false;
}
// remove exiled cards from library searched result
cardsToExile.forEach(card -> se.getSearchedTarget().remove(card.getId()));
// You may play those cards for as long as they remain exiled, and you may spend mana as though it were mana of any color to cast them
for (Card card : cardsToExile) {
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom);
}
// return false all the time
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.LIBRARY_SEARCHED;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
return controller != null && game.isOpponent(controller, event.getPlayerId());
}
@Override
public OppositionAgentReplacementEffect copy() {
return new OppositionAgentReplacementEffect(this);
}
}

View file

@ -4,8 +4,6 @@ import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
@ -13,12 +11,9 @@ import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.ExileZone;
import mage.game.Game;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID;
/**
@ -84,11 +79,8 @@ class StolenStrategyEffect extends OneShotEffect {
// Add effects only if the card has a spellAbility (e.g. not for lands).
if (!card.isLand() && card.getSpellAbility() != null) {
// allow to cast the card
game.addEffect(new StolenStrategyCastFromExileEffect(card.getId(), exileId), source);
// and you may spend mana as though it were mana of any color to cast it
ContinuousEffect effect = new StolenStrategySpendAnyManaEffect();
effect.setTargetPointer(new FixedTarget(card.getId()));
game.addEffect(effect, source);
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn);
}
}
}
@ -98,8 +90,8 @@ class StolenStrategyEffect extends OneShotEffect {
class StolenStrategyCastFromExileEffect extends AsThoughEffectImpl {
private UUID cardId;
private UUID exileId;
private final UUID cardId;
private final UUID exileId;
public StolenStrategyCastFromExileEffect(UUID cardId, UUID exileId) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
@ -133,41 +125,3 @@ class StolenStrategyCastFromExileEffect extends AsThoughEffectImpl {
return false;
}
}
class StolenStrategySpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
public StolenStrategySpendAnyManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.EndOfTurn, Outcome.Benefit);
staticText = "you may spend mana as though it were mana of any color to cast it";
}
public StolenStrategySpendAnyManaEffect(final StolenStrategySpendAnyManaEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public StolenStrategySpendAnyManaEffect copy() {
return new StolenStrategySpendAnyManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
return source.isControlledBy(affectedControllerId)
&& Objects.equals(objectId, fixedTarget.getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}

View file

@ -4,9 +4,6 @@ import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.abilities.keyword.BountyAbility;
@ -15,16 +12,12 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.counters.CounterType;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.common.TargetOpponentsCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID;
/**
@ -89,11 +82,8 @@ class TobiasBeckettEffect extends OneShotEffect {
// Add effects only if the card has a spellAbility (e.g. not for lands).
if (card.getSpellAbility() != null) {
// allow to cast the card
game.addEffect(new TobiasBeckettCastFromExileEffect(card.getId(), exileId), source);
// and you may spend mana as though it were mana of any color to cast it
ContinuousEffect effect = new TobiasBeckettSpendAnyManaEffect();
effect.setTargetPointer(new FixedTarget(card.getId()));
game.addEffect(effect, source);
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom);
}
}
return true;
@ -107,82 +97,4 @@ class TobiasBeckettEffect extends OneShotEffect {
public TobiasBeckettEffect copy() {
return new TobiasBeckettEffect(this);
}
}
// Based on GrenzoHavocRaiserCastFromExileEffect
class TobiasBeckettCastFromExileEffect extends AsThoughEffectImpl {
private UUID cardId;
private UUID exileId;
public TobiasBeckettCastFromExileEffect(UUID cardId, UUID exileId) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit);
staticText = "You may cast that card and you may spend mana as though it were mana of any color to cast it";
this.cardId = cardId;
this.exileId = exileId;
}
public TobiasBeckettCastFromExileEffect(final TobiasBeckettCastFromExileEffect effect) {
super(effect);
this.cardId = effect.cardId;
this.exileId = effect.exileId;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public TobiasBeckettCastFromExileEffect copy() {
return new TobiasBeckettCastFromExileEffect(this);
}
@Override
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
if (sourceId.equals(cardId) && source.isControlledBy(affectedControllerId)) {
ExileZone exileZone = game.getState().getExile().getExileZone(exileId);
return exileZone != null && exileZone.contains(cardId);
}
return false;
}
}
// Based on GrenzoHavocRaiserSpendAnyManaEffect
class TobiasBeckettSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
public TobiasBeckettSpendAnyManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.EndOfTurn, Outcome.Benefit);
staticText = "you may spend mana as though it were mana of any color to cast it";
}
public TobiasBeckettSpendAnyManaEffect(final TobiasBeckettSpendAnyManaEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public TobiasBeckettSpendAnyManaEffect copy() {
return new TobiasBeckettSpendAnyManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
return source.isControlledBy(affectedControllerId)
&& Objects.equals(objectId, fixedTarget.getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}
}

View file

@ -1,5 +1,6 @@
package mage.cards.w;
import mage.ApprovingObject;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
@ -22,9 +23,9 @@ import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetOpponent;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.UUID;
import mage.ApprovingObject;
/**
* @author L_J
@ -95,13 +96,7 @@ class WordOfCommandEffect extends OneShotEffect {
}
// You control that player until Word of Command finishes resolving
controller.controlPlayersTurn(game, targetPlayer.getId());
while (controller.canRespond()) {
if (controller.chooseUse(Outcome.Benefit, "Resolve " + sourceObject.getLogName() + " now" + (card != null ? " and play " + card.getLogName() : "") + '?', source, game)) {
// this is used to give the controller a little space to utilize their player controlling effect (look at face down creatures, hand, etc.)
break;
}
}
CardUtil.takeControlUnderPlayerStart(game, controller, targetPlayer, true);
// The player plays that card if able
if (card != null) {
@ -156,10 +151,7 @@ class WordOfCommandEffect extends OneShotEffect {
if (wordOfCommand != null) {
wordOfCommand.setCommandedBy(controller.getId()); // You control the player until Word of Command finishes resolving
} else {
targetPlayer.setGameUnderYourControl(true, false);
if (!targetPlayer.getTurnControlledBy().equals(controller.getId())) {
controller.getPlayersUnderYourControl().remove(targetPlayer.getId());
}
CardUtil.takeControlUnderPlayerEnd(game, controller, targetPlayer);
}
return true;
}

View file

@ -456,6 +456,8 @@ public final class CommanderLegends extends ExpansionSet {
cards.add(new SetCardInfo("On Serra's Wings", 380, Rarity.UNCOMMON, mage.cards.o.OnSerrasWings.class));
cards.add(new SetCardInfo("Opal Palace", 352, Rarity.COMMON, mage.cards.o.OpalPalace.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Opal Palace", 707, Rarity.COMMON, mage.cards.o.OpalPalace.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Opposition Agent", 141, Rarity.RARE, mage.cards.o.OppositionAgent.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Opposition Agent", 651, Rarity.RARE, mage.cards.o.OppositionAgent.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Open the Armory", 34, Rarity.UNCOMMON, mage.cards.o.OpenTheArmory.class));
cards.add(new SetCardInfo("Ordeal of Nylea", 247, Rarity.UNCOMMON, mage.cards.o.OrdealOfNylea.class));
cards.add(new SetCardInfo("Oreskos Explorer", 381, Rarity.UNCOMMON, mage.cards.o.OreskosExplorer.class));

View file

@ -120,6 +120,10 @@ public class SpendOtherManaTest extends CardTestPlayerBase {
// You may cast that card as long as it remains exiled, and you may spend mana as though it were mana of any type to cast that spell.
addCard(Zone.HAND, playerA, "Hostage Taker"); // {2}{U}{B}
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}.");
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}.");
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}.");
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}.");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hostage Taker");
addTarget(playerA, "Silvercoat Lion");

View file

@ -0,0 +1,254 @@
package org.mage.test.cards.control;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.replacement.YouControlYourOpponentsWhileSearchingReplacementEffect;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author JayDi85
*/
public class TakeControlWhileSearchingLibraryTest extends CardTestPlayerBase {
@Test
public void test_SimpleSearchingLibrary_Normal() {
removeAllCardsFromLibrary(playerA);
removeAllCardsFromLibrary(playerB);
addCard(Zone.LIBRARY, playerA, "Balduvian Bears", 1);
addCard(Zone.LIBRARY, playerB, "Kitesail Corsair", 1);
//
// Search your library for up to three creature cards and put them into your graveyard. Then shuffle your library.
addCard(Zone.HAND, playerA, "Buried Alive", 1); // {2}{B}
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
// before
checkGraveyardCount("before a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 0);
checkGraveyardCount("before b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Kitesail Corsair", 0);
// search as normal
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 3);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
addTarget(playerA, "Balduvian Bears");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
// after
checkGraveyardCount("after a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
checkGraveyardCount("after b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Kitesail Corsair", 0);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertAllCommandsUsed();
}
@Test
public void test_SimpleSearchingLibrary_TakeControl() {
removeAllCardsFromLibrary(playerA);
removeAllCardsFromLibrary(playerB);
addCard(Zone.LIBRARY, playerA, "Balduvian Bears", 1);
addCard(Zone.LIBRARY, playerB, "Kitesail Corsair", 1);
//
// Search your library for up to three creature cards and put them into your graveyard. Then shuffle your library.
addCard(Zone.HAND, playerA, "Buried Alive", 1); // {2}{B}
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
//
// You control your opponents while theyre searching their libraries.
addCustomCardWithAbility("control", playerB, new SimpleStaticAbility(
new YouControlYourOpponentsWhileSearchingReplacementEffect())
);
//
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
// before
checkGraveyardCount("before a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 0);
checkGraveyardCount("before b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Kitesail Corsair", 0);
// search under control of B
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 3);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
setChoice(playerB, "Yes"); // continue
addTarget(playerB, "Balduvian Bears"); // player B must take control for searching
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
// after
checkGraveyardCount("after a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
checkGraveyardCount("after b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Kitesail Corsair", 0);
// check that control returned
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt");
addTarget(playerA, playerB);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertAllCommandsUsed();
}
@Test
public void test_CastCardWhileSearchingLibrary_Normal() {
removeAllCardsFromLibrary(playerA);
removeAllCardsFromLibrary(playerB);
addCard(Zone.LIBRARY, playerA, "Balduvian Bears", 1);
addCard(Zone.LIBRARY, playerB, "Kitesail Corsair", 1);
//
// While you're searching your library, you may cast Panglacial Wurm from your library.
addCard(Zone.LIBRARY, playerA, "Panglacial Wurm", 1); // {5}{G}{G}
addCard(Zone.BATTLEFIELD, playerA, "Panglacial Wurm", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 7);
//
// Search your library for up to three creature cards and put them into your graveyard. Then shuffle your library.
addCard(Zone.HAND, playerA, "Buried Alive", 1); // {2}{B}
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
//
// before
checkGraveyardCount("before a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 0);
checkGraveyardCount("before b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Kitesail Corsair", 0);
// search as normal and cast
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 3);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
setChoice(playerA, "Yes"); // yes, try to cast a creature card from lib
setChoice(playerA, "Panglacial Wurm"); // try to cast
addTarget(playerA, "Balduvian Bears"); // choice for searching
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
// after
checkGraveyardCount("after a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
checkGraveyardCount("after b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Kitesail Corsair", 0);
checkPermanentCount("must cast Panglacial Wurm", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Panglacial Wurm", 2);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertAllCommandsUsed();
}
@Test
@Ignore // unsupported by unit tests, see test_UnderControlMustUseTestCommandsCorrectrly
public void test_CastCardWhileSearchingLibrary_TakeControl() {
/*
[test control manually]
// use case: login by p1 and p2 clients, cast Buried Alive -> control under p2 -> try cast Panglacial Wurm and search for Balduvian Bears
library:p1:Balduvian Bears:1
library:p2:Kitesail Corsair:1
library:p1:Panglacial Wurm:1
battlefield:p1:Panglacial Wurm:1
battlefield:p1:Forest:7
hand:p1:Buried Alive:1
battlefield:p1:Swamp:3
battlefield:p2:Opposition Agent:1
*/
removeAllCardsFromLibrary(playerA);
removeAllCardsFromLibrary(playerB);
addCard(Zone.LIBRARY, playerA, "Balduvian Bears", 1);
addCard(Zone.LIBRARY, playerB, "Kitesail Corsair", 1);
//
// While you're searching your library, you may cast Panglacial Wurm from your library.
addCard(Zone.LIBRARY, playerA, "Panglacial Wurm", 1); // {5}{G}{G}
addCard(Zone.BATTLEFIELD, playerA, "Panglacial Wurm", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 7);
//
// Search your library for up to three creature cards and put them into your graveyard. Then shuffle your library.
addCard(Zone.HAND, playerA, "Buried Alive", 1); // {2}{B}
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
// before
checkGraveyardCount("before a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 0);
checkGraveyardCount("before b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Kitesail Corsair", 0);
// search under control of B and cast under B too
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 3);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
setChoice(playerB, "Yes"); // continue
setChoice(playerB, "Yes"); // yes, try to cast a creature card from lib
setChoice(playerB, "Panglacial Wurm"); // try to cast
addTarget(playerB, "Balduvian Bears"); // choice for searching
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
// after
checkGraveyardCount("after a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
checkGraveyardCount("after b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Kitesail Corsair", 0);
checkPermanentCount("must cast Panglacial Wurm", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Panglacial Wurm", 2);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertAllCommandsUsed();
}
@Test
@Ignore
// TODO: current "take player under control" implemented in GameController and HumanPlayer,
// not "game part" - so tests and AI ignore it and must be tested manually
// see another problems with control in HumanPlayer.priority(Game game) and https://github.com/magefree/mage/issues/2088
public void test_UnderControlMustUseTestCommandsCorrectrly() {
// {4}, {T}, Sacrifice Mindslaver: You control target player during that player's next turn.
addCard(Zone.BATTLEFIELD, playerA, "Mindslaver", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
//
addCard(Zone.HAND, playerB, "Lightning Bolt", 1);
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
// activate and take control
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}, {T}");
addTarget(playerA, playerB);
// check control
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt");
addTarget(playerA, playerB);
setStrictChooseMode(true);
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertAllCommandsUsed();
}
@Test
public void test_OppositionAgent() {
// You control your opponents while theyre searching their libraries.
// While an opponent is searching their library, they exile each card they find. You may play those cards
// for as long as they remain exiled, and you may spend mana as though it were mana of any color to cast them.
addCard(Zone.BATTLEFIELD, playerB, "Opposition Agent", 1);
//
// Search your library for up to three creature cards and put them into your graveyard. Then shuffle your library.
addCard(Zone.HAND, playerA, "Buried Alive", 1); // {2}{B}
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
//
addCard(Zone.LIBRARY, playerA, "Balduvian Bears", 1); // {1}{G}
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2); // B can cast green bear for red mana
// before
checkPermanentCount("before a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 0);
checkPermanentCount("before b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Balduvian Bears", 0);
// start searching under B (bears must go to exile instead graveyard)
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
setChoice(playerB, "Yes"); // continue after new control
addTarget(playerB, "Balduvian Bears");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkGraveyardCount("after grave a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 0);
checkGraveyardCount("after grave b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Balduvian Bears", 0);
checkExileCount("after exile a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
checkExileCount("after exile b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Balduvian Bears", 0);
// B can cast green bear for red mana
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Balduvian Bears");
setStrictChooseMode(true);
setStopAt(2, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
assertPermanentCount(playerB, "Balduvian Bears", 1);
}
}

View file

@ -1302,7 +1302,7 @@ public class TestPlayer implements Player {
printStart("Permanents of " + player.getName());
printPermanents(game, game.getBattlefield().getAllActivePermanents(player.getId()));
printEnd();
Assert.fail(action.getActionName() + " - permanent " + permanentName + " must exists in " + count + " instances");
Assert.fail(action.getActionName() + " - permanent " + permanentName + " must exists in " + count + " instances, but founded " + foundedCount);
}
}
@ -2526,7 +2526,8 @@ public class TestPlayer implements Player {
this.chooseStrictModeFailed("choice", game, getInfo(source, game)
+ "\nMessage: " + message
+ "\nChoices: " + (trueText != null ? trueText : "Yes") + " - " + (falseText != null ? falseText : "No"));
+ "\nChoices: " + (trueText != null ? trueText : "Yes") + " - " + (falseText != null ? falseText : "No")
+ ((trueText != null || falseText != null) ? "\nUse Yes/No in unit tests for text choices." : ""));
return computerPlayer.chooseUse(outcome, message, secondMessage, trueText, falseText, source, game);
}

View file

@ -0,0 +1,53 @@
package mage.abilities.effects.common.asthought;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.constants.AsThoughEffectType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import java.util.UUID;
/**
* Play card from current zone. Will be discarded on any card movements or blinks.
* <p>
* Recommends to use combo effects from CardUtil.makeCardPlayableAndSpendManaAsAnyColor instead signle effect
*
* @author JayDi85
*/
public class CanPlayCardControllerEffect extends AsThoughEffectImpl {
protected final MageObjectReference mor;
public CanPlayCardControllerEffect(Game game, UUID cardId, int cardZCC, Duration duration) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit);
this.staticText = "You may play those card";
this.mor = new MageObjectReference(cardId, cardZCC, game);
}
public CanPlayCardControllerEffect(final CanPlayCardControllerEffect effect) {
super(effect);
this.mor = effect.mor;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public CanPlayCardControllerEffect copy() {
return new CanPlayCardControllerEffect(this);
}
@Override
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
if (mor.getCard(game) == null) {
discard();
return false;
}
return mor.refersTo(sourceId, game) && source.isControlledBy(affectedControllerId);
}
}

View file

@ -0,0 +1,55 @@
package mage.abilities.effects.common.asthought;
import mage.abilities.Ability;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.AsThoughManaEffect;
import mage.constants.*;
import mage.game.Game;
import mage.players.ManaPoolItem;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID;
/**
* Spend mana as any color to cast targeted card. Will not affected after any card movements or blinks.
*
* @author JayDi85
*/
public class YouMaySpendManaAsAnyColorToCastTargetEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
public YouMaySpendManaAsAnyColorToCastTargetEffect(Duration duration) {
super(AsThoughEffectType.SPEND_OTHER_MANA, duration, Outcome.Benefit);
this.staticText = "You may spend mana as though it were mana of any color to cast it";
}
public YouMaySpendManaAsAnyColorToCastTargetEffect(final YouMaySpendManaAsAnyColorToCastTargetEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public YouMaySpendManaAsAnyColorToCastTargetEffect copy() {
return new YouMaySpendManaAsAnyColorToCastTargetEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
return source.isControlledBy(affectedControllerId)
&& Objects.equals(objectId, fixedTarget.getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}

View file

@ -0,0 +1,48 @@
package mage.abilities.effects.common.replacement;
import mage.abilities.Ability;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.SearchLibraryEvent;
import mage.players.Player;
/**
* @author JayDi85
*/
public class YouControlYourOpponentsWhileSearchingReplacementEffect extends ReplacementEffectImpl {
public YouControlYourOpponentsWhileSearchingReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "You control your opponents while theyre searching their libraries";
}
YouControlYourOpponentsWhileSearchingReplacementEffect(final YouControlYourOpponentsWhileSearchingReplacementEffect effect) {
super(effect);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
SearchLibraryEvent se = (SearchLibraryEvent) event;
se.setSearchingControllerId(source.getControllerId());
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.SEARCH_LIBRARY;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
return controller != null && game.isOpponent(controller, event.getPlayerId());
}
@Override
public YouControlYourOpponentsWhileSearchingReplacementEffect copy() {
return new YouControlYourOpponentsWhileSearchingReplacementEffect(this);
}
}

View file

@ -0,0 +1,30 @@
package mage.game.events;
import mage.target.Target;
import java.util.UUID;
/**
* @author JayDi85
*/
public class LibrarySearchedEvent extends GameEvent {
protected Target searchedTarget;
/**
* Searched library event (after library searching finished). Return false on replaceEvent to
*
* @param targetPlayerId whose library searched
* @param sourceId source of the searching effect
* @param playerId who must search the library
* @param searchedTarget founded cards (targets list can be changed by replace events, see Opposition Agent)
*/
public LibrarySearchedEvent(UUID targetPlayerId, UUID sourceId, UUID playerId, Target searchedTarget) {
super(EventType.LIBRARY_SEARCHED, targetPlayerId, sourceId, playerId, searchedTarget.getTargets().size(), false);
this.searchedTarget = searchedTarget;
}
public Target getSearchedTarget() {
return this.searchedTarget;
}
}

View file

@ -0,0 +1,32 @@
package mage.game.events;
import java.util.UUID;
/**
* @author JayDi85
*/
public class SearchLibraryEvent extends GameEvent {
protected UUID searchingControllerId; // who controls the searching process, see Opposition Agent
/**
* Searching library event
*
* @param targetPlayerId whose library will be searched
* @param sourceId source of the searching effect
* @param playerId who must search the library (also see searchingControllerId)
* @param amount cards amount to search
*/
public SearchLibraryEvent(UUID targetPlayerId, UUID sourceId, UUID playerId, int amount) {
super(GameEvent.EventType.SEARCH_LIBRARY, targetPlayerId, sourceId, playerId, amount, false);
this.searchingControllerId = playerId;
}
public UUID getSearchingControllerId() {
return this.searchingControllerId;
}
public void setSearchingControllerId(UUID searchingControllerId) {
this.searchingControllerId = searchingControllerId;
}
}

View file

@ -1,10 +1,7 @@
package mage.players;
import com.google.common.collect.ImmutableMap;
import mage.ApprovingObject;
import mage.ConditionalMana;
import mage.MageObject;
import mage.Mana;
import mage.*;
import mage.abilities.*;
import mage.abilities.ActivatedAbility.ActivationStatus;
import mage.abilities.common.PassAbility;
@ -28,7 +25,6 @@ import mage.abilities.mana.ManaOptions;
import mage.actions.MageDrawAction;
import mage.cards.*;
import mage.cards.decks.Deck;
import mage.choices.ChoiceImpl;
import mage.constants.*;
import mage.counters.Counter;
import mage.counters.CounterType;
@ -38,6 +34,7 @@ import mage.designations.DesignationType;
import mage.filter.FilterCard;
import mage.filter.FilterMana;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.common.FilterCreatureForCombat;
import mage.filter.common.FilterCreatureForCombatBlock;
@ -2609,73 +2606,97 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId) {
//20091005 - 701.14c
Library searchedLibrary = null;
String searchInfo = null;
if (targetPlayerId.equals(playerId)) {
searchInfo = getLogName() + " searches their library";
searchedLibrary = library;
// searching control can be intercepted by another player, see Opposition Agent
SearchLibraryEvent searchEvent = new SearchLibraryEvent(targetPlayerId, source.getSourceId(), playerId, Integer.MAX_VALUE);
if (game.replaceEvent(searchEvent)) {
return false;
}
Player targetPlayer = game.getPlayer(targetPlayerId);
Player searchingPlayer = this;
Player searchingController = game.getPlayer(searchEvent.getSearchingControllerId());
if (targetPlayer == null || searchingController == null) {
return false;
}
String searchInfo = searchingPlayer.getLogName();
if (!searchingPlayer.getId().equals(searchingController.getId())) {
searchInfo = searchInfo + " under control of " + searchingPlayer.getLogName();
}
if (targetPlayer.getId().equals(searchingPlayer.getId())) {
searchInfo = searchInfo + " searches their library";
} else {
Player targetPlayer = game.getPlayer(targetPlayerId);
if (targetPlayer != null) {
searchInfo = getLogName() + " searches the library of " + targetPlayer.getLogName();
searchedLibrary = targetPlayer.getLibrary();
}
}
if (searchedLibrary == null) {
return false;
}
GameEvent event = GameEvent.getEvent(GameEvent.EventType.SEARCH_LIBRARY,
targetPlayerId, source.getSourceId(), playerId, Integer.MAX_VALUE);
if (game.replaceEvent(event)) {
return false;
searchInfo = searchInfo + " searches the library of " + targetPlayer.getLogName();
}
if (!game.isSimulation()) {
game.informPlayers(searchInfo);
}
// https://www.reddit.com/r/magicTCG/comments/jj8gh9/opposition_agent_and_panglacial_wurm_interaction/
// You must take full player control while searching, e.g. you can cast opponent's cards by Panglacial Wurm effect:
// * While youre searching your library, you may cast Panglacial Wurm from your library.
// So use here same code as Word of Command
// P.S. no needs in searchingController, but it helps with unit tests, see TakeControlWhileSearchingLibraryTest
boolean takeControl = false;
if (!searchingPlayer.getId().equals(searchingController.getId())) {
CardUtil.takeControlUnderPlayerStart(game, searchingController, searchingPlayer, true);
takeControl = true;
}
Library searchingLibrary = targetPlayer.getLibrary();
TargetCardInLibrary newTarget = target.copy();
int count;
int librarySearchLimit = event.getAmount();
int librarySearchLimit = searchEvent.getAmount();
List<Card> cardsFromTop = null;
do {
// TODO: prevent shuffling from moving the visualized cards
if (librarySearchLimit == Integer.MAX_VALUE) {
count = searchedLibrary.count(target.getFilter(), game);
count = searchingLibrary.count(target.getFilter(), game);
} else {
Player targetPlayer = game.getPlayer(targetPlayerId);
if (targetPlayer == null) {
return false;
}
if (cardsFromTop == null) {
cardsFromTop = new ArrayList<>(targetPlayer.getLibrary().getTopCards(game, librarySearchLimit));
cardsFromTop = new ArrayList<>(searchingLibrary.getTopCards(game, librarySearchLimit));
} else {
cardsFromTop.retainAll(targetPlayer.getLibrary().getCards(game));
cardsFromTop.retainAll(searchingLibrary.getCards(game));
}
newTarget.setCardLimit(Math.min(librarySearchLimit, cardsFromTop.size()));
count = Math.min(searchedLibrary.count(target.getFilter(), game), librarySearchLimit);
count = Math.min(searchingLibrary.count(target.getFilter(), game), librarySearchLimit);
}
if (count < target.getNumberOfTargets()) {
newTarget.setMinNumberOfTargets(count);
}
if (newTarget.choose(Outcome.Neutral, playerId, targetPlayerId, game)) {
if (targetPlayerId.equals(playerId) && handleLibraryCastableCards(library,
game, targetPlayerId)) { // for handling Panglacial Wurm
// handling Panglacial Wurm - cast cards while searching from own library
if (targetPlayer.getId().equals(searchingPlayer.getId())) {
if (handleCastableCardsWhileLibrarySearching(library, game, targetPlayer)) {
// clear all choices to start from scratch (casted cards must be removed from library)
newTarget.clearChosen();
continue;
}
}
if (newTarget.choose(Outcome.Neutral, searchingController.getId(), targetPlayer.getId(), game)) {
target.getTargets().clear();
for (UUID targetId : newTarget.getTargets()) {
target.add(targetId, game);
}
} else if (targetPlayerId.equals(playerId) && handleLibraryCastableCards(library,
game, targetPlayerId)) { // for handling Panglacial Wurm
newTarget.clearChosen();
continue;
}
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LIBRARY_SEARCHED, targetPlayerId, playerId));
// END SEARCH
if (takeControl) {
CardUtil.takeControlUnderPlayerEnd(game, searchingController, searchingPlayer);
game.informPlayers("Control of " + searchingPlayer.getLogName() + " is back");
}
LibrarySearchedEvent searchedEvent = new LibrarySearchedEvent(targetPlayer.getId(), source.getSourceId(), searchingPlayer.getId(), target);
if (game.replaceEvent(searchedEvent)) {
return false;
}
break;
} while (true);
return true;
}
@ -2690,58 +2711,53 @@ public abstract class PlayerImpl implements Player, Serializable {
}
}
private boolean handleLibraryCastableCards(Library library, Game game, UUID targetPlayerId) {
// for handling Panglacial Wurm
boolean alreadyChosenUse = false;
Map<UUID, String> libraryCastableCardTracker = new HashMap<>();
searchForCards:
do {
for (Card card : library.getCards(game)) {
for (Ability ability : card.getAbilities()) {
if (ability.getClass() == WhileSearchingPlayFromLibraryAbility.class) {
libraryCastableCardTracker.put(card.getId(), card.getIdName());
}
}
private boolean handleCastableCardsWhileLibrarySearching(Library library, Game game, Player targetPlayer) {
// must return true after cast try (to restart searching process without casted cards)
// uses for handling Panglacial Wurm:
// * While you're searching your library, you may cast Panglacial Wurm from your library.
List<UUID> castableCards = library.getCards(game).stream()
.filter(card -> card.getAbilities(game).containsClass(WhileSearchingPlayFromLibraryAbility.class))
.map(MageItem::getId)
.collect(Collectors.toList());
if (castableCards.size() == 0) {
return false;
}
// only humans can use it
if (!targetPlayer.isHuman() && !targetPlayer.isTestMode()) {
return false;
}
if (!targetPlayer.chooseUse(Outcome.AIDontUseIt, "Library have " + castableCards.size() + " castable cards on searching. Do you want to cast it?", null, game)) {
return false;
}
boolean casted = false;
TargetCard targetCard = new TargetCard(0, 1, Zone.LIBRARY, StaticFilters.FILTER_CARD);
targetCard.setTargetName("card to cast from library");
targetCard.setNotTarget(true);
while (castableCards.size() > 0) {
targetCard.clearChosen();
if (!targetPlayer.choose(Outcome.AIDontUseIt, new CardsImpl(castableCards), targetCard, game)) {
break;
}
if (!libraryCastableCardTracker.isEmpty()) {
Player player = game.getPlayer(targetPlayerId);
if (player != null) {
if (player.isHuman() && (alreadyChosenUse || player.chooseUse(Outcome.AIDontUseIt,
"Cast a creature card from your library? (choose \"No\" to finish search)", null, game))) {
ChoiceImpl chooseCard = new ChoiceImpl();
chooseCard.setMessage("Which creature do you wish to cast from your library?");
Set<String> choice = new LinkedHashSet<>();
for (Entry<UUID, String> entry : libraryCastableCardTracker.entrySet()) {
choice.add(new AbstractMap.SimpleEntry<>(entry).getValue());
}
chooseCard.setChoices(choice);
while (!choice.isEmpty()) {
if (player.choose(Outcome.AIDontUseIt, chooseCard, game)) {
String chosenCard = chooseCard.getChoice();
for (Entry<UUID, String> entry : libraryCastableCardTracker.entrySet()) {
if (chosenCard.equals(entry.getValue())) {
Card card = game.getCard(entry.getKey());
if (card != null) {
// TODO: fix costs (why is Panglacial Wurm automatically accepting payment?)
player.cast(card.getSpellAbility(), game, false, null);
}
chooseCard.clearChoice();
libraryCastableCardTracker.clear();
alreadyChosenUse = true;
continue searchForCards;
}
}
continue;
}
break;
}
return true;
}
}
Card card = game.getCard(targetCard.getFirstTarget());
if (card == null) {
break;
}
break;
} while (alreadyChosenUse);
return alreadyChosenUse;
// AI NOTICE: if you want AI implement here then remove selected card from castable after each
// choice (otherwise you catch infinite freeze on uncastable use case)
// casting selected card
// TODO: fix costs (why is Panglacial Wurm automatically accepting payment?)
targetPlayer.cast(targetPlayer.chooseAbilityForCast(card, game, false), game, false, null);
castableCards.remove(card.getId());
casted = true;
}
return casted;
}
@Override

View file

@ -10,6 +10,8 @@ import mage.abilities.SpellAbility;
import mage.abilities.costs.VariableCost;
import mage.abilities.costs.mana.*;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.common.asthought.CanPlayCardControllerEffect;
import mage.abilities.effects.common.asthought.YouMaySpendManaAsAnyColorToCastTargetEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.HintUtils;
import mage.cards.Card;
@ -28,6 +30,7 @@ import mage.game.permanent.token.Token;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.Target;
import mage.target.targetpointer.FixedTarget;
import mage.util.functions.CopyTokenFunction;
import org.apache.log4j.Logger;
@ -955,4 +958,56 @@ public final class CardUtil {
}
return RULES_ERROR_INFO;
}
/**
* Take control under another player, use it in inner effects like Word of Commands. Don't forget to end it in same code.
*
* @param game
* @param controller
* @param targetPlayer
* @param givePauseForResponse if you want to give controller time to watch opponent's hand (if you remove control effect in the end of code)
*/
public static void takeControlUnderPlayerStart(Game game, Player controller, Player targetPlayer, boolean givePauseForResponse) {
controller.controlPlayersTurn(game, targetPlayer.getId());
if (givePauseForResponse) {
while (controller.canRespond()) {
if (controller.chooseUse(Outcome.Benefit, "You got control of " + targetPlayer.getLogName()
+ ". Use switch hands button to view opponent's hand.", null,
"Continue", "Wait", null, game)) {
break;
}
}
}
}
/**
* Return control under another player, use it in inner effects like Word of Commands.
*
* @param game
* @param controller
* @param targetPlayer
*/
public static void takeControlUnderPlayerEnd(Game game, Player controller, Player targetPlayer) {
targetPlayer.setGameUnderYourControl(true, false);
if (!targetPlayer.getTurnControlledBy().equals(controller.getId())) {
controller.getPlayersUnderYourControl().remove(targetPlayer.getId());
}
}
/**
* Add effects to game that allows to play/cast card from current zone and spend mana as any type for it.
* Effects will be discarded/ignored on any card movements or blinks (after ZCC change)
*
* @param game
* @param card
* @param duration
*/
public static void makeCardPlayableAndSpendManaAsAnyColor(Game game, Ability source, Card card, Duration duration) {
// Effect can be used for cards in zones and permanents on battlefield
// So there is a workaround to get real ZCC (PermanentCard's ZCC is static, but it must be from Card's ZCC)
// Example: Hostage Taker
int zcc = game.getState().getZoneChangeCounter(card.getId());
game.addEffect(new CanPlayCardControllerEffect(game, card.getId(), zcc, duration), source);
game.addEffect(new YouMaySpendManaAsAnyColorToCastTargetEffect(duration).setTargetPointer(new FixedTarget(card.getId(), zcc)), source);
}
}