separate 'you may play'|'you may cast' AsThoughtEffect approuvers

Also, reworked Gonti, Lord of Luxury and checks it now works properly with Zoetic Cavern.
This commit is contained in:
Susucre 2024-04-13 12:10:53 +02:00 committed by GitHub
parent 31295eb645
commit c77634c843
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
142 changed files with 464 additions and 531 deletions

View file

@ -118,7 +118,9 @@ public class SpellAbility extends ActivatedAbilityImpl {
}
// play from not own hand
Set<ApprovingObject> approvingObjects = game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this, playerId, game);
Set<ApprovingObject> approvingObjects = new HashSet<>();
approvingObjects.addAll(game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this, playerId, game));
approvingObjects.addAll(game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, this, playerId, game));
if (approvingObjects.isEmpty() && getSpellAbilityType().equals(SpellAbilityType.ADVENTURE_SPELL)) {
// allowed to cast adventures from non-hand?
approvingObjects = game.getContinuousEffects().asThough(getSourceId(), AsThoughEffectType.CAST_ADVENTURE_FROM_NOT_OWN_HAND_ZONE, this, playerId, game);

View file

@ -48,7 +48,7 @@ class CastFromGraveyardOnceEffect extends AsThoughEffectImpl {
private final FilterCard filter;
CastFromGraveyardOnceEffect(FilterCard filter) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
this.filter = filter;
this.staticText = "Once during each of your turns, you may cast " + filter.getMessage()
+ (filter.getMessage().contains("from your graveyard") ? "" : " from your graveyard");

View file

@ -3,7 +3,6 @@ package mage.abilities.common;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.Mode;
import mage.abilities.costs.common.ExileSourceFromHandCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.ContinuousEffectImpl;
@ -68,7 +67,7 @@ class CastExiledFromHandCardEffect extends OneShotEffect {
.map(MageObjectReference.class::cast)
.map(mor -> mor.getCard(game))
.ifPresent(card -> CardUtil.makeCardPlayable(
game, source, card, Duration.Custom, false
game, source, card, true, Duration.Custom, false
));
return true;
}
@ -83,12 +82,12 @@ class GainManaAbilitiesWhileExiledEffect extends ContinuousEffectImpl {
this.colors = colors;
this.staticText =
"target land gains \"{T}: Add " +
CardUtil.concatWithOr(
Arrays.stream(colors.split(""))
.map(s -> '{' + s + '}')
.collect(Collectors.toList())
) +
"\" until {this} is cast from exile";
CardUtil.concatWithOr(
Arrays.stream(colors.split(""))
.map(s -> '{' + s + '}')
.collect(Collectors.toList())
) +
"\" until {this} is cast from exile";
}
private GainManaAbilitiesWhileExiledEffect(final GainManaAbilitiesWhileExiledEffect effect) {

View file

@ -34,7 +34,7 @@ public class MayCastFromGraveyardSourceAbility extends StaticAbility {
class MayCastFromGraveyardEffect extends AsThoughEffectImpl {
MayCastFromGraveyardEffect() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.PutCreatureInPlay);
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.PutCreatureInPlay);
staticText = "you may cast {this} from your graveyard";
}

View file

@ -0,0 +1,102 @@
package mage.abilities.effects;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.common.asthought.MayLookAtTargetCardEffect;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CastManaAdjustment;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* This exiles the target card or cards.
* Each can be looked at by the source's controller.
* For each card exiled this way, that player may play|cast that card as long as it stays exiled. (+ mana adjustement)
* e.g. [[Gonti, Lord of Luxury]]
*
* @author Susucr
*/
public class ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect extends OneShotEffect {
private final boolean useCastSpellOnly;
private final CastManaAdjustment manaAdjustment;
public ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect(boolean useCastSpellOnly, CastManaAdjustment manaAdjustment) {
super(Outcome.Exile);
switch (manaAdjustment) {
case NONE:
case AS_THOUGH_ANY_MANA_TYPE:
case AS_THOUGH_ANY_MANA_COLOR:
this.manaAdjustment = manaAdjustment;
break;
case WITHOUT_PAYING_MANA_COST: // TODO when needed
default:
throw new IllegalArgumentException("Wrong code usage, manaAdjustment is not yet supported: " + manaAdjustment);
}
this.useCastSpellOnly = useCastSpellOnly;
}
private ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect(final ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect effect) {
super(effect);
this.manaAdjustment = effect.manaAdjustment;
this.useCastSpellOnly = effect.useCastSpellOnly;
}
@Override
public ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect copy() {
return new ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Cards cards = new CardsImpl(getTargetPointer()
.getTargets(game, source)
.stream()
.map(game::getCard)
.filter(Objects::nonNull)
.collect(Collectors.toList())
);
if (controller == null || cards.isEmpty()) {
return false;
}
// move card to exile
UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
MageObject sourceObject = source.getSourceObject(game);
String exileName = sourceObject == null ? "" : sourceObject.getIdName();
for (Card card : cards.getCards(game)) {
card.setFaceDown(true, game);
if (controller.moveCardsToExile(card, source, game, false, exileZoneId, exileName)) {
card.setFaceDown(true, game);
switch (manaAdjustment) {
case NONE:
CardUtil.makeCardPlayable(game, source, card, useCastSpellOnly, Duration.Custom, false, controller.getId(), null);
break;
case AS_THOUGH_ANY_MANA_TYPE:
case AS_THOUGH_ANY_MANA_COLOR:
// TODO: untangle why there is a confusion between the two.
CardUtil.makeCardPlayable(game, source, card, useCastSpellOnly, Duration.Custom, true, controller.getId(), null);
break;
case WITHOUT_PAYING_MANA_COST: // TODO.
default:
throw new IllegalArgumentException("Wrong code usage, manaAdjustment is not yet supported: " + manaAdjustment);
}
// For as long as that card remains exiled, you may look at it
ContinuousEffect effect = new MayLookAtTargetCardEffect(controller.getId());
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
}
}
return true;
}
}

View file

@ -72,7 +72,7 @@ public class ExileAdventureSpellEffect extends OneShotEffect implements MageSing
class AdventureCastFromExileEffect extends AsThoughEffectImpl {
public AdventureCastFromExileEffect() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
staticText = "Then exile this card. You may cast the creature later from exile.";
}

View file

@ -120,7 +120,7 @@ public class MayCastTargetCardEffect extends OneShotEffect {
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null);
} else {
// TODO: support (and add tests!) for the non-NONE manaAdjustment
CardUtil.makeCardPlayable(game, source, card, duration, false);
CardUtil.makeCardPlayable(game, source, card, true, duration, false);
}
if (thenExile) {
ContinuousEffect effect = new ThatSpellGraveyardExileReplacementEffect(true);

View file

@ -27,13 +27,13 @@ public class CanPlayCardControllerEffect extends AsThoughEffectImpl {
protected final UUID playerId;
protected final Condition condition;
public CanPlayCardControllerEffect(Game game, UUID cardId, int cardZCC, Duration duration) {
this(game, cardId, cardZCC, duration, null, null);
public CanPlayCardControllerEffect(Game game, UUID cardId, int cardZCC, boolean useCastSpellOnly, Duration duration) {
this(game, cardId, cardZCC, useCastSpellOnly, duration, null, null);
}
public CanPlayCardControllerEffect(Game game, UUID cardId, int cardZCC, Duration duration, UUID playerId, Condition condition) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit);
this.staticText = "You may play those card";
public CanPlayCardControllerEffect(Game game, UUID cardId, int cardZCC, boolean useCastSpellOnly, Duration duration, UUID playerId, Condition condition) {
super(useCastSpellOnly ? AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE : AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit);
this.staticText = useCastSpellOnly ? "You may cast this card" : "You may play this card";
this.mor = new MageObjectReference(cardId, cardZCC, game);
this.playerId = playerId;
this.condition = condition;
@ -57,18 +57,18 @@ public class CanPlayCardControllerEffect extends AsThoughEffectImpl {
}
@Override
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
public boolean applies(UUID objectId, Ability ability, UUID affectedControllerId, Game game) {
if (mor.getCard(game) == null) {
discard();
return false;
}
if (condition != null && !condition.apply(game, source)) {
if (condition != null && !condition.apply(game, ability)) {
return false;
}
UUID objectIdToCast = CardUtil.getMainCardId(game, sourceId); // affected to all card's parts
UUID objectIdToCast = CardUtil.getMainCardId(game, objectId); // affected to all card's parts
return mor.refersTo(objectIdToCast, game)
&& (playerId == null ? source.isControlledBy(affectedControllerId) : playerId.equals(affectedControllerId));
&& (playerId == null ? ability.isControlledBy(affectedControllerId) : playerId.equals(affectedControllerId));
}
}

View file

@ -55,7 +55,7 @@ public class AftermathAbility extends SimpleStaticAbility {
class AftermathCastFromGraveyard extends AsThoughEffectImpl {
public AftermathCastFromGraveyard() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit);
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit);
}
protected AftermathCastFromGraveyard(final AftermathCastFromGraveyard effect) {