mirror of
https://github.com/correl/mage.git
synced 2024-12-25 03:00:15 +00:00
Merge pull request #6093 from magefree/asThough_and_non_hand_plays_rework
As though and non hand plays rework
This commit is contained in:
commit
71cd4b73b4
41 changed files with 663 additions and 442 deletions
|
@ -1742,7 +1742,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
if (ability instanceof PlayLandAbility) {
|
||||
return true;
|
||||
}
|
||||
if (!ability.getSourceId().equals(getCastSourceIdWithAlternateMana())
|
||||
if (!getCastSourceIdWithAlternateMana().contains(ability.getSourceId())
|
||||
&& ability.getManaCostsToPay().convertedManaCost() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -96,15 +96,14 @@ class BolassCitadelPlayTheTopCardEffect extends AsThoughEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
Card cardOnTop = game.getCard(objectId);
|
||||
if (cardOnTop == null) {
|
||||
return false;
|
||||
}
|
||||
if (playerId.equals(source.getControllerId())
|
||||
&& cardOnTop.isOwnedBy(source.getControllerId())) {
|
||||
Player controller = game.getPlayer(cardOnTop.getOwnerId());
|
||||
Card cardToCheck = game.getCard(objectId);
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
|
||||
if (playerId.equals(source.getControllerId()) && cardToCheck.isOwnedBy(source.getControllerId())) {
|
||||
Player controller = game.getPlayer(cardToCheck.getOwnerId());
|
||||
if (controller != null
|
||||
&& cardOnTop.equals(controller.getLibrary().getFromTop(game))) {
|
||||
&& controller.getLibrary().getFromTop(game) != null
|
||||
&& objectId.equals(controller.getLibrary().getFromTop(game).getId())) {
|
||||
if (affectedAbility instanceof ActivatedAbility) {
|
||||
ActivatedAbility activatedAbility = (ActivatedAbility) affectedAbility;
|
||||
// add the life cost first
|
||||
|
|
|
@ -156,11 +156,12 @@ class CovetousUrgeSpendAnyManaEffect extends AsThoughEffectImpl implements AsTho
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, fixedTarget.getTarget())
|
||||
&& fixedTarget.getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package mage.cards.c;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
|
@ -21,8 +20,9 @@ import mage.target.common.TargetOpponent;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Styxo
|
||||
*/
|
||||
public final class CunningAbduction extends CardImpl {
|
||||
|
@ -125,12 +125,8 @@ class CunningAbductionSpendAnyManaEffect extends AsThoughEffectImpl implements A
|
|||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1
|
||||
if (game.getState().getZoneChangeCounter(objectId) == ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
} else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
this.discard();
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -26,8 +24,10 @@ import mage.players.Player;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class DaxosOfMeletis extends CardImpl {
|
||||
|
@ -178,11 +178,11 @@ class DaxosOfMeletisSpendAnyManaEffect extends AsThoughEffectImpl implements AsT
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, ((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& (((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId))
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& 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
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.DiesAttachedTriggeredAbility;
|
||||
|
@ -15,14 +13,7 @@ import mage.abilities.keyword.EnchantAbility;
|
|||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.ControllerPredicate;
|
||||
import mage.game.Game;
|
||||
|
@ -32,8 +23,10 @@ import mage.players.Player;
|
|||
import mage.target.TargetPermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class DeadMansChest extends CardImpl {
|
||||
|
@ -142,9 +135,7 @@ class DeadMansChestCastFromExileEffect extends AsThoughEffectImpl {
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
if (objectId.equals(getTargetPointer().getFirst(game, source))) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
return true;
|
||||
}
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
} else {
|
||||
if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
|
@ -181,12 +172,8 @@ class DeadMansChestSpendManaEffect extends AsThoughEffectImpl implements AsThoug
|
|||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1
|
||||
if (game.getState().getZoneChangeCounter(objectId) == ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
} else {
|
||||
if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
|
|
|
@ -1,27 +1,15 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
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.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.*;
|
||||
import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
|
||||
import mage.abilities.keyword.FirstStrikeAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
|
@ -34,8 +22,10 @@ import mage.players.Player;
|
|||
import mage.target.common.TargetCardInOpponentsGraveyard;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class DireFleetDaredevil extends CardImpl {
|
||||
|
@ -144,10 +134,11 @@ class DireFleetDaredevilSpendAnyManaEffect extends AsThoughEffectImpl implements
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, ((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -192,7 +183,7 @@ class DireFleetDaredevilReplacementEffect extends ReplacementEffectImpl {
|
|||
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
||||
return zEvent.getToZone() == Zone.GRAVEYARD
|
||||
&& event.getTargetId().equals(((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1
|
||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1
|
||||
== game.getState().getZoneChangeCounter(event.getTargetId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
package mage.cards.g;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -25,8 +22,11 @@ import mage.target.common.TargetOpponent;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class GontiLordOfLuxury extends CardImpl {
|
||||
|
@ -185,22 +185,18 @@ class GontiLordOfLuxurySpendAnyManaEffect extends AsThoughEffectImpl implements
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
Card theCard = game.getCard(objectId);
|
||||
if(theCard == null){
|
||||
if (theCard == null) {
|
||||
return false;
|
||||
}
|
||||
Card mainCard = theCard.getMainCard();
|
||||
if(mainCard == null){
|
||||
if (mainCard == null) {
|
||||
return false;
|
||||
}
|
||||
objectId = mainCard.getId(); // for split cards
|
||||
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1
|
||||
if (game.getState().getZoneChangeCounter(objectId) == ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
} else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
this.discard();
|
||||
|
@ -238,11 +234,11 @@ class GontiLordOfLuxuryLookEffect extends AsThoughEffectImpl {
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
Card theCard = game.getCard(objectId);
|
||||
if(theCard == null){
|
||||
if (theCard == null) {
|
||||
return false;
|
||||
}
|
||||
Card mainCard = theCard.getMainCard();
|
||||
if(mainCard == null){
|
||||
if (mainCard == null) {
|
||||
return false;
|
||||
}
|
||||
objectId = mainCard.getId(); // for split cards
|
||||
|
|
|
@ -233,11 +233,12 @@ class GrenzoHavocRaiserSpendAnyManaEffect extends AsThoughEffectImpl implements
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, ((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& (((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId))
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -106,7 +106,7 @@ class HostageTakerExileEffect extends OneShotEffect {
|
|||
ContinuousEffect effect = new HostageTakerSpendAnyManaEffect();
|
||||
effect.setTargetPointer(new FixedTarget(card.getId(), game));
|
||||
game.addEffect(effect, source);
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,11 +173,12 @@ class HostageTakerSpendAnyManaEffect extends AsThoughEffectImpl implements AsTho
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, fixedTarget.getTarget())
|
||||
&& fixedTarget.getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.o;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
|
@ -19,8 +17,9 @@ import mage.players.ManaPoolItem;
|
|||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class OathOfNissa extends CardImpl {
|
||||
|
@ -128,13 +127,10 @@ class OathOfNissaSpendAnyManaEffect extends AsThoughEffectImpl implements AsThou
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (source.isControlledBy(affectedControllerId)) {
|
||||
MageObject mageObject = game.getObject(objectId);
|
||||
if (mageObject != null) {
|
||||
if (mageObject.isPlaneswalker()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return mageObject != null && mageObject.isPlaneswalker();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
|
@ -10,12 +9,7 @@ import mage.abilities.effects.OneShotEffect;
|
|||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.filter.common.FilterNonlandCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.ManaPoolItem;
|
||||
|
@ -25,8 +19,9 @@ import mage.target.common.TargetOpponent;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class PsychicIntrusion extends CardImpl {
|
||||
|
@ -149,10 +144,9 @@ class PsychicIntrusionCastFromExileEffect extends AsThoughEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (objectId.equals(getTargetPointer().getFirst(game, source))) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
return true;
|
||||
}
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
} else {
|
||||
if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
|
@ -189,12 +183,8 @@ class PsychicIntrusionSpendAnyManaEffect extends AsThoughEffectImpl implements A
|
|||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1
|
||||
if (game.getState().getZoneChangeCounter(objectId) == ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
} else {
|
||||
if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.q;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
|
@ -10,28 +8,20 @@ import mage.abilities.common.SimpleStaticAbility;
|
|||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.AsThoughManaEffect;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.ManaPoolItem;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public final class QuicksilverElemental extends CardImpl {
|
||||
|
@ -151,10 +141,9 @@ class QuickSilverElementalBlueManaEffect extends AsThoughEffectImpl implements A
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (objectId.equals(getTargetPointer().getFirst(game, source))) {
|
||||
if (affectedControllerId.equals(source.getControllerId())) {
|
||||
return true;
|
||||
}
|
||||
return affectedControllerId.equals(source.getControllerId());
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -194,11 +194,12 @@ class RobberOfTheRichSpendAnyManaEffect extends AsThoughEffectImpl implements As
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, fixedTarget.getTarget())
|
||||
&& fixedTarget.getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= fixedTarget.getZoneChangeCounter() + 1
|
||||
&& (game.getState().getZone(objectId) == Zone.STACK || game.getState().getZone(objectId) == Zone.EXILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
|
@ -13,13 +10,7 @@ import mage.abilities.effects.OneShotEffect;
|
|||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
import mage.players.ManaPoolItem;
|
||||
|
@ -27,8 +18,10 @@ import mage.players.Player;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class StolenStrategy extends CardImpl {
|
||||
|
@ -165,11 +158,11 @@ class StolenStrategySpendAnyManaEffect extends AsThoughEffectImpl implements AsT
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, ((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& (((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId))
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& 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
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -12,18 +9,8 @@ import mage.abilities.effects.AsThoughManaEffect;
|
|||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.cards.*;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.ManaPoolItem;
|
||||
|
@ -32,8 +19,11 @@ import mage.target.TargetCard;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class ThiefOfSanity extends CardImpl {
|
||||
|
@ -200,12 +190,8 @@ class ThiefOfSanitySpendAnyManaEffect extends AsThoughEffectImpl implements AsTh
|
|||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
if (affectedControllerId.equals(authorizedPlayerId)) {
|
||||
// if the card moved from exile to stack the zone change counter is increased by 1
|
||||
if (game.getState().getZoneChangeCounter(objectId) == ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
|
||||
return affectedControllerId.equals(authorizedPlayerId);
|
||||
} else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
|
||||
// object has moved zone so effect can be discarted
|
||||
this.discard();
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.*;
|
||||
import mage.abilities.effects.common.ExileCardsFromTopOfLibraryTargetEffect;
|
||||
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;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.*;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
|
@ -24,15 +24,17 @@ import mage.target.common.TargetOpponentsCreaturePermanent;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author NinthWorld
|
||||
*/
|
||||
public final class TobiasBeckett extends CardImpl {
|
||||
|
||||
public TobiasBeckett(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
|
||||
|
||||
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.HUNTER);
|
||||
|
@ -75,7 +77,7 @@ class TobiasBeckettEffect extends OneShotEffect {
|
|||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
Permanent bountyTriggered = game.getPermanent(this.getTargetPointer().getFirst(game, source));
|
||||
if(bountyTriggered != null) {
|
||||
if (bountyTriggered != null) {
|
||||
Player opponent = game.getPlayer(bountyTriggered.getControllerId());
|
||||
if (opponent != null) {
|
||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||
|
@ -171,11 +173,11 @@ class TobiasBeckettSpendAnyManaEffect extends AsThoughEffectImpl implements AsTh
|
|||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
FixedTarget fixedTarget = ((FixedTarget) getTargetPointer());
|
||||
return source.isControlledBy(affectedControllerId)
|
||||
&& Objects.equals(objectId, ((FixedTarget) getTargetPointer()).getTarget())
|
||||
&& ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId)
|
||||
&& (((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(objectId))
|
||||
&& game.getState().getZone(objectId) == Zone.STACK;
|
||||
&& 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
|
||||
|
|
|
@ -73,6 +73,7 @@ class ViviensInvocationEffect extends OneShotEffect {
|
|||
Zone.LIBRARY,
|
||||
new FilterCreatureCard("creature card to put on the battlefield")
|
||||
);
|
||||
target.setNotTarget(true);
|
||||
if (controller.choose(Outcome.PutCreatureInPlay, cards, target, game)) {
|
||||
Card card = cards.get(target.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.v;
|
||||
|
||||
import mage.MageInt;
|
||||
|
@ -83,12 +82,10 @@ class VizierOfTheMenagerieTopCardCastEffect extends AsThoughEffectImpl {
|
|||
MageObject vizierOfTheMenagerie = game.getObject(source.getSourceId());
|
||||
if (vizierOfTheMenagerie != null
|
||||
&& topCard != null) {
|
||||
if (topCard == card
|
||||
return topCard == card
|
||||
&& topCard.isCreature()
|
||||
&& topCard.getSpellAbility() != null
|
||||
&& topCard.getSpellAbility().spellCanBeActivatedRegularlyNow(controller.getId(), game)) {
|
||||
return true;
|
||||
}
|
||||
&& topCard.getSpellAbility().spellCanBeActivatedRegularlyNow(controller.getId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,10 +117,10 @@ class VizierOfTheMenagerieManaEffect extends AsThoughEffectImpl implements AsTho
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
if (source.isControlledBy(affectedControllerId)) {
|
||||
MageObject mageObject = game.getObject(objectId);
|
||||
return mageObject != null
|
||||
&& mageObject.isCreature();
|
||||
return mageObject != null && mageObject.isCreature();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.AI.basic;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
|
@ -8,7 +7,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBaseAI;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class CastCreaturesTest extends CardTestPlayerBaseAI {
|
||||
|
@ -173,7 +171,7 @@ public class CastCreaturesTest extends CardTestPlayerBaseAI {
|
|||
|
||||
/**
|
||||
* Tests that the creature is cast if enough mana is available.
|
||||
*
|
||||
* <p>
|
||||
* Once Ammit Eternal is cast against a computer AI opponent, the AI just
|
||||
* decides to sit there and only play basic lands. I've sat there and decked
|
||||
* it because it just plays lands. It's like it views giving the Ammit
|
||||
|
@ -194,8 +192,11 @@ public class CastCreaturesTest extends CardTestPlayerBaseAI {
|
|||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Ammit Eternal");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(3, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerB, "Ammit Eternal", 1);
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.cards.abilities.activated;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
|
@ -7,7 +6,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class LightningStormTest extends CardTestPlayerBase {
|
||||
|
@ -16,10 +14,9 @@ public class LightningStormTest extends CardTestPlayerBase {
|
|||
* So, this just happened to me. My opponent cast Lightning Storm and while
|
||||
* it was on the stack I couldn't use the ability despite having land in
|
||||
* hand which isn't something I've had an issue with before.
|
||||
*
|
||||
* <p>
|
||||
* My opponent had a Leyline of Sanctity in play, so perhaps that was
|
||||
* causing the issue somehow? Does anyone want to try and replicate it?
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void ActivateByBothPlayersTest() {
|
||||
|
@ -31,22 +28,32 @@ public class LightningStormTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerA, "Mountain");
|
||||
addCard(Zone.HAND, playerB, "Mountain");
|
||||
|
||||
// A activate
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Storm", playerB);
|
||||
|
||||
// B discard and re-target
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Discard");
|
||||
setChoice(playerB, "playerA");
|
||||
setChoice(playerB, "Mountain");
|
||||
setChoice(playerB, "Yes");
|
||||
addTarget(playerB, playerA);
|
||||
|
||||
// A discard and re-target
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
|
||||
setChoice(playerA, "playerB");
|
||||
setChoice(playerA, "Mountain");
|
||||
setChoice(playerA, "Yes");
|
||||
addTarget(playerA, playerB);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerA, "Lightning Storm", 1);
|
||||
assertGraveyardCount(playerB, "Mountain", 1);
|
||||
assertGraveyardCount(playerA, "Mountain", 1);
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 13);
|
||||
assertLife(playerB, 20 - 3 - 2 - 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
package org.mage.test.cards.asthough;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class PlayTopCardFromLibraryTest extends CardTestPlayerBase {
|
||||
|
||||
/*
|
||||
Bolas's Citadel
|
||||
{3}{B}{B}{B}
|
||||
You may look at the top card of your library any time.
|
||||
You may play the top card of your library. If you cast a spell this way, pay life equal to its converted mana cost rather than pay its mana cost.
|
||||
{T}, Sacrifice ten nonland permanents: Each opponent loses 10 life.
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void test_CreaturePlay() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.LIBRARY, playerA, "Balduvian Bears", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); // 2 CMC
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 1);
|
||||
assertLife(playerA, 20 - 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_CreaturePlay2() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.LIBRARY, playerA, "Balduvian Bears", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Vizier of the Menagerie", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ManaCostmodifications() {
|
||||
//
|
||||
// {5}{B}{B}
|
||||
// You may cast Scourge of Nel Toth from your graveyard by paying {B}{B} and sacrificing two creatures rather than paying its mana cost.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Scourge of Nel Toth", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Kitesail Corsair", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scourge of Nel Toth");
|
||||
setChoice(playerA, "Kitesail Corsair");
|
||||
setChoice(playerA, "Kitesail Corsair");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Scourge of Nel Toth", 1);
|
||||
assertLife(playerA, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SplitRightPlay() {
|
||||
// https://github.com/magefree/mage/issues/5912
|
||||
// Bolas's citadel requires you to pay mana instead of life for a split card on top of library.
|
||||
//
|
||||
// Steps to reproduce:
|
||||
//
|
||||
// Bolas's Citadel in play, Revival//Revenge on top of library.
|
||||
// Cast Revenge, choose target
|
||||
// receive prompt to pay 4WB.
|
||||
//
|
||||
// Expected outcome
|
||||
//
|
||||
// No prompt for mana payment, payment of six life instead.
|
||||
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.LIBRARY, playerA, "Revival // Revenge", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel", 1);
|
||||
|
||||
// Double your life total. Target opponent loses half their life, rounded up.
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Revenge", playerB); // {4}{W}{B} = 6 life
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, (20 - 6) * 2);
|
||||
assertLife(playerB, 20 / 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SplitLeftPlay() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.LIBRARY, playerA, "Revival // Revenge", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel", 1);
|
||||
addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 1);
|
||||
|
||||
// Return target creature card with converted mana cost 3 or less from your graveyard to the battlefield.
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Revival", "Balduvian Bears"); // {W/B}{W/B} = 2 life
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 20 - 2);
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerA, "Balduvian Bears", 0);
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 1);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.cards.asthough;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
|
@ -7,7 +6,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class SpendOtherManaTest extends CardTestPlayerBase {
|
||||
|
@ -47,7 +45,7 @@ public class SpendOtherManaTest extends CardTestPlayerBase {
|
|||
/**
|
||||
* Tron mana doesn't work with Oath of Nissa. (e.g. can't cast Chandra,
|
||||
* Flamecaller with Urza's Tower, Power Plant, and Mine.)
|
||||
*
|
||||
* <p>
|
||||
* AI don't get the Planeswalker as playable card (probably because of the
|
||||
* as thought effect)
|
||||
*/
|
||||
|
@ -76,7 +74,7 @@ public class SpendOtherManaTest extends CardTestPlayerBase {
|
|||
* I was unable to cast Nissa, Voice of Zendikar using black mana with Oath
|
||||
* of Nissa in play. Pretty sure Oath is working usually, so here were the
|
||||
* conditions in my game:
|
||||
*
|
||||
* <p>
|
||||
* -Cast Dark Petition with spell mastery -Attempt to cast Nissa, Voice of
|
||||
* Zendikar using the triple black mana from Dark Petition
|
||||
*/
|
||||
|
@ -122,20 +120,84 @@ public class SpendOtherManaTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerA, "Hostage Taker"); // {2}{U}{B}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hostage Taker");
|
||||
setChoice(playerA, "Silvercoat Lion");
|
||||
addTarget(playerA, "Silvercoat Lion");
|
||||
|
||||
// red mana must be used as any mana
|
||||
activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {R}."); // red mana to pool
|
||||
activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {R}."); // red mana to pool
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion"); // cast it from exile with red mana from pool
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Hostage Taker", 1);
|
||||
assertTappedCount("Mountain", true, 4);
|
||||
|
||||
assertPermanentCount(playerA, "Silvercoat Lion", 1);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_QuicksilverElemental_Normal() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
|
||||
// {U}: Quicksilver Elemental gains all activated abilities of target creature until end of turn.
|
||||
// You may spend blue mana as though it were mana of any color to pay the activation costs of Quicksilver Elemental’s abilities.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Quicksilver Elemental"); // Creature {1}{W}
|
||||
// {R}, {T}: Anaba Shaman deals 1 damage to any target.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Anaba Shaman");
|
||||
|
||||
// gain abilities
|
||||
checkPlayableAbility("must not have", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}, {T}:", false);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{U}:", "Anaba Shaman");
|
||||
|
||||
// use ability
|
||||
checkPlayableAbility("must have new ability", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{R}, {T}:", true);
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{R}, {T}:", playerB);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20 - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_QuicksilverElemental_Flicker() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
|
||||
// {U}: Quicksilver Elemental gains all activated abilities of target creature until end of turn.
|
||||
// You may spend blue mana as though it were mana of any color to pay the activation costs of Quicksilver Elemental’s abilities.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Quicksilver Elemental"); // Creature {1}{W}
|
||||
// {R}, {T}: Anaba Shaman deals 1 damage to any target.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Anaba Shaman");
|
||||
// Exile target nontoken permanent, then return it to the battlefield under its owner’s control.
|
||||
addCard(Zone.HAND, playerA, "Flicker"); // {1}{W}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
|
||||
// gain abilities
|
||||
checkPlayableAbility("must not have", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}, {T}:", false);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{U}:", "Anaba Shaman");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
checkPlayableAbility("must have new ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}, {T}:", true);
|
||||
|
||||
// renew target
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flicker", "Anaba Shaman");
|
||||
|
||||
// use ability
|
||||
checkPlayableAbility("must save ability", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{R}, {T}:", true);
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{R}, {T}:", playerB);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerA, "Flicker", 1);
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20 - 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.cards.continuous;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
|
@ -7,7 +6,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
@ -23,20 +21,22 @@ public class PsychicIntrusionTest extends CardTestPlayerBase {
|
|||
// Target opponent reveals their hand. You choose a nonland card from that player's
|
||||
// graveyard or hand and exile it. You may cast that card for as long as it remains exiled,
|
||||
// and you may spend mana as though it were mana of any color to cast that spell.
|
||||
addCard(Zone.HAND, playerA, "Psychic Intrusion", 1);
|
||||
addCard(Zone.HAND, playerA, "Psychic Intrusion", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||
|
||||
addCard(Zone.HAND, playerB, "Elspeth, Sun's Champion", 1);
|
||||
|
||||
addCard(Zone.HAND, playerB, "Elspeth, Sun's Champion", 1); // {4}{W}{W}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Psychic Intrusion", playerB);
|
||||
addTarget(playerA, "Elspeth, Sun's Champion");
|
||||
|
||||
setChoice(playerA, "Elspeth, Sun's Champion");
|
||||
|
||||
// cast from exile with any mana
|
||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Elspeth, Sun's Champion");
|
||||
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerA, "Psychic Intrusion", 1);
|
||||
assertHandCount(playerB, "Elspeth, Sun's Champion", 0);
|
||||
|
|
|
@ -5,7 +5,6 @@ import mage.constants.Zone;
|
|||
import mage.counters.CounterType;
|
||||
import mage.game.permanent.Permanent;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
|
@ -282,7 +281,8 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerB, "Curious Pair");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Psychic Intrusion", playerB);
|
||||
playerA.addChoice("Curious Pair");
|
||||
setChoice(playerA, "Curious Pair");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
|
||||
|
||||
|
@ -462,6 +462,7 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
|||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.LIBRARY, playerA, "Curious Pair");
|
||||
|
||||
showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
|
@ -473,7 +474,7 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Not yet working correctly.")
|
||||
//@Ignore("Not yet working correctly.")
|
||||
public void testCastTreatsToShareWithWrennAndSixEmblem() {
|
||||
/*
|
||||
* Wrenn and Six {R}{G}
|
||||
|
@ -487,11 +488,16 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerA, "Forest");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Wrenn and Six");
|
||||
addCard(Zone.GRAVEYARD, playerA, "Curious Pair");
|
||||
addCard(Zone.HAND, playerA, "Forest");
|
||||
addCard(Zone.HAND, playerA, "Forest"); // pay for retrace
|
||||
|
||||
addCounters(1, PhaseStep.UPKEEP, playerA, "Wrenn and Six", CounterType.LOYALTY, 5);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-7: You get an emblem");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
// retrace - You may cast this card from your graveyard by discarding a land card as an additional cost to cast it
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
||||
setChoice(playerA, "Forest");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
@ -523,6 +529,8 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerA, "Curious Pair");
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Until your next");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
showAvaileableAbilities("abils", 1, PhaseStep.BEGIN_COMBAT, playerA);
|
||||
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Treats to Share");
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
|
|
@ -32,7 +32,6 @@ public class BolassCitadelTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore("This is broken for now.")
|
||||
public void testCastAdventure() {
|
||||
/*
|
||||
* Curious Pair {1}{G}
|
||||
|
@ -44,7 +43,6 @@ public class BolassCitadelTest extends CardTestPlayerBase {
|
|||
* Create a Food token.
|
||||
*/
|
||||
setStrictChooseMode(true);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel");
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.LIBRARY, playerA, "Curious Pair");
|
||||
|
@ -54,7 +52,6 @@ public class BolassCitadelTest extends CardTestPlayerBase {
|
|||
execute();
|
||||
|
||||
assertAllCommandsUsed();
|
||||
assertTapped("Forest", false);
|
||||
assertHandCount(playerA, 0);
|
||||
assertPermanentCount(playerA, "Food", 1);
|
||||
assertExileCount(playerA, "Curious Pair", 1);
|
||||
|
|
|
@ -6,7 +6,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* also tests regenerate and tests that permanents with protection can be
|
||||
* sacrificed
|
||||
*
|
||||
|
@ -46,10 +45,13 @@ public class FiendOfTheShadowsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerB, "Swamp");
|
||||
|
||||
attack(1, playerA, "Fiend of the Shadows");
|
||||
addTarget(playerB, "Swamp");
|
||||
playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Swamp");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 17);
|
||||
|
|
|
@ -2374,20 +2374,25 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
|
||||
@Override
|
||||
public UUID getCastSourceIdWithAlternateMana() {
|
||||
public Set<UUID> getCastSourceIdWithAlternateMana() {
|
||||
return computerPlayer.getCastSourceIdWithAlternateMana();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManaCosts getCastSourceIdManaCosts() {
|
||||
public Map<UUID, ManaCosts<ManaCost>> getCastSourceIdManaCosts() {
|
||||
return computerPlayer.getCastSourceIdManaCosts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Costs<Cost> getCastSourceIdCosts() {
|
||||
public Map<UUID, Costs<Cost>> getCastSourceIdCosts() {
|
||||
return computerPlayer.getCastSourceIdCosts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCastSourceIdManaCosts() {
|
||||
computerPlayer.clearCastSourceIdManaCosts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInPayManaMode() {
|
||||
return computerPlayer.isInPayManaMode();
|
||||
|
|
|
@ -19,8 +19,8 @@ import mage.counters.Counter;
|
|||
import mage.counters.Counters;
|
||||
import mage.designations.Designation;
|
||||
import mage.designations.DesignationType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.Graveyard;
|
||||
import mage.game.Table;
|
||||
|
@ -1208,7 +1208,7 @@ public class PlayerStub implements Player {
|
|||
}
|
||||
|
||||
@Override
|
||||
public UUID getCastSourceIdWithAlternateMana() {
|
||||
public Set<UUID> getCastSourceIdWithAlternateMana() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1218,15 +1218,20 @@ public class PlayerStub implements Player {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ManaCosts getCastSourceIdManaCosts() {
|
||||
public Map<UUID, Costs<Cost>> getCastSourceIdCosts() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Costs<Cost> getCastSourceIdCosts() {
|
||||
public Map<UUID, ManaCosts<ManaCost>> getCastSourceIdManaCosts() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCastSourceIdManaCosts() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPermissionToShowHandCards(UUID watcherUserId) {
|
||||
|
||||
|
@ -1374,19 +1379,19 @@ public class PlayerStub implements Player {
|
|||
|
||||
@Override
|
||||
public void addPhyrexianToColors(FilterMana colors) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePhyrexianFromColors(FilterMana colors) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterMana getPhyrexianColors() {
|
||||
return (new FilterMana());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) {
|
||||
return card.getSpellAbility();
|
||||
|
|
|
@ -63,7 +63,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
*/
|
||||
public boolean spellCanBeActivatedRegularlyNow(UUID playerId, Game game) {
|
||||
MageObject object = game.getObject(sourceId);
|
||||
if ((Boolean) game.getState().getValue("CastFromExileEnabled" + object.getId()) != null) {
|
||||
if (game.getState().getValue("CastFromExileEnabled" + object.getId()) != null) {
|
||||
return (Boolean) game.getState().getValue("CastFromExileEnabled" + object.getId()); // card like Chandra, Torch of Defiance +1 loyal ability)
|
||||
}
|
||||
return null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game) // check this first to allow Offering in main phase
|
||||
|
@ -98,7 +98,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
if (getSpellAbilityType() == SpellAbilityType.BASE_ALTERNATE) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null
|
||||
&& getSourceId().equals(player.getCastSourceIdWithAlternateMana())) {
|
||||
&& player.getCastSourceIdWithAlternateMana().contains(getSourceId())) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package mage.abilities.effects;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Duration;
|
||||
|
@ -8,8 +7,9 @@ import mage.constants.EffectType;
|
|||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements AsThoughEffect {
|
||||
|
@ -29,10 +29,11 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
// affectedControllerId = player to check
|
||||
if (getAsThoughEffectType().equals(AsThoughEffectType.LOOK_AT_FACE_DOWN)) {
|
||||
return applies(objectId, source, playerId, game);
|
||||
} else {
|
||||
return applies(objectId, source, affectedAbility.getControllerId(), game);
|
||||
return applies(objectId, source, playerId, game);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -505,12 +505,19 @@ public class ContinuousEffects implements Serializable {
|
|||
UUID idToCheck;
|
||||
if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) affectedAbility.getSourceObject(game)).getParentCard().getId();
|
||||
} else if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof AdventureCardSpell
|
||||
&& type != AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE
|
||||
&& type != AsThoughEffectType.CAST_AS_INSTANT) {
|
||||
// adventure spell uses alternative characteristics for spell/stack
|
||||
idToCheck = ((AdventureCardSpell) affectedAbility.getSourceObject(game)).getParentCard().getId();
|
||||
} else {
|
||||
Card card = game.getCard(objectId);
|
||||
if (card != null && card instanceof SplitCardHalf) {
|
||||
if (card instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) card).getParentCard().getId();
|
||||
} else if (card != null && type == AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE
|
||||
&& card instanceof AdventureCardSpell) {
|
||||
} else if (card instanceof AdventureCardSpell
|
||||
&& type != AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE
|
||||
&& type != AsThoughEffectType.CAST_AS_INSTANT) {
|
||||
// adventure spell uses alternative characteristics for spell/stack
|
||||
idToCheck = ((AdventureCardSpell) card).getParentCard().getId();
|
||||
} else {
|
||||
idToCheck = objectId;
|
||||
|
|
|
@ -51,7 +51,7 @@ public class ExileAdventureSpellEffect extends OneShotEffect implements MageSing
|
|||
Spell spell = game.getStack().getSpell(source.getId());
|
||||
if (spell != null && !spell.isCopy()) {
|
||||
Card spellCard = spell.getCard();
|
||||
if (spellCard != null && spellCard instanceof AdventureCardSpell) {
|
||||
if (spellCard instanceof AdventureCardSpell) {
|
||||
UUID exileId = adventureExileId(controller.getId(), game);
|
||||
game.getExile().createZone(exileId, "On an Adventure");
|
||||
AdventureCardSpell adventureSpellCard = (AdventureCardSpell) spellCard;
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
|
||||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
|
@ -15,6 +10,8 @@ import mage.filter.FilterCard;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author nantuko
|
||||
*/
|
||||
|
@ -55,26 +52,18 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
Card cardOnTop = game.getCard(objectId);
|
||||
Card cardToCheckProperties = cardOnTop;
|
||||
Card cardToCheck = game.getCard(objectId);
|
||||
objectId = game.getCard(objectId).getMainCard().getId(); // for split cards
|
||||
|
||||
// Check each ability individually, as e.g. Adventures and associated creatures may get different results from the filter.
|
||||
if (affectedAbility != null) {
|
||||
MageObject sourceObject = affectedAbility.getSourceObject(game);
|
||||
if (sourceObject != null && sourceObject instanceof Card) {
|
||||
cardToCheckProperties = (Card) sourceObject;
|
||||
}
|
||||
}
|
||||
|
||||
if (cardOnTop != null
|
||||
if (cardToCheck != null
|
||||
&& playerId.equals(source.getControllerId())
|
||||
&& cardOnTop.isOwnedBy(source.getControllerId())
|
||||
&& (!cardToCheckProperties.getManaCost().isEmpty() || cardToCheckProperties.isLand())
|
||||
&& filter.match(cardToCheckProperties, game)) {
|
||||
Player player = game.getPlayer(cardOnTop.getOwnerId());
|
||||
if (player != null && cardOnTop.equals(player.getLibrary().getFromTop(game))) {
|
||||
return true;
|
||||
}
|
||||
&& cardToCheck.isOwnedBy(source.getControllerId())
|
||||
&& (!cardToCheck.getManaCost().isEmpty() || cardToCheck.isLand())
|
||||
&& filter.match(cardToCheck, game)) {
|
||||
Player player = game.getPlayer(cardToCheck.getOwnerId());
|
||||
|
||||
UUID needCardID = player.getLibrary().getFromTop(game) == null ? null : player.getLibrary().getFromTop(game).getId();
|
||||
return objectId.equals(needCardID);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -100,6 +100,7 @@ class HideawayExileEffect extends OneShotEffect {
|
|||
cards.addAll(controller.getLibrary().getTopCards(game, 4));
|
||||
if (!cards.isEmpty()) {
|
||||
TargetCard target1 = new TargetCard(Zone.LIBRARY, filter1);
|
||||
target1.setNotTarget(true);
|
||||
if (controller.choose(Outcome.Detriment, cards, target1, game)) {
|
||||
Card card = cards.get(target1.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.Iterator;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.OptionalAdditionalCost;
|
||||
import mage.abilities.costs.OptionalAdditionalCostImpl;
|
||||
import mage.abilities.costs.OptionalAdditionalSourceCosts;
|
||||
import mage.abilities.costs.*;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
@ -23,8 +17,9 @@ import mage.game.stack.Spell;
|
|||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class ReplicateAbility extends StaticAbility implements OptionalAdditionalSourceCosts {
|
||||
|
@ -91,12 +86,12 @@ public class ReplicateAbility extends StaticAbility implements OptionalAdditiona
|
|||
String times = "";
|
||||
if (additionalCost.isRepeatable()) {
|
||||
int numActivations = additionalCost.getActivateCount();
|
||||
times = Integer.toString(numActivations + 1) + (numActivations == 0 ? " time " : " times ");
|
||||
times = (numActivations + 1) + (numActivations == 0 ? " time " : " times ");
|
||||
}
|
||||
if (additionalCost.canPay(ability, sourceId, controllerId, game)
|
||||
&& player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(times).append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) {
|
||||
additionalCost.activate();
|
||||
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) {
|
||||
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext(); ) {
|
||||
Cost cost = (Cost) it.next();
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
|
@ -170,7 +165,7 @@ class ReplicateTriggeredAbility extends TriggeredAbilityImpl {
|
|||
if (card != null) {
|
||||
for (Ability ability : card.getAbilities(game)) {
|
||||
if (ability instanceof ReplicateAbility) {
|
||||
if (((ReplicateAbility) ability).isActivated()) {
|
||||
if (ability.isActivated()) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
effect.setValue("ReplicateSpell", spell);
|
||||
effect.setValue("ReplicateCount", ((ReplicateAbility) ability).getActivateCount());
|
||||
|
@ -213,7 +208,7 @@ class ReplicateCopyEffect extends OneShotEffect {
|
|||
if (card != null) {
|
||||
for (Ability ability : card.getAbilities(game)) {
|
||||
if (ability instanceof ReplicateAbility) {
|
||||
if (((ReplicateAbility) ability).isActivated()) {
|
||||
if (ability.isActivated()) {
|
||||
((ReplicateAbility) ability).resetReplicate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ import mage.abilities.Abilities;
|
|||
import mage.abilities.AbilitiesImpl;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.constants.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -26,7 +27,7 @@ public abstract class AdventureCard extends CardImpl {
|
|||
public AdventureCard(AdventureCard card) {
|
||||
super(card);
|
||||
this.spellCard = card.getSpellCard().copy();
|
||||
((AdventureCardSpell)this.spellCard).setParentCard(this);
|
||||
((AdventureCardSpell) this.spellCard).setParentCard(this);
|
||||
}
|
||||
|
||||
public Card getSpellCard() {
|
||||
|
@ -91,6 +92,11 @@ public abstract class AdventureCard extends CardImpl {
|
|||
return allAbilities;
|
||||
}
|
||||
|
||||
public Abilities<Ability> getSharedAbilities() {
|
||||
// abilities without spellcard
|
||||
return super.getAbilities();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwnerId(UUID ownerId) {
|
||||
super.setOwnerId(ownerId);
|
||||
|
|
|
@ -19,7 +19,6 @@ import java.util.List;
|
|||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author phulin
|
||||
*/
|
||||
public class AdventureCardSpellImpl extends CardImpl implements AdventureCardSpell {
|
||||
|
@ -112,7 +111,7 @@ class AdventureCardSpellAbility extends SpellAbility {
|
|||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
ExileZone adventureExileZone = game.getExile().getExileZone(ExileAdventureSpellEffect.adventureExileId(playerId, game));
|
||||
Card spellCard = game.getCard(this.getSourceId());
|
||||
if (spellCard != null && spellCard instanceof AdventureCardSpell) {
|
||||
if (spellCard instanceof AdventureCardSpell) {
|
||||
Card card = ((AdventureCardSpell) spellCard).getParentCard();
|
||||
if (adventureExileZone != null && adventureExileZone.contains(card.getId())) {
|
||||
return ActivationStatus.getFalse();
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package mage.constants;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public enum AsThoughEffectType {
|
||||
|
@ -19,15 +18,29 @@ public enum AsThoughEffectType {
|
|||
BLOCK_FORESTWALK,
|
||||
DAMAGE_NOT_BLOCKED,
|
||||
BE_BLOCKED,
|
||||
PLAY_FROM_NOT_OWN_HAND_ZONE, // do not use dialogs in "applies" method for that type of effect (it calls multiple times)
|
||||
|
||||
// PLAY_FROM_NOT_OWN_HAND_ZONE + CAST_AS_INSTANT:
|
||||
// 1. Do not use dialogs in "applies" method for that type of effect (it calls multiple times and will freeze the game)
|
||||
// 2. All effects in "applies" must checks affectedControllerId.equals(source.getControllerId()) (if not then all players will be able to play it)
|
||||
// 3. Target points to mainCard, but card's characteristics from objectId (split, adventure)
|
||||
// TODO: search all PLAY_FROM_NOT_OWN_HAND_ZONE and CAST_AS_INSTANT effects and add support of mainCard and objectId
|
||||
PLAY_FROM_NOT_OWN_HAND_ZONE,
|
||||
CAST_AS_INSTANT,
|
||||
|
||||
ACTIVATE_AS_INSTANT,
|
||||
DAMAGE,
|
||||
SHROUD,
|
||||
HEXPROOF,
|
||||
PAY_0_ECHO,
|
||||
LOOK_AT_FACE_DOWN,
|
||||
|
||||
// SPEND_OTHER_MANA:
|
||||
// 1. It's uses for mana calcs at any zone, not stack only
|
||||
// 2. Compare zone change counter as "objectZCC <= targetZCC + 1"
|
||||
// 3. Compare zone with original (like exiled) and stack, not stack only
|
||||
// TODO: search all SPEND_ONLY_MANA effects and improve counters compare as SPEND_OTHER_MANA
|
||||
SPEND_OTHER_MANA,
|
||||
|
||||
SPEND_ONLY_MANA,
|
||||
TARGET
|
||||
}
|
||||
|
|
|
@ -20,8 +20,8 @@ import mage.counters.Counter;
|
|||
import mage.counters.Counters;
|
||||
import mage.designations.Designation;
|
||||
import mage.designations.DesignationType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.Graveyard;
|
||||
import mage.game.Table;
|
||||
|
@ -75,7 +75,7 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
void setLife(int life, Game game, UUID sourceId);
|
||||
|
||||
/**
|
||||
* @param amount amount of life loss
|
||||
* @param amount amount of life loss
|
||||
* @param game
|
||||
* @param atCombat was the source combat damage
|
||||
* @return
|
||||
|
@ -351,7 +351,7 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* @param source
|
||||
* @param game
|
||||
* @param targetPlayerId player whose library will be searched
|
||||
* @param triggerEvents whether searching will trigger any game events
|
||||
* @param triggerEvents whether searching will trigger any game events
|
||||
* @return true if search was successful
|
||||
*/
|
||||
boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId, boolean triggerEvents);
|
||||
|
@ -372,23 +372,23 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
/**
|
||||
* Plays a card if possible
|
||||
*
|
||||
* @param card the card that can be cast
|
||||
* @param card the card that can be cast
|
||||
* @param game
|
||||
* @param noMana if it's a spell i can be cast without paying mana
|
||||
* @param noMana if it's a spell i can be cast without paying mana
|
||||
* @param ignoreTiming if it's cast during the resolution of another spell
|
||||
* no sorcery or play land timing restriction are checked. For a land it has
|
||||
* to be the turn of the player playing that card.
|
||||
* @param reference mage object that allows to play the card
|
||||
* no sorcery or play land timing restriction are checked. For a land it has
|
||||
* to be the turn of the player playing that card.
|
||||
* @param reference mage object that allows to play the card
|
||||
* @return
|
||||
*/
|
||||
boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference reference);
|
||||
|
||||
/**
|
||||
* @param card the land card to play
|
||||
* @param card the land card to play
|
||||
* @param game
|
||||
* @param ignoreTiming false - it won't be checked if the stack is empty and
|
||||
* you are able to play a Sorcery. It's still checked, if you are able to
|
||||
* play a land concerning the number of lands you already played.
|
||||
* you are able to play a Sorcery. It's still checked, if you are able to
|
||||
* play a land concerning the number of lands you already played.
|
||||
* @return
|
||||
*/
|
||||
boolean playLand(Card card, Game game, boolean ignoreTiming);
|
||||
|
@ -534,11 +534,11 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
/**
|
||||
* Moves the cards from cards to the bottom of the players library.
|
||||
*
|
||||
* @param cards - list of cards that have to be moved
|
||||
* @param game - game
|
||||
* @param cards - list of cards that have to be moved
|
||||
* @param game - game
|
||||
* @param anyOrder - true if player can determine the order of the cards
|
||||
* else random order
|
||||
* @param source - source ability
|
||||
* else random order
|
||||
* @param source - source ability
|
||||
* @return
|
||||
*/
|
||||
boolean putCardsOnBottomOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder);
|
||||
|
@ -559,10 +559,10 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
/**
|
||||
* Moves the cards from cards to the top of players library.
|
||||
*
|
||||
* @param cards - list of cards that have to be moved
|
||||
* @param game - game
|
||||
* @param cards - list of cards that have to be moved
|
||||
* @param game - game
|
||||
* @param anyOrder - true if player can determine the order of the cards
|
||||
* @param source - source ability
|
||||
* @param source - source ability
|
||||
* @return
|
||||
*/
|
||||
boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder);
|
||||
|
@ -593,8 +593,8 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
/**
|
||||
* Choose the order in which blockers get damage assigned to
|
||||
*
|
||||
* @param blockers list of blockers where to choose the next one from
|
||||
* @param combatGroup the concerning combat group
|
||||
* @param blockers list of blockers where to choose the next one from
|
||||
* @param combatGroup the concerning combat group
|
||||
* @param blockerOrder the already set order of blockers
|
||||
* @param game
|
||||
* @return blocker next to add to the blocker order
|
||||
|
@ -664,7 +664,7 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* @param card
|
||||
* @param game
|
||||
* @param abilitiesToActivate extra info about abilities that can be
|
||||
* activated on NO option
|
||||
* activated on NO option
|
||||
* @return player looked at the card
|
||||
*/
|
||||
boolean lookAtFaceDownCard(Card card, Game game, int abilitiesToActivate);
|
||||
|
@ -737,11 +737,11 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* @param toZone
|
||||
* @param source
|
||||
* @param game
|
||||
* @param tapped the cards are tapped on the battlefield
|
||||
* @param faceDown the cards are face down in the to zone
|
||||
* @param byOwner the card is moved (or put onto battlefield) by the owner
|
||||
* of the card and if target zone is battlefield controls the permanent
|
||||
* (instead of the controller of the source)
|
||||
* @param tapped the cards are tapped on the battlefield
|
||||
* @param faceDown the cards are face down in the to zone
|
||||
* @param byOwner the card is moved (or put onto battlefield) by the owner
|
||||
* of the card and if target zone is battlefield controls the permanent
|
||||
* (instead of the controller of the source)
|
||||
* @param appliedEffects
|
||||
* @return
|
||||
*/
|
||||
|
@ -777,7 +777,7 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* list of applied effects is not saved
|
||||
*
|
||||
* @param card
|
||||
* @param exileId exile zone id (optional)
|
||||
* @param exileId exile zone id (optional)
|
||||
* @param exileName name of exile zone (optional)
|
||||
* @param sourceId
|
||||
* @param game
|
||||
|
@ -819,7 +819,7 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* @param sourceId
|
||||
* @param game
|
||||
* @param fromZone if null, this info isn't postet
|
||||
* @param toTop to the top of the library else to the bottom
|
||||
* @param toTop to the top of the library else to the bottom
|
||||
* @param withName show the card name in the log
|
||||
* @return
|
||||
*/
|
||||
|
@ -844,18 +844,20 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* without mana (null) or the mana set to manaCosts instead of its normal
|
||||
* mana costs.
|
||||
*
|
||||
* @param sourceId the source that can be cast without mana
|
||||
* @param sourceId the source that can be cast without mana
|
||||
* @param manaCosts alternate ManaCost, null if it can be cast without mana
|
||||
* cost
|
||||
* @param costs alternate other costs you need to pay
|
||||
* cost
|
||||
* @param costs alternate other costs you need to pay
|
||||
*/
|
||||
void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts<ManaCost> manaCosts, Costs<Cost> costs);
|
||||
|
||||
UUID getCastSourceIdWithAlternateMana();
|
||||
Set<UUID> getCastSourceIdWithAlternateMana();
|
||||
|
||||
ManaCosts<ManaCost> getCastSourceIdManaCosts();
|
||||
Map<UUID, ManaCosts<ManaCost>> getCastSourceIdManaCosts();
|
||||
|
||||
Costs<Cost> getCastSourceIdCosts();
|
||||
Map<UUID, Costs<Cost>> getCastSourceIdCosts();
|
||||
|
||||
void clearCastSourceIdManaCosts();
|
||||
|
||||
// permission handling to show hand cards
|
||||
void addPermissionToShowHandCards(UUID watcherUserId);
|
||||
|
|
|
@ -32,8 +32,8 @@ import mage.counters.Counters;
|
|||
import mage.designations.Designation;
|
||||
import mage.designations.DesignationType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.common.FilterCreatureForCombat;
|
||||
import mage.filter.common.FilterCreatureForCombatBlock;
|
||||
|
@ -160,9 +160,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
protected boolean reachedNextTurnAfterLeaving = false;
|
||||
|
||||
// indicates that the spell with the set sourceId can be cast with an alternate mana costs (can also be no mana costs)
|
||||
protected UUID castSourceIdWithAlternateMana;
|
||||
protected ManaCosts<ManaCost> castSourceIdManaCosts;
|
||||
protected Costs<Cost> castSourceIdCosts;
|
||||
// support multiple cards with alternative mana cost
|
||||
protected Set<UUID> castSourceIdWithAlternateMana = new HashSet<>();
|
||||
protected Map<UUID, ManaCosts<ManaCost>> castSourceIdManaCosts = new HashMap<>();
|
||||
protected Map<UUID, Costs<Cost>> castSourceIdCosts = new HashMap<>();
|
||||
|
||||
// indicates that the player is in mana payment phase
|
||||
protected boolean payManaMode = false;
|
||||
|
@ -271,9 +272,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.priorityTimeLeft = player.getPriorityTimeLeft();
|
||||
this.reachedNextTurnAfterLeaving = player.reachedNextTurnAfterLeaving;
|
||||
|
||||
this.castSourceIdWithAlternateMana = player.castSourceIdWithAlternateMana;
|
||||
this.castSourceIdManaCosts = player.castSourceIdManaCosts;
|
||||
this.castSourceIdCosts = player.castSourceIdCosts;
|
||||
this.castSourceIdWithAlternateMana.addAll(player.castSourceIdWithAlternateMana);
|
||||
this.castSourceIdManaCosts.putAll(player.castSourceIdManaCosts);
|
||||
this.castSourceIdCosts.putAll(player.castSourceIdCosts);
|
||||
|
||||
this.payManaMode = player.payManaMode;
|
||||
this.phyrexianColors = player.phyrexianColors.copy();
|
||||
|
||||
|
@ -340,9 +342,12 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.turnControllers.clear();
|
||||
this.turnControllers.addAll(player.getTurnControllers());
|
||||
this.reachedNextTurnAfterLeaving = player.hasReachedNextTurnAfterLeaving();
|
||||
this.castSourceIdWithAlternateMana = player.getCastSourceIdWithAlternateMana();
|
||||
this.castSourceIdManaCosts = player.getCastSourceIdManaCosts();
|
||||
this.castSourceIdCosts = player.getCastSourceIdCosts();
|
||||
|
||||
this.clearCastSourceIdManaCosts();
|
||||
this.castSourceIdWithAlternateMana.addAll(player.getCastSourceIdWithAlternateMana());
|
||||
this.castSourceIdManaCosts.putAll(player.getCastSourceIdManaCosts());
|
||||
this.castSourceIdCosts.putAll(player.getCastSourceIdCosts());
|
||||
|
||||
this.phyrexianColors = player.getPhyrexianColors().copy();
|
||||
|
||||
this.designations.clear();
|
||||
|
@ -417,9 +422,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.setLife(game.getLife(), game, (UUID) null);
|
||||
this.setReachedNextTurnAfterLeaving(false);
|
||||
|
||||
this.castSourceIdWithAlternateMana = null;
|
||||
this.castSourceIdManaCosts = null;
|
||||
this.castSourceIdCosts = null;
|
||||
this.clearCastSourceIdManaCosts();
|
||||
|
||||
this.getManaPool().init(); // needed to remove mana that not empties on step change from previous game if left
|
||||
this.phyrexianColors = new FilterMana();
|
||||
|
||||
|
@ -444,9 +448,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.canPlayCardsFromGraveyard = false;
|
||||
this.topCardRevealed = false;
|
||||
this.alternativeSourceCosts.clear();
|
||||
this.castSourceIdWithAlternateMana = null;
|
||||
this.castSourceIdManaCosts = null;
|
||||
this.castSourceIdCosts = null;
|
||||
this.clearCastSourceIdManaCosts();
|
||||
this.getManaPool().clearEmptyManaPoolRules();
|
||||
this.phyrexianColors = new FilterMana();
|
||||
}
|
||||
|
@ -617,7 +619,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (abilities.containsKey(HexproofAbility.getInstance().getId())) {
|
||||
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
||||
&& null == game.getContinuousEffects().asThough(this.getId(),
|
||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)) {
|
||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -625,7 +627,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (abilities.containsKey(HexproofFromWhiteAbility.getInstance().getId())) {
|
||||
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
||||
&& null == game.getContinuousEffects().asThough(this.getId(),
|
||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
||||
&& source.getColor(game).isWhite()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -634,7 +636,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (abilities.containsKey(HexproofFromBlueAbility.getInstance().getId())) {
|
||||
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
||||
&& null == game.getContinuousEffects().asThough(this.getId(),
|
||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
||||
&& source.getColor(game).isBlue()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -643,7 +645,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (abilities.containsKey(HexproofFromBlackAbility.getInstance().getId())) {
|
||||
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
||||
&& null == game.getContinuousEffects().asThough(this.getId(),
|
||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
||||
&& source.getColor(game).isBlack()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -652,7 +654,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (abilities.containsKey(HexproofFromMonocoloredAbility.getInstance().getId())) {
|
||||
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
||||
&& null == game.getContinuousEffects().asThough(this.getId(),
|
||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
||||
&& !source.getColor(game).isColorless()
|
||||
&& !source.getColor(game).isMulticolored()) {
|
||||
return false;
|
||||
|
@ -695,7 +697,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
game.informPlayers(getLogName() + " discards down to "
|
||||
+ this.maxHandSize
|
||||
+ (this.maxHandSize == 1
|
||||
? " hand card" : " hand cards"));
|
||||
? " hand card" : " hand cards"));
|
||||
}
|
||||
discard(hand.size() - this.maxHandSize, null, game);
|
||||
}
|
||||
|
@ -813,7 +815,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (card != null) {
|
||||
GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD,
|
||||
card.getId(), source == null
|
||||
? null : source.getSourceId(), playerId);
|
||||
? null : source.getSourceId(), playerId);
|
||||
gameEvent.setFlag(source != null); // event from effect or from cost (source == null)
|
||||
if (!game.replaceEvent(gameEvent, source)) {
|
||||
// write info to game log first so game log infos from triggered or replacement effects follow in the game log
|
||||
|
@ -828,7 +830,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
// So discard is also successful if card is moved to another zone by replacement effect!
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARD,
|
||||
card.getId(), source == null
|
||||
? null : source.getSourceId(), playerId));
|
||||
? null : source.getSourceId(), playerId));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1061,26 +1063,33 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts<ManaCost> manaCosts, Costs<Cost> costs) {
|
||||
castSourceIdWithAlternateMana = sourceId;
|
||||
castSourceIdManaCosts = manaCosts;
|
||||
castSourceIdCosts = costs;
|
||||
castSourceIdWithAlternateMana.add(sourceId);
|
||||
castSourceIdManaCosts.put(sourceId, manaCosts);
|
||||
castSourceIdCosts.put(sourceId, costs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getCastSourceIdWithAlternateMana() {
|
||||
public Set<UUID> getCastSourceIdWithAlternateMana() {
|
||||
return castSourceIdWithAlternateMana;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Costs<Cost> getCastSourceIdCosts() {
|
||||
public Map<UUID, Costs<Cost>> getCastSourceIdCosts() {
|
||||
return castSourceIdCosts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManaCosts getCastSourceIdManaCosts() {
|
||||
public Map<UUID, ManaCosts<ManaCost>> getCastSourceIdManaCosts() {
|
||||
return castSourceIdManaCosts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCastSourceIdManaCosts() {
|
||||
this.castSourceIdCosts.clear();
|
||||
this.castSourceIdManaCosts.clear();
|
||||
this.castSourceIdWithAlternateMana.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPayManaMode(boolean payManaMode) {
|
||||
this.payManaMode = payManaMode;
|
||||
|
@ -1144,11 +1153,13 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
// Update the zcc to the stack
|
||||
ability.setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(ability.getSourceId()));
|
||||
|
||||
// ALTERNATIVE COST from dynamic effects
|
||||
// some effects set sourceId to cast without paying mana costs or other costs
|
||||
if (ability.getSourceId().equals(getCastSourceIdWithAlternateMana())) {
|
||||
if (getCastSourceIdWithAlternateMana().contains(ability.getSourceId())) {
|
||||
Ability spellAbility = spell.getSpellAbility();
|
||||
ManaCosts alternateCosts = getCastSourceIdManaCosts();
|
||||
Costs<Cost> costs = getCastSourceIdCosts();
|
||||
ManaCosts alternateCosts = getCastSourceIdManaCosts().get(ability.getSourceId());
|
||||
Costs<Cost> costs = getCastSourceIdCosts().get(ability.getSourceId());
|
||||
if (alternateCosts == null) {
|
||||
noMana = true;
|
||||
} else {
|
||||
|
@ -1162,7 +1173,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
spellAbility.getCosts().addAll(costs);
|
||||
}
|
||||
}
|
||||
setCastSourceIdWithAlternateMana(null, null, null);
|
||||
clearCastSourceIdManaCosts(); // TODO: test multiple alternative cost for different cards as same time
|
||||
|
||||
GameEvent event = GameEvent.getEvent(GameEvent.EventType.CAST_SPELL,
|
||||
spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId, permittingObject);
|
||||
game.fireEvent(event);
|
||||
|
@ -1506,7 +1518,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
// Also called on the whole split card but only passing the fuse ability and other whole-split-card shared abilities
|
||||
// as candidates.
|
||||
private void getUseableActivatedAbilitiesHalfImpl(MageObject object, Zone zone, Game game, Abilities<Ability> candidateAbilites,
|
||||
LinkedHashMap<UUID, ActivatedAbility> output) {
|
||||
LinkedHashMap<UUID, ActivatedAbility> output) {
|
||||
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
|
||||
ManaOptions availableMana = null;
|
||||
// ManaOptions availableMana = getManaAvailable(game); // can only be activated if mana calculation works flawless otherwise player can't play spells they could play if calculation would work correctly
|
||||
|
@ -1555,10 +1567,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
!= null
|
||||
// if anyone sees an issue with this code, please report it. Worked in my testing.
|
||||
|| game.getContinuousEffects().asThough(object.getId(),
|
||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE,
|
||||
ability,
|
||||
this.getId(),
|
||||
game)
|
||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE,
|
||||
ability,
|
||||
this.getId(),
|
||||
game)
|
||||
!= null) {
|
||||
if (canUse
|
||||
|| ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
|
||||
|
@ -1913,9 +1925,9 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
private List<Permanent> getPermanentsThatCanBeUntapped(Game game,
|
||||
List<Permanent> canBeUntapped,
|
||||
RestrictionUntapNotMoreThanEffect handledEffect,
|
||||
Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
|
||||
List<Permanent> canBeUntapped,
|
||||
RestrictionUntapNotMoreThanEffect handledEffect,
|
||||
Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
|
||||
List<Permanent> leftForUntap = new ArrayList<>();
|
||||
// select permanents that can still be untapped
|
||||
for (Permanent permanent : canBeUntapped) {
|
||||
|
@ -2623,7 +2635,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId,
|
||||
boolean triggerEvents) {
|
||||
boolean triggerEvents) {
|
||||
//20091005 - 701.14c
|
||||
Library searchedLibrary = null;
|
||||
String searchInfo = null;
|
||||
|
@ -2827,7 +2839,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
/**
|
||||
* @param game
|
||||
* @param appliedEffects
|
||||
* @param numSides Number of sides the dice has
|
||||
* @param numSides Number of sides the dice has
|
||||
* @return the number that the player rolled
|
||||
*/
|
||||
@Override
|
||||
|
@ -2864,16 +2876,16 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
/**
|
||||
* @param game
|
||||
* @param appliedEffects
|
||||
* @param numberChaosSides The number of chaos sides the planar die
|
||||
* currently has (normally 1 but can be 5)
|
||||
* @param numberChaosSides The number of chaos sides the planar die
|
||||
* currently has (normally 1 but can be 5)
|
||||
* @param numberPlanarSides The number of chaos sides the planar die
|
||||
* currently has (normally 1)
|
||||
* currently has (normally 1)
|
||||
* @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
|
||||
* or NilRoll
|
||||
*/
|
||||
@Override
|
||||
public PlanarDieRoll rollPlanarDie(Game game, ArrayList<UUID> appliedEffects, int numberChaosSides,
|
||||
int numberPlanarSides) {
|
||||
int numberPlanarSides) {
|
||||
int result = RandomUtil.nextInt(9) + 1;
|
||||
PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL;
|
||||
if (numberChaosSides + numberPlanarSides > 9) {
|
||||
|
@ -3030,7 +3042,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
/**
|
||||
* @param ability
|
||||
* @param available if null, it won't be checked if enough mana is available
|
||||
* @param available if null, it won't be checked if enough mana is available
|
||||
* @param sourceObject
|
||||
* @param game
|
||||
* @return
|
||||
|
@ -3046,7 +3058,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
game.getContinuousEffects().costModification(copy, game);
|
||||
}
|
||||
|
||||
Card card = game.getCard(ability.getSourceId());
|
||||
Card card = game.getCard(copy.getSourceId());
|
||||
if (card != null) {
|
||||
for (Ability ability0 : card.getAbilities()) {
|
||||
if (ability0 instanceof AdjustingSourceCosts) {
|
||||
|
@ -3072,10 +3084,16 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (available == null) {
|
||||
return true;
|
||||
}
|
||||
MageObjectReference permittingObject = game.getContinuousEffects().asThough(ability.getSourceId(),
|
||||
AsThoughEffectType.SPEND_OTHER_MANA, ability, ability.getControllerId(), game);
|
||||
MageObjectReference permittingObject = game.getContinuousEffects().asThough(copy.getSourceId(),
|
||||
AsThoughEffectType.SPEND_OTHER_MANA, copy, copy.getControllerId(), game);
|
||||
for (Mana mana : abilityOptions) {
|
||||
for (Mana avail : available) {
|
||||
// TODO: SPEND_OTHER_MANA effects with getAsThoughManaType can change mana type to pay,
|
||||
// but that code processing it as any color, need to test and fix another use cases
|
||||
// (example: Sunglasses of Urza - may spend white mana as though it were red mana)
|
||||
|
||||
//
|
||||
// add tests for non any color like Sunglasses of Urza
|
||||
if (permittingObject != null && mana.count() <= avail.count()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -3087,14 +3105,35 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
// ALTERNATIVE COST from source card (AlternativeCostSourceAbility)
|
||||
for (Ability objectAbility : sourceObject.getAbilities()) {
|
||||
if (objectAbility instanceof AlternativeCostSourceAbility) {
|
||||
if (objectAbility.getCosts().canPay(ability, ability.getSourceId(), playerId, game)) {
|
||||
if (objectAbility.getCosts().canPay(copy, copy.getSourceId(), playerId, game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return canPlayCardByAlternateCost(card, available, ability, game);
|
||||
|
||||
// ALTERNATIVE COST FROM dynamic effects
|
||||
if (getCastSourceIdWithAlternateMana().contains(copy.getSourceId())) {
|
||||
ManaCosts alternateCosts = getCastSourceIdManaCosts().get(copy.getSourceId());
|
||||
Costs<Cost> costs = getCastSourceIdCosts().get(copy.getSourceId());
|
||||
|
||||
boolean canPutToPlay = true;
|
||||
if (alternateCosts != null && !alternateCosts.canPay(copy, copy.getSourceId(), playerId, game)) {
|
||||
canPutToPlay = false;
|
||||
}
|
||||
if (costs != null && !costs.canPay(copy, copy.getSourceId(), playerId, game)) {
|
||||
canPutToPlay = false;
|
||||
}
|
||||
|
||||
if (canPutToPlay) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ALTERNATIVE COST from source card (any AlternativeSourceCosts)
|
||||
return canPlayCardByAlternateCost(card, available, copy, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -3215,35 +3254,85 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
private List<Ability> cardPlayableAbilities(Game game, Card card, boolean setControllerId) {
|
||||
List<Ability> playable = new ArrayList();
|
||||
if (card != null) {
|
||||
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) {
|
||||
if (!ability.canActivate(playerId, game).canActivate()) {
|
||||
continue;
|
||||
}
|
||||
private void getPlayableFromNonHandCardAll(Game game, Zone fromZone, Card card, ManaOptions availableMana, List<Ability> output) {
|
||||
if (fromZone == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
UUID savedControllerId = null;
|
||||
if (setControllerId) {
|
||||
// For when owner != caster, e.g. with Psychic Intrusion and similar effects.
|
||||
savedControllerId = getId();
|
||||
ability.setControllerId(getId());
|
||||
}
|
||||
if (ability instanceof SpellAbility
|
||||
&& null != game.getContinuousEffects().asThough(card.getId(),
|
||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, ability, getId(), game)) {
|
||||
playable.add(ability);
|
||||
} else if (ability instanceof PlayLandAbility
|
||||
&& null != game.getContinuousEffects().asThough(card.getId(),
|
||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), getId(), game)) {
|
||||
playable.add(ability);
|
||||
}
|
||||
if (setControllerId) {
|
||||
ability.setControllerId(savedControllerId);
|
||||
}
|
||||
// BASIC abilities
|
||||
if (card instanceof SplitCard) {
|
||||
SplitCard splitCard = (SplitCard) card;
|
||||
getPlayableFromNonHandCardSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(), availableMana, output);
|
||||
getPlayableFromNonHandCardSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(), availableMana, output);
|
||||
getPlayableFromNonHandCardSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(), availableMana, output);
|
||||
} else if (card instanceof AdventureCard) {
|
||||
// adventure must use different card characteristics for different spells (main or adventure)
|
||||
AdventureCard adventureCard = (AdventureCard) card;
|
||||
getPlayableFromNonHandCardSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(), availableMana, output);
|
||||
getPlayableFromNonHandCardSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(), availableMana, output);
|
||||
} else {
|
||||
getPlayableFromNonHandCardSingle(game, fromZone, card, card.getAbilities(), availableMana, output);
|
||||
}
|
||||
|
||||
// DYNAMIC ADDED abilities
|
||||
if (fromZone != Zone.ALL) { // TODO: test revealed cards with dynamic added abilities
|
||||
// Other activated abilities (added dynamic by effects)
|
||||
LinkedHashMap<UUID, ActivatedAbility> useable;
|
||||
if (card instanceof AdventureCard) {
|
||||
// adventure cards (contains two different cards: main and adventure spell)
|
||||
useable = new LinkedHashMap<>();
|
||||
getOtherUseableActivatedAbilities(((AdventureCard) card).getSpellCard(), fromZone, game, useable);
|
||||
output.addAll(useable.values());
|
||||
|
||||
useable = new LinkedHashMap<>();
|
||||
getOtherUseableActivatedAbilities(card, fromZone, game, useable);
|
||||
output.addAll(useable.values());
|
||||
} else {
|
||||
// all other cards (TODO: check split cards with dynamic added abilities)
|
||||
useable = new LinkedHashMap<>();
|
||||
getOtherUseableActivatedAbilities(card, fromZone, game, useable);
|
||||
output.addAll(useable.values());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void getPlayableFromNonHandCardSingle(Game game, Zone fromZone, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
|
||||
|
||||
// check "can play from hand" condition as original controller (effects checks affected controller with source controller)
|
||||
// TODO: remove card.getSpellAbility() ?
|
||||
MageObjectReference permittingObject = game.getContinuousEffects().asThough(card.getId(),
|
||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), this.getId(), game);
|
||||
boolean canActivateAsHandZone = permittingObject != null
|
||||
|| (fromZone == Zone.GRAVEYARD && canPlayCardsFromGraveyard());
|
||||
|
||||
// check "can play" condition as affected controller
|
||||
for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
|
||||
UUID savedControllerId = ability.getControllerId();
|
||||
ability.setControllerId(this.getId());
|
||||
try {
|
||||
boolean possibleToPlay = false;
|
||||
|
||||
// spell/hand abilities (play from all zones)
|
||||
// need permitingObject or canPlayCardsFromGraveyard
|
||||
if (canActivateAsHandZone
|
||||
&& ability.getZone().match(Zone.HAND)
|
||||
&& (ability instanceof SpellAbility || ability instanceof PlayLandAbility)) {
|
||||
possibleToPlay = true;
|
||||
}
|
||||
|
||||
// zone's abilities (play from specific zone)
|
||||
// no need in permitingObject
|
||||
if (fromZone != Zone.ALL && ability.getZone().match(fromZone)) {
|
||||
possibleToPlay = true;
|
||||
}
|
||||
|
||||
if (possibleToPlay && canPlay(ability, availableMana, card, game)) {
|
||||
output.add(ability);
|
||||
}
|
||||
} finally {
|
||||
ability.setControllerId(savedControllerId);
|
||||
}
|
||||
}
|
||||
return playable;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3262,18 +3351,16 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
boolean fromAll = fromZone.equals(Zone.ALL);
|
||||
Collection<Card> cards;
|
||||
|
||||
if (hidden && (fromAll || fromZone == Zone.HAND)) {
|
||||
cards = hideDuplicatedAbilities ? hand.getUniqueCards(game) : hand.getCards(game);
|
||||
for (Card card : cards) {
|
||||
for (Card card : hand.getCards(game)) {
|
||||
for (Ability ability : card.getAbilities(game)) { // gets this activated ability from hand? (Morph?)
|
||||
if (ability.getZone().match(Zone.HAND)) {
|
||||
if (ability instanceof ActivatedAbility) {
|
||||
if (!(ability instanceof PlayLandAbility)
|
||||
|| !game.getContinuousEffects().preventedByRuleModification(
|
||||
GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, ability.getSourceId(),
|
||||
ability.getSourceId(), playerId), ability, game, true)) {
|
||||
GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, ability.getSourceId(),
|
||||
ability.getSourceId(), playerId), ability, game, true)) {
|
||||
if (canPlay((ActivatedAbility) ability, availableMana, card, game)) {
|
||||
playable.add(ability);
|
||||
}
|
||||
|
@ -3297,37 +3384,15 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
if (fromAll || fromZone == Zone.GRAVEYARD) {
|
||||
cards = hideDuplicatedAbilities ? graveyard.getUniqueCards(game) : graveyard.getCards(game);
|
||||
for (Card card : cards) {
|
||||
// Handle split cards in graveyard to support Aftermath
|
||||
if (card instanceof SplitCard) {
|
||||
SplitCard splitCard = (SplitCard) card;
|
||||
getPlayableFromGraveyardCard(game, splitCard.getLeftHalfCard(),
|
||||
splitCard.getLeftHalfCard().getAbilities(), availableMana, playable);
|
||||
getPlayableFromGraveyardCard(game, splitCard.getRightHalfCard(),
|
||||
splitCard.getRightHalfCard().getAbilities(), availableMana, playable);
|
||||
getPlayableFromGraveyardCard(game, splitCard, splitCard.getSharedAbilities(),
|
||||
availableMana, playable);
|
||||
} else if (card instanceof AdventureCard) {
|
||||
AdventureCard adventureCard = (AdventureCard) card;
|
||||
getPlayableFromGraveyardCard(game, adventureCard.getSpellCard(),
|
||||
adventureCard.getSpellCard().getAbilities(), availableMana, playable);
|
||||
getPlayableFromGraveyardCard(game, adventureCard, adventureCard.getAbilities(), availableMana, playable);
|
||||
} else {
|
||||
getPlayableFromGraveyardCard(game, card, card.getAbilities(), availableMana, playable);
|
||||
}
|
||||
|
||||
// Other activated abilities
|
||||
LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>();
|
||||
getOtherUseableActivatedAbilities(card, Zone.GRAVEYARD, game, useable);
|
||||
playable.addAll(useable.values());
|
||||
for (Card card : graveyard.getCards(game)) {
|
||||
getPlayableFromNonHandCardAll(game, Zone.GRAVEYARD, card, availableMana, playable);
|
||||
}
|
||||
}
|
||||
|
||||
if (fromAll || fromZone == Zone.EXILED) {
|
||||
for (ExileZone exile : game.getExile().getExileZones()) {
|
||||
for (Card card : exile.getCards(game)) {
|
||||
playable.addAll(cardPlayableAbilities(game, card, true));
|
||||
getPlayableFromNonHandCardAll(game, Zone.EXILED, card, availableMana, playable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3336,7 +3401,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (fromAll) {
|
||||
for (Cards revealedCards : game.getState().getRevealed().values()) {
|
||||
for (Card card : revealedCards.getCards(game)) {
|
||||
playable.addAll(cardPlayableAbilities(game, card, false));
|
||||
// revealed cards can be from any zones
|
||||
getPlayableFromNonHandCardAll(game, game.getState().getZone(card.getId()), card, availableMana, playable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3348,7 +3414,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (player != null) {
|
||||
if (/*player.isTopCardRevealed() &&*/player.getLibrary().hasCards()) {
|
||||
Card card = player.getLibrary().getFromTop(game);
|
||||
playable.addAll(cardPlayableAbilities(game, card, false));
|
||||
getPlayableFromNonHandCardAll(game, Zone.LIBRARY, card, availableMana, playable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3615,7 +3681,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId,
|
||||
UUID controllerId, Game game
|
||||
UUID controllerId, Game game
|
||||
) {
|
||||
return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game);
|
||||
}
|
||||
|
@ -3769,8 +3835,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCards(Card card, Zone toZone,
|
||||
Ability source, Game game,
|
||||
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
||||
Ability source, Game game,
|
||||
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
||||
) {
|
||||
Set<Card> cardList = new HashSet<>();
|
||||
if (card != null) {
|
||||
|
@ -3781,22 +3847,22 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCards(Cards cards, Zone toZone,
|
||||
Ability source, Game game
|
||||
Ability source, Game game
|
||||
) {
|
||||
return moveCards(cards.getCards(game), toZone, source, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveCards(Set<Card> cards, Zone toZone,
|
||||
Ability source, Game game
|
||||
Ability source, Game game
|
||||
) {
|
||||
return moveCards(cards, toZone, source, game, false, false, false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveCards(Set<Card> cards, Zone toZone,
|
||||
Ability source, Game game,
|
||||
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
||||
Ability source, Game game,
|
||||
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
||||
) {
|
||||
if (cards.isEmpty()) {
|
||||
return true;
|
||||
|
@ -3891,8 +3957,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCardsToExile(Card card, Ability source,
|
||||
Game game, boolean withName, UUID exileId,
|
||||
String exileZoneName
|
||||
Game game, boolean withName, UUID exileId,
|
||||
String exileZoneName
|
||||
) {
|
||||
Set<Card> cards = new HashSet<>();
|
||||
cards.add(card);
|
||||
|
@ -3901,8 +3967,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCardsToExile(Set<Card> cards, Ability source,
|
||||
Game game, boolean withName, UUID exileId,
|
||||
String exileZoneName
|
||||
Game game, boolean withName, UUID exileId,
|
||||
String exileZoneName
|
||||
) {
|
||||
if (cards.isEmpty()) {
|
||||
return true;
|
||||
|
@ -3918,14 +3984,14 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
|
||||
Game game
|
||||
Game game
|
||||
) {
|
||||
return this.moveCardToHandWithInfo(card, sourceId, game, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
|
||||
Game game, boolean withName
|
||||
Game game, boolean withName
|
||||
) {
|
||||
boolean result = false;
|
||||
Zone fromZone = game.getState().getZone(card.getId());
|
||||
|
@ -3950,7 +4016,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source,
|
||||
Game game, Zone fromZone
|
||||
Game game, Zone fromZone
|
||||
) {
|
||||
UUID sourceId = source == null ? null : source.getSourceId();
|
||||
Set<Card> movedCards = new LinkedHashSet<>();
|
||||
|
@ -3958,7 +4024,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
// identify cards from one owner
|
||||
Cards cards = new CardsImpl();
|
||||
UUID ownerId = null;
|
||||
for (Iterator<Card> it = allCards.iterator(); it.hasNext();) {
|
||||
for (Iterator<Card> it = allCards.iterator(); it.hasNext(); ) {
|
||||
Card card = it.next();
|
||||
if (cards.isEmpty()) {
|
||||
ownerId = card.getOwnerId();
|
||||
|
@ -4021,7 +4087,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId,
|
||||
Game game, Zone fromZone
|
||||
Game game, Zone fromZone
|
||||
) {
|
||||
if (card == null) {
|
||||
return false;
|
||||
|
@ -4050,8 +4116,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId,
|
||||
Game game, Zone fromZone,
|
||||
boolean toTop, boolean withName
|
||||
Game game, Zone fromZone,
|
||||
boolean toTop, boolean withName
|
||||
) {
|
||||
if (card == null) {
|
||||
return false;
|
||||
|
@ -4085,7 +4151,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId,
|
||||
Game game, Zone fromZone, boolean withName) {
|
||||
Game game, Zone fromZone, boolean withName) {
|
||||
if (card == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -4108,7 +4174,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName()
|
||||
+ (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '
|
||||
+ (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH)
|
||||
+ ' ' : "") + "to the exile zone");
|
||||
+ ' ' : "") + "to the exile zone");
|
||||
|
||||
}
|
||||
result = true;
|
||||
|
|
Loading…
Reference in a new issue