mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 21:12:04 -08:00
* Some rework/clean up of the PlayFromNotOwnHandZone effects (fixes #6580). Some added tests.
This commit is contained in:
parent
8e4d966ff3
commit
85709c0a16
29 changed files with 465 additions and 797 deletions
|
|
@ -105,7 +105,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
return ActivationStatus.getFalse();
|
||||
}
|
||||
}
|
||||
if (costs.canPay(this, sourceId, controllerId, game)) {
|
||||
if (costs.canPay(this, sourceId, playerId, game)) {
|
||||
if (getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) {
|
||||
SplitCard splitCard = (SplitCard) game.getCard(getSourceId());
|
||||
if (splitCard != null) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import mage.constants.*;
|
|||
import mage.game.Game;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.cards.SplitCardHalf;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -42,7 +45,14 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
|
|||
}
|
||||
|
||||
/**
|
||||
* Helper to check that affectedAbility is compatible for alternative cast modifications by setCastSourceIdWithAlternateMana
|
||||
* Helper to check that affectedAbility is compatible for alternative cast
|
||||
* modifications by setCastSourceIdWithAlternateMana
|
||||
*
|
||||
* @param cardToCheck
|
||||
* @param affectedAbilityToCheck
|
||||
* @param playerToCheck
|
||||
* @param source
|
||||
* @return
|
||||
*/
|
||||
public boolean isAbilityAppliedForAlternateCast(Card cardToCheck, Ability affectedAbilityToCheck, UUID playerToCheck, Ability source) {
|
||||
return cardToCheck != null
|
||||
|
|
@ -52,4 +62,35 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
|
|||
&& (affectedAbilityToCheck.getAbilityType() == AbilityType.SPELL
|
||||
|| affectedAbilityToCheck.getAbilityType() == AbilityType.PLAY_LAND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to do the neccessary to allow the card from objectId to be cast or played (if it's a land) without paying any mana.
|
||||
* Additional costs (like sacrificing or discarding) have still to be payed.
|
||||
* Checks if the card is of the correct type or in the correct zone have to be done before.
|
||||
*
|
||||
* @param objectId sourceId of the card to play
|
||||
* @param source source ability that allows this effect
|
||||
* @param affectedControllerId player allowed to play the card
|
||||
* @param game
|
||||
* @return
|
||||
*/
|
||||
protected boolean allowCardToPlayWithoutMana(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
Player player = game.getPlayer(affectedControllerId);
|
||||
Card card = game.getCard(objectId);
|
||||
if (card == null || player == null) {
|
||||
return false;
|
||||
}
|
||||
if (!card.isLand()) {
|
||||
if (card instanceof SplitCard) {
|
||||
SplitCardHalf leftCard = ((SplitCard) card).getLeftHalfCard();
|
||||
player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCard.getSpellAbility().getCosts());
|
||||
SplitCardHalf rightCard = ((SplitCard) card).getRightHalfCard();
|
||||
player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCard.getSpellAbility().getCosts());
|
||||
} else {
|
||||
player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,23 @@
|
|||
package mage.abilities.effects.common.asthought;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.FixedTargets;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -19,6 +27,7 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl {
|
|||
|
||||
private final Zone fromZone;
|
||||
private final TargetController allowedCaster;
|
||||
private final boolean withoutMana;
|
||||
|
||||
public PlayFromNotOwnHandZoneTargetEffect() {
|
||||
this(Duration.EndOfTurn);
|
||||
|
|
@ -33,15 +42,21 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl {
|
|||
}
|
||||
|
||||
public PlayFromNotOwnHandZoneTargetEffect(Zone fromZone, TargetController allowedCaster, Duration duration) {
|
||||
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit);
|
||||
this(fromZone, allowedCaster, duration, false);
|
||||
}
|
||||
|
||||
public PlayFromNotOwnHandZoneTargetEffect(Zone fromZone, TargetController allowedCaster, Duration duration, boolean withoutMana) {
|
||||
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, withoutMana ? Outcome.PlayForFree : Outcome.PutCardInPlay);
|
||||
this.fromZone = fromZone;
|
||||
this.allowedCaster = allowedCaster;
|
||||
this.withoutMana = withoutMana;
|
||||
}
|
||||
|
||||
public PlayFromNotOwnHandZoneTargetEffect(final PlayFromNotOwnHandZoneTargetEffect effect) {
|
||||
super(effect);
|
||||
this.fromZone = effect.fromZone;
|
||||
this.allowedCaster = effect.allowedCaster;
|
||||
this.withoutMana = effect.withoutMana;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -56,27 +71,91 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
return applies(objectId, null, source, game, affectedControllerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
|
||||
List<UUID> targets = getTargetPointer().getTargets(game, source);
|
||||
if (targets.isEmpty()) {
|
||||
this.discard();
|
||||
return false;
|
||||
}
|
||||
switch (allowedCaster) {
|
||||
case YOU:
|
||||
if (affectedControllerId != source.getControllerId()) {
|
||||
if (playerId != source.getControllerId()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case OPPONENT:
|
||||
if (!game.getOpponents(source.getControllerId()).contains(affectedControllerId)) {
|
||||
if (!game.getOpponents(source.getControllerId()).contains(playerId)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case ANY:
|
||||
break;
|
||||
}
|
||||
List<UUID> targets = getTargetPointer().getTargets(game, source);
|
||||
if (targets.isEmpty()) {
|
||||
this.discard();
|
||||
UUID objectIdToCast = CardUtil.getMainCardId(game, objectId);
|
||||
if (targets.contains(objectIdToCast)
|
||||
&& playerId.equals(source.getControllerId())
|
||||
&& game.getState().getZone(objectId).match(fromZone)) {
|
||||
if (withoutMana) {
|
||||
if (affectedAbility != null) {
|
||||
objectIdToCast = affectedAbility.getSourceId();
|
||||
}
|
||||
return allowCardToPlayWithoutMana(objectIdToCast, source, playerId, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean exileAndPlayFromExile(Game game, Ability source, Card card, TargetController allowedCaster, Duration duration, boolean withoutMana) {
|
||||
if (card == null) {
|
||||
return true;
|
||||
}
|
||||
Set<Card> cards = new HashSet<>();
|
||||
cards.add(card);
|
||||
return exileAndPlayFromExile(game, source, cards, allowedCaster, duration, withoutMana);
|
||||
}
|
||||
/**
|
||||
* Exiles the cards and let the allowed player play them from exile for the given duration
|
||||
*
|
||||
* @param game
|
||||
* @param source
|
||||
* @param cards
|
||||
* @param allowedCaster
|
||||
* @param duration
|
||||
* @param withoutMana
|
||||
* @return
|
||||
*/
|
||||
public static boolean exileAndPlayFromExile(Game game, Ability source, Set<Card> cards, TargetController allowedCaster, Duration duration, boolean withoutMana) {
|
||||
if (cards == null || cards.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
if (controller == null || sourceObject == null) {
|
||||
return false;
|
||||
}
|
||||
return targets.contains(objectId)
|
||||
&& affectedControllerId.equals(source.getControllerId())
|
||||
&& game.getState().getZone(objectId).match(fromZone);
|
||||
UUID exileId = CardUtil.getExileZoneId(
|
||||
controller.getId().toString()
|
||||
+ "-" + game.getState().getTurnNum()
|
||||
+ "-" + sourceObject.getIdName(), game
|
||||
);
|
||||
String exileName = sourceObject.getIdName() + " free play"
|
||||
+ (Duration.EndOfTurn.equals(duration) ? " on turn " + game.getState().getTurnNum():"")
|
||||
+ " for " + controller.getName();
|
||||
if (Duration.EndOfTurn.equals(duration)) {
|
||||
game.getExile().createZone(exileId, exileName).setCleanupOnEndTurn(true);
|
||||
}
|
||||
if (!controller.moveCardsToExile(cards, source, game, true, exileId, exileName)) {
|
||||
return false;
|
||||
}
|
||||
ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, allowedCaster, duration, withoutMana);
|
||||
effect.setTargetPointer(new FixedTargets(cards, game));
|
||||
game.addEffect(effect, source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ public interface Card extends MageObject {
|
|||
/**
|
||||
* For cards: return all basic and dynamic abilities
|
||||
* For permanents: return all basic and dynamic abilities
|
||||
* @param game
|
||||
* @return
|
||||
*/
|
||||
Abilities<Ability> getAbilities(Game game);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,12 @@ public class FixedTarget implements TargetPointer {
|
|||
this(mor.getSourceId(), mor.getZoneChangeCounter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Target counter is immediatly initialised with current zoneChangeCounter value from the GameState
|
||||
* Sets fixed the currect zone chnage counter
|
||||
* @param card used to get the objectId
|
||||
* @param game
|
||||
*/
|
||||
public FixedTarget(Card card, Game game) {
|
||||
this.targetId = card.getId();
|
||||
this.zoneChangeCounter = card.getZoneChangeCounter(game);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue