mirror of
https://github.com/magefree/mage.git
synced 2025-12-27 05:52:06 -08:00
Merge branch 'master' into omniscience-fix
This commit is contained in:
commit
b3cc1f49a7
389 changed files with 9467 additions and 5524 deletions
|
|
@ -1,25 +1,25 @@
|
|||
package mage;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import mage.abilities.Abilities;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCosts;
|
||||
import mage.abilities.keyword.ChangelingAbility;
|
||||
import mage.abilities.text.TextPart;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.FrameStyle;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SubTypeSet;
|
||||
import mage.constants.SuperType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.util.SubTypeList;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface MageObject extends MageItem, Serializable {
|
||||
|
||||
String getName();
|
||||
|
|
@ -126,6 +126,10 @@ public interface MageObject extends MageItem, Serializable {
|
|||
return getCardType().contains(CardType.PLANESWALKER);
|
||||
}
|
||||
|
||||
default boolean isTribal() {
|
||||
return getCardType().contains(CardType.TRIBAL);
|
||||
}
|
||||
|
||||
default boolean isPermanent() {
|
||||
return isCreature() || isArtifact() || isPlaneswalker() || isEnchantment() || isLand();
|
||||
}
|
||||
|
|
@ -139,6 +143,9 @@ public interface MageObject extends MageItem, Serializable {
|
|||
}
|
||||
|
||||
default void addSuperType(SuperType superType) {
|
||||
if (getSuperType().contains(superType)) {
|
||||
return;
|
||||
}
|
||||
getSuperType().add(superType);
|
||||
}
|
||||
|
||||
|
|
@ -151,9 +158,31 @@ public interface MageObject extends MageItem, Serializable {
|
|||
}
|
||||
|
||||
default void addCardType(CardType cardType) {
|
||||
if (getCardType().contains(cardType)) {
|
||||
return;
|
||||
}
|
||||
getCardType().add(cardType);
|
||||
}
|
||||
|
||||
default void addSubType(Game game, SubType... subTypes) {
|
||||
for (SubType subType : subTypes) {
|
||||
if (subType.canGain(this)
|
||||
&& !hasSubtype(subType, game)) {
|
||||
getSubtype(game).add(subType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default void removeAllSubTypes(Game game) {
|
||||
getSubtype(game).clear();
|
||||
setIsAllCreatureTypes(false);
|
||||
}
|
||||
|
||||
default void removeAllCreatureTypes(Game game) {
|
||||
getSubtype(game).removeAll(SubType.getCreatureTypes());
|
||||
setIsAllCreatureTypes(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether two cards share card types.
|
||||
*
|
||||
|
|
@ -180,27 +209,36 @@ public interface MageObject extends MageItem, Serializable {
|
|||
return false;
|
||||
}
|
||||
|
||||
default boolean shareSubtypes(Card otherCard, Game game) {
|
||||
|
||||
if (otherCard == null) {
|
||||
throw new IllegalArgumentException("Params can't be null");
|
||||
default boolean shareCreatureTypes(Card otherCard, Game game) {
|
||||
if (!isCreature() && !isTribal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.isCreature() && otherCard.isCreature()) {
|
||||
if (this.hasAbility(ChangelingAbility.getInstance(), game)
|
||||
|| this.isAllCreatureTypes()
|
||||
|| otherCard.hasAbility(ChangelingAbility.getInstance(), game)
|
||||
|| otherCard.isAllCreatureTypes()) {
|
||||
return true;
|
||||
}
|
||||
if (!otherCard.isCreature() && !otherCard.isTribal()) {
|
||||
return false;
|
||||
}
|
||||
for (SubType subtype : this.getSubtype(game)) {
|
||||
if (otherCard.hasSubtype(subtype, game)) {
|
||||
return true;
|
||||
}
|
||||
boolean isAllA = this.isAllCreatureTypes();
|
||||
boolean isAnyA = isAllA || this.getSubtype(game)
|
||||
.stream()
|
||||
.map(SubType::getSubTypeSet)
|
||||
.anyMatch(SubTypeSet.CreatureType::equals);
|
||||
boolean isAllB = otherCard.isAllCreatureTypes();
|
||||
boolean isAnyB = isAllB || otherCard
|
||||
.getSubtype(game)
|
||||
.stream()
|
||||
.map(SubType::getSubTypeSet)
|
||||
.anyMatch(SubTypeSet.CreatureType::equals);
|
||||
if (!isAnyA || !isAnyB) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (isAllA) {
|
||||
return isAllB || isAnyB;
|
||||
}
|
||||
return isAnyA
|
||||
&& (isAllB || this
|
||||
.getSubtype(game)
|
||||
.stream()
|
||||
.filter(subType -> subType.getSubTypeSet() == SubTypeSet.CreatureType)
|
||||
.anyMatch(subType -> otherCard.hasSubtype(subType, game)));
|
||||
}
|
||||
|
||||
boolean isAllCreatureTypes();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package mage;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import mage.abilities.Abilities;
|
||||
import mage.abilities.AbilitiesImpl;
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -11,7 +9,6 @@ import mage.abilities.costs.mana.ManaCosts;
|
|||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.keyword.ChangelingAbility;
|
||||
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
||||
import mage.abilities.text.TextPart;
|
||||
import mage.abilities.text.TextPartSubType;
|
||||
|
|
@ -20,10 +17,11 @@ import mage.cards.mock.MockCard;
|
|||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.util.GameLog;
|
||||
import mage.util.SubTypeList;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public abstract class MageObjectImpl implements MageObject {
|
||||
|
||||
protected UUID objectId;
|
||||
|
|
@ -230,25 +228,10 @@ public abstract class MageObjectImpl implements MageObject {
|
|||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
SubTypeList subtypes = this.getSubtype(game);
|
||||
if (subtypes.contains(value)) {
|
||||
if (value.getSubTypeSet() == SubTypeSet.CreatureType && isAllCreatureTypes()) {
|
||||
return true;
|
||||
} else {
|
||||
// checking for Changeling
|
||||
// first make sure input parameter is a creature subtype
|
||||
// if not, then ChangelingAbility doesn't matter
|
||||
if (value.getSubTypeSet() != SubTypeSet.CreatureType) {
|
||||
return false;
|
||||
}
|
||||
// as it is a creature subtype, then check the existence of Changeling
|
||||
Abilities<Ability> checkList;
|
||||
if (this instanceof Permanent) {
|
||||
checkList = ((Permanent) this).getAbilities(game);
|
||||
} else {
|
||||
checkList = abilities;
|
||||
}
|
||||
return checkList.contains(ChangelingAbility.getInstance()) || isAllCreatureTypes();
|
||||
}
|
||||
return getSubtype(game).contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -289,7 +272,7 @@ public abstract class MageObjectImpl implements MageObject {
|
|||
|
||||
@Override
|
||||
public void setIsAllCreatureTypes(boolean value) {
|
||||
isAllCreatureTypes = value;
|
||||
isAllCreatureTypes = value && (this.isTribal() || this.isCreature());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ class KinshipBaseEffect extends OneShotEffect {
|
|||
if (card != null) {
|
||||
Cards cards = new CardsImpl(card);
|
||||
controller.lookAtCards(sourcePermanent.getName(), cards, game);
|
||||
if (sourcePermanent.shareSubtypes(card, game)) {
|
||||
if (sourcePermanent.shareCreatureTypes(card, game)) {
|
||||
if (controller.chooseUse(outcome, new StringBuilder("Kinship - Reveal ").append(card.getLogName()).append('?').toString(), source, game)) {
|
||||
controller.revealCards(sourcePermanent.getName(), cards, game);
|
||||
for (Effect effect : kinshipEffects) {
|
||||
|
|
|
|||
|
|
@ -7,10 +7,7 @@ import mage.game.Game;
|
|||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import static mage.constants.CardType.CREATURE;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author htrajan
|
||||
*/
|
||||
public class AttachedToCreatureSourceTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
|
@ -33,7 +30,7 @@ public class AttachedToCreatureSourceTriggeredAbility extends TriggeredAbilityIm
|
|||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
Permanent attachedPermanent = game.getPermanent(event.getTargetId());
|
||||
return attachedPermanent != null && attachedPermanent.getCardType().contains(CREATURE);
|
||||
return attachedPermanent != null && attachedPermanent.isCreature();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbilityImpl;
|
||||
import mage.abilities.SpecialAction;
|
||||
|
|
@ -15,22 +12,18 @@ import mage.abilities.effects.common.AttachEffect;
|
|||
import mage.abilities.effects.common.CreateSpecialActionEffect;
|
||||
import mage.abilities.effects.common.RemoveSpecialActionEffect;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.DependencyType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.Target;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author emerald000
|
||||
*/
|
||||
public class LicidAbility extends ActivatedAbilityImpl {
|
||||
|
|
@ -116,8 +109,8 @@ class LicidContinuousEffect extends ContinuousEffectImpl {
|
|||
case TypeChangingEffects_4:
|
||||
licid.getCardType().clear();
|
||||
licid.addCardType(CardType.ENCHANTMENT);
|
||||
licid.getSubtype(game).clear();
|
||||
licid.getSubtype(game).add(SubType.AURA);
|
||||
licid.removeAllSubTypes(game);
|
||||
licid.addSubType(game, SubType.AURA);
|
||||
break;
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
List<Ability> toRemove = new ArrayList<>();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
|
|
@ -11,7 +10,6 @@ import mage.game.stack.Spell;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public class SpellCastControllerTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
|
@ -67,7 +65,7 @@ public class SpellCastControllerTriggeredAbility extends TriggeredAbilityImpl {
|
|||
Spell spell = game.getStack().getSpell(event.getTargetId());
|
||||
if (spell != null && filter.match(spell, getSourceId(), getControllerId(), game)) {
|
||||
if (rememberSource) {
|
||||
this.getEffects().get(0).setTargetPointer(new FixedTarget(spell.getId()));
|
||||
this.getEffects().get(0).setTargetPointer(new FixedTarget(spell.getId(), game));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ package mage.abilities.effects;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.cards.SplitCardHalf;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -22,7 +22,7 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
|
|||
public AsThoughEffectImpl(AsThoughEffectType type, Duration duration, Outcome outcome) {
|
||||
this(type, duration, outcome, false);
|
||||
}
|
||||
|
||||
|
||||
public AsThoughEffectImpl(AsThoughEffectType type, Duration duration, Outcome outcome, boolean consumable) {
|
||||
super(duration, outcome);
|
||||
this.type = type;
|
||||
|
|
@ -74,12 +74,12 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
|
|||
* 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 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
|
||||
* @return
|
||||
*/
|
||||
protected boolean allowCardToPlayWithoutMana(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
Player player = game.getPlayer(affectedControllerId);
|
||||
|
|
@ -89,9 +89,14 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
|
|||
}
|
||||
if (!card.isLand()) {
|
||||
if (card instanceof SplitCard) {
|
||||
SplitCardHalf leftCard = ((SplitCard) card).getLeftHalfCard();
|
||||
Card leftCard = ((SplitCard) card).getLeftHalfCard();
|
||||
player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCard.getSpellAbility().getCosts());
|
||||
SplitCardHalf rightCard = ((SplitCard) card).getRightHalfCard();
|
||||
Card rightCard = ((SplitCard) card).getRightHalfCard();
|
||||
player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCard.getSpellAbility().getCosts());
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
Card leftCard = ((ModalDoubleFacesCard) card).getLeftHalfCard();
|
||||
player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCard.getSpellAbility().getCosts());
|
||||
Card rightCard = ((ModalDoubleFacesCard) card).getRightHalfCard();
|
||||
player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCard.getSpellAbility().getCosts());
|
||||
} else {
|
||||
player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.effects;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.MageSingleton;
|
||||
|
|
@ -7,6 +8,8 @@ import mage.abilities.StaticAbility;
|
|||
import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect;
|
||||
import mage.abilities.effects.common.continuous.CommanderReplacementEffect;
|
||||
import mage.cards.*;
|
||||
import mage.choices.Choice;
|
||||
import mage.choices.ChoiceImpl;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicate;
|
||||
|
|
@ -29,9 +32,6 @@ import java.io.Serializable;
|
|||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.ApprovingObject;
|
||||
import mage.choices.Choice;
|
||||
import mage.choices.ChoiceImpl;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -334,7 +334,7 @@ public class ContinuousEffects implements Serializable {
|
|||
* @param event
|
||||
* @param game
|
||||
* @return a list of all {@link ReplacementEffect} that apply to the current
|
||||
* event
|
||||
* event
|
||||
*/
|
||||
private Map<ReplacementEffect, Set<Ability>> getApplicableReplacementEffects(GameEvent event, Game game) {
|
||||
Map<ReplacementEffect, Set<Ability>> replaceEffects = new HashMap<>();
|
||||
|
|
@ -343,7 +343,7 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
// boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT);
|
||||
//get all applicable transient Replacement effects
|
||||
for (Iterator<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext(); ) {
|
||||
ReplacementEffect effect = iterator.next();
|
||||
if (!effect.checksEventType(event, game)) {
|
||||
continue;
|
||||
|
|
@ -376,7 +376,7 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext(); ) {
|
||||
PreventionEffect effect = iterator.next();
|
||||
if (!effect.checksEventType(event, game)) {
|
||||
continue;
|
||||
|
|
@ -507,7 +507,7 @@ public class ContinuousEffects implements Serializable {
|
|||
* @param controllerId
|
||||
* @param game
|
||||
* @return sourceId of the permitting effect if any exists otherwise returns
|
||||
* null
|
||||
* null
|
||||
*/
|
||||
public ApprovingObject asThough(UUID objectId, AsThoughEffectType type, Ability affectedAbility, UUID controllerId, Game game) {
|
||||
List<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(type, game);
|
||||
|
|
@ -515,6 +515,8 @@ 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 ModalDoubleFacesCardHalf) {
|
||||
idToCheck = ((ModalDoubleFacesCardHalf) affectedAbility.getSourceObject(game)).getParentCard().getId();
|
||||
} else if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof AdventureCardSpell
|
||||
&& !type.needPlayCardAbility()) {
|
||||
// adventure spell uses alternative characteristics for spell/stack
|
||||
|
|
@ -523,6 +525,8 @@ public class ContinuousEffects implements Serializable {
|
|||
Card card = game.getCard(objectId);
|
||||
if (card instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) card).getParentCard().getId();
|
||||
} else if (card instanceof ModalDoubleFacesCardHalf) {
|
||||
idToCheck = ((ModalDoubleFacesCardHalf) card).getParentCard().getId();
|
||||
} else if (card instanceof AdventureCardSpell
|
||||
&& !type.needPlayCardAbility()) {
|
||||
// adventure spell uses alternative characteristics for spell/stack
|
||||
|
|
@ -568,24 +572,24 @@ public class ContinuousEffects implements Serializable {
|
|||
} else if (possibleApprovingObjects.size() > 1) {
|
||||
// Select the ability that you use to permit the action
|
||||
Map<String, String> keyChoices = new HashMap<>();
|
||||
for(ApprovingObject approvingObject :possibleApprovingObjects) {
|
||||
MageObject mageObject = game.getObject(approvingObject.getApprovingAbility().getSourceId());
|
||||
keyChoices.put(approvingObject.getApprovingAbility().getId().toString(),
|
||||
for (ApprovingObject approvingObject : possibleApprovingObjects) {
|
||||
MageObject mageObject = game.getObject(approvingObject.getApprovingAbility().getSourceId());
|
||||
keyChoices.put(approvingObject.getApprovingAbility().getId().toString(),
|
||||
(approvingObject.getApprovingAbility().getRule(mageObject == null ? "" : mageObject.getName()))
|
||||
+ (mageObject == null ? "" : " (" + mageObject.getIdName() + ")"));
|
||||
+ (mageObject == null ? "" : " (" + mageObject.getIdName() + ")"));
|
||||
}
|
||||
Choice choicePermitting = new ChoiceImpl(true);
|
||||
choicePermitting.setMessage("Choose the permitting object");
|
||||
choicePermitting.setKeyChoices(keyChoices);
|
||||
Player player = game.getPlayer(controllerId);
|
||||
player.choose(Outcome.Detriment, choicePermitting, game);
|
||||
for(ApprovingObject approvingObject: possibleApprovingObjects) {
|
||||
for (ApprovingObject approvingObject : possibleApprovingObjects) {
|
||||
if (approvingObject.getApprovingAbility().getId().toString().equals(choicePermitting.getChoiceKey())) {
|
||||
return approvingObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return null;
|
||||
|
||||
|
|
@ -833,7 +837,7 @@ public class ContinuousEffects implements Serializable {
|
|||
do {
|
||||
Map<ReplacementEffect, Set<Ability>> rEffects = getApplicableReplacementEffects(event, game);
|
||||
// Remove all consumed effects (ability dependant)
|
||||
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext();) {
|
||||
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext(); ) {
|
||||
ReplacementEffect entry = it1.next();
|
||||
if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9.
|
||||
Set<UUID> consumedAbilitiesIds = consumed.get(entry.getId());
|
||||
|
|
@ -1018,7 +1022,7 @@ public class ContinuousEffects implements Serializable {
|
|||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> dependentTo.contains(entry.getKey().getId())
|
||||
&& entry.getValue().contains(effect.getId()))
|
||||
&& entry.getValue().contains(effect.getId()))
|
||||
.forEach(entry -> {
|
||||
entry.getValue().remove(effect.getId());
|
||||
dependentTo.remove(entry.getKey().getId());
|
||||
|
|
@ -1052,7 +1056,7 @@ public class ContinuousEffects implements Serializable {
|
|||
continue;
|
||||
}
|
||||
// check if waiting effects can be applied now
|
||||
for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) {
|
||||
for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) {
|
||||
Map.Entry<ContinuousEffect, Set<UUID>> entry = iterator.next();
|
||||
if (!appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -103,10 +103,11 @@ public class CopyEffect extends ContinuousEffectImpl {
|
|||
for (CardType type : copyFromObject.getCardType()) {
|
||||
permanent.addCardType(type);
|
||||
}
|
||||
permanent.getSubtype(game).clear();
|
||||
for (SubType type : copyFromObject.getSubtype(game)) {
|
||||
permanent.getSubtype(game).add(type);
|
||||
}
|
||||
|
||||
permanent.removeAllSubTypes(game);
|
||||
permanent.getSubtype(game).addAll(copyFromObject.getSubtype(game));
|
||||
permanent.setIsAllCreatureTypes(copyFromObject.isAllCreatureTypes());
|
||||
|
||||
permanent.getSuperType().clear();
|
||||
for (SuperType type : copyFromObject.getSuperType()) {
|
||||
permanent.addSuperType(type);
|
||||
|
|
|
|||
|
|
@ -31,10 +31,11 @@ public class CopyTokenEffect extends ContinuousEffectImpl {
|
|||
for (CardType type : token.getCardType()) {
|
||||
permanent.addCardType(type);
|
||||
}
|
||||
permanent.getSubtype(game).clear();
|
||||
permanent.removeAllSubTypes(game);
|
||||
for (SubType type : token.getSubtype(game)) {
|
||||
permanent.getSubtype(game).add(type);
|
||||
permanent.addSubType(game, type);
|
||||
}
|
||||
permanent.setIsAllCreatureTypes(token.isAllCreatureTypes());
|
||||
permanent.getSuperType().clear();
|
||||
for (SuperType type : token.getSuperType()) {
|
||||
permanent.addSuperType(type);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -25,6 +20,11 @@ import mage.util.CardUtil;
|
|||
import mage.util.functions.ApplyToPermanent;
|
||||
import mage.util.functions.EmptyApplyToPermanent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
|
@ -179,10 +179,10 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
token.getSuperType().remove(SuperType.LEGENDARY);
|
||||
}
|
||||
|
||||
if (startingLoyalty!=-1){
|
||||
if (startingLoyalty != -1) {
|
||||
token.setStartingLoyalty(startingLoyalty);
|
||||
}
|
||||
if (additionalCardType != null && !token.getCardType().contains(additionalCardType)) {
|
||||
if (additionalCardType != null) {
|
||||
token.addCardType(additionalCardType);
|
||||
}
|
||||
if (hasHaste) {
|
||||
|
|
@ -199,12 +199,12 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
token.removePTCDA();
|
||||
token.getToughness().modifyBaseValue(tokenToughness);
|
||||
}
|
||||
if (additionalSubType != null && !token.hasSubtype(additionalSubType, game)) {
|
||||
token.getSubtype(game).add(additionalSubType);
|
||||
if (onlySubType != null) {
|
||||
token.removeAllCreatureTypes(game);
|
||||
token.addSubType(game, onlySubType);
|
||||
}
|
||||
if (onlySubType != null && !token.hasSubtype(onlySubType, game)) {
|
||||
token.getSubtype(game).clear();
|
||||
token.getSubtype(game).add(onlySubType);
|
||||
if (additionalSubType != null && !token.hasSubtype(additionalSubType, game)) {
|
||||
token.addSubType(game, additionalSubType);
|
||||
}
|
||||
if (color != null) {
|
||||
token.getColor(game).setColor(color);
|
||||
|
|
|
|||
|
|
@ -29,9 +29,7 @@ public class AddCardSubTypeTargetEffect extends ContinuousEffectImpl {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Permanent target = game.getPermanent(targetPointer.getFirst(game, source));
|
||||
if (target != null) {
|
||||
if (!target.hasSubtype(addedSubType, game)) {
|
||||
target.getSubtype(game).add(addedSubType);
|
||||
}
|
||||
target.addSubType(game, addedSubType);
|
||||
} else {
|
||||
if (duration == Duration.Custom) {
|
||||
discard();
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ public class AddCardSubtypeAllEffect extends ContinuousEffectImpl {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
|
||||
if (perm != null && !perm.hasSubtype(addedSubtype, game)) {
|
||||
perm.getSubtype(game).add(addedSubtype);
|
||||
if (perm != null) {
|
||||
perm.addSubType(game, addedSubtype);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ public class AddCardSubtypeAttachedEffect extends ContinuousEffectImpl {
|
|||
Permanent equipment = game.getPermanent(source.getSourceId());
|
||||
if (equipment != null && equipment.getAttachedTo() != null) {
|
||||
Permanent target = game.getPermanent(equipment.getAttachedTo());
|
||||
if (target != null && !target.hasSubtype(addedSubtype, game))
|
||||
target.getSubtype(game).add(addedSubtype);
|
||||
if (target != null)
|
||||
target.addSubType(game, addedSubtype);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class AddCardTypeAttachedEffect extends ContinuousEffectImpl {
|
|||
Permanent equipment = game.getPermanent(source.getSourceId());
|
||||
if (equipment != null && equipment.getAttachedTo() != null) {
|
||||
Permanent target = game.getPermanent(equipment.getAttachedTo());
|
||||
if (target != null && !target.getCardType().contains(addedCardType)) {
|
||||
if (target != null) {
|
||||
target.addCardType(addedCardType);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
|
||||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
|
|
@ -11,6 +8,9 @@ import mage.constants.*;
|
|||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author emerald000
|
||||
*/
|
||||
|
|
@ -46,9 +46,7 @@ public class AddCardTypeSourceEffect extends ContinuousEffectImpl {
|
|||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null && affectedObjectList.contains(new MageObjectReference(permanent, game))) {
|
||||
for (CardType cardType : addedCardTypes) {
|
||||
if (!permanent.getCardType().contains(cardType)) {
|
||||
permanent.addCardType(cardType);
|
||||
}
|
||||
permanent.addCardType(cardType);
|
||||
}
|
||||
return true;
|
||||
} else if (this.getDuration() == Duration.Custom) {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,15 @@
|
|||
|
||||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.DependencyType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* @author nantuko
|
||||
|
|
@ -48,9 +43,7 @@ public class AddCardTypeTargetEffect extends ContinuousEffectImpl {
|
|||
Permanent target = game.getPermanent(targetId);
|
||||
if (target != null) {
|
||||
for (CardType cardType : addedCardTypes) {
|
||||
if (!target.getCardType().contains(cardType)) {
|
||||
target.addCardType(cardType);
|
||||
}
|
||||
target.addCardType(cardType);
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ public class AddChosenSubtypeEffect extends ContinuousEffectImpl {
|
|||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
SubType subType = ChooseCreatureTypeEffect.getChosenCreatureType(permanent.getId(), game);
|
||||
if (subType != null && !permanent.hasSubtype(subType, game)) {
|
||||
permanent.getSubtype(game).add(subType);
|
||||
if (subType != null) {
|
||||
permanent.addSubType(game, subType);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import mage.game.permanent.Permanent;
|
|||
public class BecomesAllBasicsControlledEffect extends ContinuousEffectImpl {
|
||||
|
||||
public BecomesAllBasicsControlledEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment);
|
||||
super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment);
|
||||
this.staticText = "Lands you control are every basic land type in addition to their other types";
|
||||
dependencyTypes.add(DependencyType.BecomeMountain);
|
||||
dependencyTypes.add(DependencyType.BecomeForest);
|
||||
|
|
@ -27,65 +27,29 @@ public class BecomesAllBasicsControlledEffect extends ContinuousEffectImpl {
|
|||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BecomesAllBasicsControlledEffect copy() {
|
||||
return new BecomesAllBasicsControlledEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(
|
||||
StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, source.getControllerId(), game)) {
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
if (!permanent.hasSubtype(SubType.SWAMP, game)) {
|
||||
permanent.getSubtype(game).add(SubType.SWAMP);
|
||||
}
|
||||
if (!permanent.hasSubtype(SubType.MOUNTAIN, game)) {
|
||||
permanent.getSubtype(game).add(SubType.MOUNTAIN);
|
||||
}
|
||||
if (!permanent.hasSubtype(SubType.FOREST, game)) {
|
||||
permanent.getSubtype(game).add(SubType.FOREST);
|
||||
}
|
||||
if (!permanent.hasSubtype(SubType.ISLAND, game)) {
|
||||
permanent.getSubtype(game).add(SubType.ISLAND);
|
||||
}
|
||||
if (!permanent.hasSubtype(SubType.PLAINS, game)) {
|
||||
permanent.getSubtype(game).add(SubType.PLAINS);
|
||||
}
|
||||
if (permanent.hasSubtype(SubType.SWAMP, game)
|
||||
&& !permanent.getAbilities().containsRule(new BlackManaAbility())) {
|
||||
permanent.addAbility(new BlackManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
if (permanent.hasSubtype(SubType.MOUNTAIN, game)
|
||||
&& !permanent.getAbilities().containsRule(new RedManaAbility())) {
|
||||
permanent.addAbility(new RedManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
if (permanent.hasSubtype(SubType.FOREST, game)
|
||||
&& !permanent.getAbilities().containsRule(new GreenManaAbility())) {
|
||||
permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
if (permanent.hasSubtype(SubType.ISLAND, game)
|
||||
&& !permanent.getAbilities().containsRule(new BlueManaAbility())) {
|
||||
permanent.addAbility(new BlueManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
if (permanent.hasSubtype(SubType.PLAINS, game)
|
||||
&& !permanent.getAbilities().containsRule(new WhiteManaAbility())) {
|
||||
permanent.addAbility(new WhiteManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
}
|
||||
permanent.addSubType(
|
||||
game,
|
||||
SubType.PLAINS,
|
||||
SubType.ISLAND,
|
||||
SubType.SWAMP,
|
||||
SubType.MOUNTAIN,
|
||||
SubType.FOREST
|
||||
);
|
||||
permanent.addAbility(new WhiteManaAbility(), source.getSourceId(), game);
|
||||
permanent.addAbility(new BlueManaAbility(), source.getSourceId(), game);
|
||||
permanent.addAbility(new BlackManaAbility(), source.getSourceId(), game);
|
||||
permanent.addAbility(new RedManaAbility(), source.getSourceId(), game);
|
||||
permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLayer(Layer layer) {
|
||||
return layer == Layer.TypeChangingEffects_4;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,18 +5,12 @@ import mage.MageObjectReference;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.constants.DependencyType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.Target;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class BecomesAuraSourceEffect extends ContinuousEffectImpl implements SourceEffect {
|
||||
|
|
@ -58,9 +52,7 @@ public class BecomesAuraSourceEffect extends ContinuousEffectImpl implements Sou
|
|||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (!permanent.hasSubtype(SubType.AURA, game)) {
|
||||
permanent.getSubtype(game).add(SubType.AURA);
|
||||
}
|
||||
permanent.addSubType(game, SubType.AURA);
|
||||
}
|
||||
break;
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl {
|
|||
protected List<SubType> landTypes = new ArrayList<>();
|
||||
|
||||
public BecomesBasicLandEnchantedEffect(SubType... landNames) {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment);
|
||||
super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment);
|
||||
landTypes.addAll(Arrays.asList(landNames));
|
||||
this.staticText = setText();
|
||||
}
|
||||
|
|
@ -26,68 +26,55 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl {
|
|||
this.landTypes.addAll(effect.landTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BecomesBasicLandEnchantedEffect copy() {
|
||||
return new BecomesBasicLandEnchantedEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent enchantment = game.getPermanent(source.getSourceId());
|
||||
if (enchantment != null && enchantment.getAttachedTo() != null) {
|
||||
Permanent permanent = game.getPermanent(enchantment.getAttachedTo());
|
||||
if (permanent != null) {
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
// lands intrictically have the mana ability associated with their type, so added here in layer 4
|
||||
permanent.getSubtype(game).removeAll(SubType.getLandTypes());
|
||||
permanent.getSubtype(game).addAll(landTypes);
|
||||
permanent.removeAllAbilities(source.getSourceId(), game);
|
||||
for (SubType landType : landTypes) {
|
||||
switch (landType) {
|
||||
case SWAMP:
|
||||
if (permanent.hasSubtype(SubType.SWAMP, game)) {
|
||||
permanent.addAbility(new BlackManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
case MOUNTAIN:
|
||||
if (permanent.hasSubtype(SubType.MOUNTAIN, game)) {
|
||||
permanent.addAbility(new RedManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
case FOREST:
|
||||
if (permanent.hasSubtype(SubType.FOREST, game)) {
|
||||
permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
case ISLAND:
|
||||
if (permanent.hasSubtype(SubType.ISLAND, game)) {
|
||||
permanent.addAbility(new BlueManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
case PLAINS:
|
||||
if (permanent.hasSubtype(SubType.PLAINS, game)) {
|
||||
permanent.addAbility(new WhiteManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
if (enchantment == null || enchantment.getAttachedTo() == null) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanent(enchantment.getAttachedTo());
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
// lands intrictically have the mana ability associated with their type, so added here in layer 4
|
||||
permanent.getSubtype(game).removeAll(SubType.getLandTypes());
|
||||
permanent.getSubtype(game).addAll(landTypes);
|
||||
permanent.removeAllAbilities(source.getSourceId(), game);
|
||||
for (SubType landType : landTypes) {
|
||||
switch (landType) {
|
||||
case PLAINS:
|
||||
if (permanent.hasSubtype(SubType.PLAINS, game)) {
|
||||
permanent.addAbility(new WhiteManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
case ISLAND:
|
||||
if (permanent.hasSubtype(SubType.ISLAND, game)) {
|
||||
permanent.addAbility(new BlueManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
case SWAMP:
|
||||
if (permanent.hasSubtype(SubType.SWAMP, game)) {
|
||||
permanent.addAbility(new BlackManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
case MOUNTAIN:
|
||||
if (permanent.hasSubtype(SubType.MOUNTAIN, game)) {
|
||||
permanent.addAbility(new RedManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
case FOREST:
|
||||
if (permanent.hasSubtype(SubType.FOREST, game)) {
|
||||
permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLayer(Layer layer) {
|
||||
return layer == Layer.TypeChangingEffects_4;
|
||||
return true;
|
||||
}
|
||||
|
||||
private String setText() {
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl {
|
|||
landTypesToAdd.clear();
|
||||
for (SubType subtype : landTypes) {
|
||||
if (!land.hasSubtype(subtype, game)) {
|
||||
land.getSubtype(game).add(subtype);
|
||||
land.addSubType(game, subtype);
|
||||
landTypesToAdd.add(subtype);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,14 +60,10 @@ public class BecomesBlackZombieAdditionEffect extends ContinuousEffectImpl {
|
|||
if (creature != null) {
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (!creature.hasSubtype(SubType.ZOMBIE, game)) {
|
||||
creature.getSubtype(game).add(SubType.ZOMBIE);
|
||||
}
|
||||
}
|
||||
creature.addSubType(game, SubType.ZOMBIE);
|
||||
break;
|
||||
case ColorChangingEffects_5:
|
||||
if (sublayer == SubLayer.NA && this.giveBlackColor) {
|
||||
if (this.giveBlackColor) {
|
||||
creature.getColor(game).setBlack(true);
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -25,18 +25,18 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
|
|||
private boolean loseTypes = false;
|
||||
protected boolean loseName = false;
|
||||
|
||||
public BecomesCreatureAllEffect(Token token, String theyAreStillType,
|
||||
FilterPermanent filter, Duration duration, boolean loseColor) {
|
||||
public BecomesCreatureAllEffect(Token token, String theyAreStillType,
|
||||
FilterPermanent filter, Duration duration, boolean loseColor) {
|
||||
this(token, theyAreStillType, filter, duration, loseColor, false, false);
|
||||
}
|
||||
|
||||
public BecomesCreatureAllEffect(Token token, String theyAreStillType,
|
||||
FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName) {
|
||||
public BecomesCreatureAllEffect(Token token, String theyAreStillType,
|
||||
FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName) {
|
||||
this(token, theyAreStillType, filter, duration, loseColor, loseName, false);
|
||||
}
|
||||
|
||||
public BecomesCreatureAllEffect(Token token, String theyAreStillType,
|
||||
FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName, boolean loseTypes) {
|
||||
public BecomesCreatureAllEffect(Token token, String theyAreStillType,
|
||||
FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName, boolean loseTypes) {
|
||||
super(duration, Outcome.BecomeCreature);
|
||||
this.token = token;
|
||||
this.theyAreStillType = theyAreStillType;
|
||||
|
|
@ -44,7 +44,7 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
|
|||
this.loseColor = loseColor;
|
||||
this.loseName = loseName;
|
||||
this.loseTypes = loseTypes;
|
||||
|
||||
|
||||
this.dependencyTypes.add(DependencyType.BecomeCreature);
|
||||
}
|
||||
|
||||
|
|
@ -87,83 +87,67 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
|
||||
for (Permanent permanent : affectedPermanents) {
|
||||
if (permanent != null) {
|
||||
switch (layer) {
|
||||
case TextChangingEffects_3:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (loseName) {
|
||||
permanent.setName(token.getName());
|
||||
}
|
||||
if (permanent == null) {
|
||||
continue;
|
||||
}
|
||||
switch (layer) {
|
||||
case TextChangingEffects_3:
|
||||
if (loseName) {
|
||||
permanent.setName(token.getName());
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeChangingEffects_4:
|
||||
for (CardType t : token.getCardType()) {
|
||||
permanent.addCardType(t);
|
||||
}
|
||||
if (theyAreStillType != null || loseTypes) {
|
||||
permanent.removeAllCreatureTypes(game);
|
||||
}
|
||||
for (SubType t : token.getSubtype(game)) {
|
||||
permanent.addSubType(game, t);
|
||||
}
|
||||
permanent.setIsAllCreatureTypes(token.isAllCreatureTypes());
|
||||
|
||||
for (SuperType t : token.getSuperType()) {
|
||||
if (!permanent.getSuperType().contains(t)) {
|
||||
permanent.addSuperType(t);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TypeChangingEffects_4:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (theyAreStillType != null) {
|
||||
permanent.getSubtype(game).retainAll(SubType.getLandTypes());
|
||||
permanent.getSubtype(game).addAll(token.getSubtype(game));
|
||||
} else {
|
||||
if (loseTypes) {
|
||||
permanent.getSubtype(game).retainAll(SubType.getLandTypes());
|
||||
}
|
||||
break;
|
||||
|
||||
for (SubType t : token.getSubtype(game)) {
|
||||
if (!permanent.hasSubtype(t, game)) {
|
||||
permanent.getSubtype(game).add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
case ColorChangingEffects_5:
|
||||
if (this.loseColor) {
|
||||
permanent.getColor(game).setWhite(false);
|
||||
permanent.getColor(game).setBlue(false);
|
||||
permanent.getColor(game).setBlack(false);
|
||||
permanent.getColor(game).setRed(false);
|
||||
permanent.getColor(game).setGreen(false);
|
||||
}
|
||||
if (token.getColor(game).hasColor()) {
|
||||
permanent.getColor(game).addColor(token.getColor(game));
|
||||
}
|
||||
break;
|
||||
|
||||
for (SuperType t : token.getSuperType()) {
|
||||
if (!permanent.getSuperType().contains(t)) {
|
||||
permanent.addSuperType(t);
|
||||
}
|
||||
}
|
||||
|
||||
for (CardType t : token.getCardType()) {
|
||||
if (!permanent.getCardType().contains(t)) {
|
||||
permanent.addCardType(t);
|
||||
}
|
||||
}
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
if (!token.getAbilities().isEmpty()) {
|
||||
for (Ability ability : token.getAbilities()) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ColorChangingEffects_5:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (this.loseColor) {
|
||||
permanent.getColor(game).setBlack(false);
|
||||
permanent.getColor(game).setGreen(false);
|
||||
permanent.getColor(game).setBlue(false);
|
||||
permanent.getColor(game).setWhite(false);
|
||||
permanent.getColor(game).setRed(false);
|
||||
}
|
||||
if (token.getColor(game).hasColor()) {
|
||||
permanent.getColor(game).addColor(token.getColor(game));
|
||||
}
|
||||
case PTChangingEffects_7:
|
||||
if (sublayer == SubLayer.SetPT_7b) {
|
||||
int power = token.getPower().getValue();
|
||||
int toughness = token.getToughness().getValue();
|
||||
if (power != 0 && toughness != 0) {
|
||||
permanent.getPower().setValue(power);
|
||||
permanent.getToughness().setValue(toughness);
|
||||
}
|
||||
break;
|
||||
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (!token.getAbilities().isEmpty()) {
|
||||
for (Ability ability : token.getAbilities()) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PTChangingEffects_7:
|
||||
if (sublayer == SubLayer.SetPT_7b) {
|
||||
int power = token.getPower().getValue();
|
||||
int toughness = token.getToughness().getValue();
|
||||
if (power != 0 && toughness != 0) {
|
||||
permanent.getPower().setValue(power);
|
||||
permanent.getToughness().setValue(toughness);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -78,19 +78,14 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
|
|||
switch (loseType) {
|
||||
case ALL:
|
||||
case ALL_BUT_COLOR:
|
||||
permanent.removeAllSubTypes(game);
|
||||
break;
|
||||
case ABILITIES_SUBTYPE:
|
||||
if (permanent.isLand()) {
|
||||
permanent.getSubtype(game).retainAll(SubType.getLandTypes());
|
||||
} else {
|
||||
permanent.getSubtype(game).clear();
|
||||
}
|
||||
permanent.setIsAllCreatureTypes(false);
|
||||
permanent.removeAllCreatureTypes(game);
|
||||
break;
|
||||
}
|
||||
for (SubType t : token.getSubtype(game)) {
|
||||
if (!permanent.hasSubtype(t, game)) {
|
||||
permanent.getSubtype(game).add(t);
|
||||
}
|
||||
permanent.addSubType(game, t);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -98,11 +93,11 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
|
|||
case ColorChangingEffects_5:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (loseType == LoseType.ALL || loseType == LoseType.COLOR) {
|
||||
permanent.getColor(game).setBlack(false);
|
||||
permanent.getColor(game).setGreen(false);
|
||||
permanent.getColor(game).setBlue(false);
|
||||
permanent.getColor(game).setWhite(false);
|
||||
permanent.getColor(game).setBlue(false);
|
||||
permanent.getColor(game).setBlack(false);
|
||||
permanent.getColor(game).setRed(false);
|
||||
permanent.getColor(game).setGreen(false);
|
||||
}
|
||||
if (token.getColor(game).hasColor()) {
|
||||
permanent.getColor(game).addColor(token.getColor(game));
|
||||
|
|
|
|||
|
|
@ -1,42 +1,33 @@
|
|||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.*;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.Token;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public class BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect extends ContinuousEffectImpl {
|
||||
public class BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect extends OneShotEffect {
|
||||
|
||||
public enum LoseType {
|
||||
NONE, ALL, ALL_BUT_COLOR, ABILITIES, ABILITIES_SUBTYPE_AND_PT
|
||||
}
|
||||
|
||||
protected Token token;
|
||||
protected String type;
|
||||
protected LoseType loseType; // what attributes are lost
|
||||
private final Token token;
|
||||
private final Duration duration;
|
||||
|
||||
public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(Token token, String text, Duration duration) {
|
||||
this(token, text, duration, LoseType.NONE);
|
||||
}
|
||||
|
||||
public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(Token token, String text, Duration duration, LoseType loseType) {
|
||||
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.BecomeCreature);
|
||||
this.token = token;
|
||||
this.loseType = loseType;
|
||||
super(Outcome.BecomeCreature);
|
||||
staticText = text;
|
||||
this.token = token;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(final BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect effect) {
|
||||
super(effect);
|
||||
this.token = effect.token.copy();
|
||||
this.type = effect.type;
|
||||
this.loseType = effect.loseType;
|
||||
this.duration = effect.duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -44,122 +35,19 @@ public class BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect extends Co
|
|||
return new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
Permanent attachedPermanent = game.getPermanent(source.getSourceId());
|
||||
if (attachedPermanent != null) {
|
||||
Permanent permanentAttachedTo = game.getPermanent(attachedPermanent.getAttachedTo());
|
||||
if (permanentAttachedTo != null) {
|
||||
affectedObjectList.add(new MageObjectReference(permanentAttachedTo, game));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
boolean attachedExists = false;
|
||||
Permanent enchantment = game.getPermanent(source.getSourceId());
|
||||
if (enchantment != null) {
|
||||
for (MageObjectReference mageObjectReference : affectedObjectList) {
|
||||
Permanent permanentAttachedTo = mageObjectReference.getPermanent(game);
|
||||
if (permanentAttachedTo != null) {
|
||||
attachedExists = true;
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
for (SuperType superType : token.getSuperType()) {
|
||||
permanentAttachedTo.addSuperType(superType);
|
||||
|
||||
}
|
||||
// card type
|
||||
switch (loseType) {
|
||||
case ALL:
|
||||
case ALL_BUT_COLOR:
|
||||
permanentAttachedTo.getCardType().clear();
|
||||
break;
|
||||
}
|
||||
for (CardType cardType : token.getCardType()) {
|
||||
permanentAttachedTo.addCardType(cardType);
|
||||
}
|
||||
|
||||
// sub type
|
||||
switch (loseType) {
|
||||
case ALL:
|
||||
case ALL_BUT_COLOR:
|
||||
case ABILITIES_SUBTYPE_AND_PT:
|
||||
permanentAttachedTo.getSubtype(game).retainAll(SubType.getLandTypes());
|
||||
break;
|
||||
}
|
||||
for (SubType subType : token.getSubtype(game)) {
|
||||
if (!permanentAttachedTo.hasSubtype(subType, game)) {
|
||||
permanentAttachedTo.getSubtype(game).add(subType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case ColorChangingEffects_5:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (loseType == LoseType.ALL) {
|
||||
permanentAttachedTo.getColor(game).setBlack(false);
|
||||
permanentAttachedTo.getColor(game).setGreen(false);
|
||||
permanentAttachedTo.getColor(game).setBlue(false);
|
||||
permanentAttachedTo.getColor(game).setWhite(false);
|
||||
permanentAttachedTo.getColor(game).setRed(false);
|
||||
}
|
||||
if (token.getColor(game).hasColor()) {
|
||||
permanentAttachedTo.getColor(game).setColor(token.getColor(game));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
switch (loseType) {
|
||||
case ALL:
|
||||
case ALL_BUT_COLOR:
|
||||
case ABILITIES:
|
||||
case ABILITIES_SUBTYPE_AND_PT:
|
||||
permanentAttachedTo.removeAllAbilities(source.getSourceId(), game);
|
||||
break;
|
||||
}
|
||||
for (Ability ability : token.getAbilities()) {
|
||||
permanentAttachedTo.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case PTChangingEffects_7:
|
||||
if (sublayer == SubLayer.SetPT_7b) {
|
||||
permanentAttachedTo.getPower().setValue(token.getPower().getValue());
|
||||
permanentAttachedTo.getToughness().setValue(token.getToughness().getValue());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!attachedExists) {
|
||||
discard();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
Permanent permanent = source.getSourcePermanentOrLKI(game);
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
Permanent enchanted = game.getPermanent(permanent.getAttachedTo());
|
||||
if (enchanted == null) {
|
||||
return false;
|
||||
}
|
||||
game.addEffect(new BecomesCreatureTargetEffect(
|
||||
token, false, true, duration
|
||||
).setTargetPointer(new FixedTarget(enchanted, game)), source);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLayer(Layer layer) {
|
||||
return layer == Layer.PTChangingEffects_7
|
||||
|| layer == Layer.AbilityAddingRemovingEffects_6
|
||||
|| layer == Layer.ColorChangingEffects_5
|
||||
|| layer == Layer.TypeChangingEffects_4;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,78 +82,66 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements
|
|||
} else {
|
||||
permanent = game.getPermanent(source.getSourceId());
|
||||
}
|
||||
if (permanent != null) {
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (losePreviousTypes) {
|
||||
permanent.getCardType().clear();
|
||||
}
|
||||
for (CardType cardType : token.getCardType()) {
|
||||
if (permanent.getCardType().contains(cardType)) {
|
||||
continue;
|
||||
}
|
||||
permanent.addCardType(cardType);
|
||||
}
|
||||
|
||||
if (theyAreStillType != null && theyAreStillType.isEmpty() || theyAreStillType == null && permanent.isLand()) {
|
||||
permanent.getSubtype(game).retainAll(SubType.getLandTypes());
|
||||
}
|
||||
if (!token.getSubtype(game).isEmpty()) {
|
||||
for (SubType subType : token.getSubtype(game)) {
|
||||
if (permanent.hasSubtype(subType, game)) {
|
||||
continue;
|
||||
}
|
||||
permanent.getSubtype(game).add(subType);
|
||||
}
|
||||
}
|
||||
permanent.setIsAllCreatureTypes(token.isAllCreatureTypes());
|
||||
}
|
||||
break;
|
||||
|
||||
case ColorChangingEffects_5:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (token.getColor(game).hasColor()) {
|
||||
permanent.getColor(game).setColor(token.getColor(game));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (loseAbilities) {
|
||||
permanent.removeAllAbilities(source.getSourceId(), game);
|
||||
}
|
||||
for (Ability ability : token.getAbilities()) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PTChangingEffects_7:
|
||||
if ((sublayer == SubLayer.CharacteristicDefining_7a && isCharacterDefining())
|
||||
|| (sublayer == SubLayer.SetPT_7b && !isCharacterDefining())) {
|
||||
if (power != null) {
|
||||
permanent.getPower().setValue(power.calculate(game, source, this)); // check all other becomes to use calculate?
|
||||
} else if (token.getPower() != null) {
|
||||
permanent.getPower().setValue(token.getPower().getValue());
|
||||
}
|
||||
if (toughness != null) {
|
||||
permanent.getToughness().setValue(toughness.calculate(game, source, this));
|
||||
} else if (token.getToughness() != null) {
|
||||
permanent.getToughness().setValue(token.getToughness().getValue());
|
||||
}
|
||||
}
|
||||
break;
|
||||
if (permanent == null) {
|
||||
if (duration == Duration.Custom) {
|
||||
this.discard();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
if (losePreviousTypes) {
|
||||
permanent.getCardType().clear();
|
||||
}
|
||||
for (CardType cardType : token.getCardType()) {
|
||||
permanent.addCardType(cardType);
|
||||
}
|
||||
|
||||
return true;
|
||||
if (theyAreStillType != null && theyAreStillType.isEmpty()
|
||||
|| theyAreStillType == null && permanent.isLand()) {
|
||||
permanent.removeAllCreatureTypes(game);
|
||||
}
|
||||
if (!token.getSubtype(game).isEmpty()) {
|
||||
for (SubType subType : token.getSubtype(game)) {
|
||||
permanent.addSubType(game, subType);
|
||||
}
|
||||
}
|
||||
permanent.setIsAllCreatureTypes(token.isAllCreatureTypes());
|
||||
break;
|
||||
|
||||
} else if (duration == Duration.Custom) {
|
||||
this.discard();
|
||||
case ColorChangingEffects_5:
|
||||
if (token.getColor(game).hasColor()) {
|
||||
permanent.getColor(game).setColor(token.getColor(game));
|
||||
}
|
||||
break;
|
||||
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
if (loseAbilities) {
|
||||
permanent.removeAllAbilities(source.getSourceId(), game);
|
||||
}
|
||||
for (Ability ability : token.getAbilities()) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
|
||||
case PTChangingEffects_7:
|
||||
if ((sublayer == SubLayer.CharacteristicDefining_7a && isCharacterDefining())
|
||||
|| (sublayer == SubLayer.SetPT_7b && !isCharacterDefining())) {
|
||||
if (power != null) {
|
||||
permanent.getPower().setValue(power.calculate(game, source, this)); // check all other becomes to use calculate?
|
||||
} else if (token.getPower() != null) {
|
||||
permanent.getPower().setValue(token.getPower().getValue());
|
||||
}
|
||||
if (toughness != null) {
|
||||
permanent.getToughness().setValue(toughness.calculate(game, source, this));
|
||||
} else if (token.getToughness() != null) {
|
||||
permanent.getToughness().setValue(token.getToughness().getValue());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -171,7 +159,10 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements
|
|||
|
||||
@Override
|
||||
public boolean hasLayer(Layer layer) {
|
||||
return layer == Layer.PTChangingEffects_7 || layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4;
|
||||
return layer == Layer.PTChangingEffects_7
|
||||
|| layer == Layer.AbilityAddingRemovingEffects_6
|
||||
|| layer == Layer.ColorChangingEffects_5
|
||||
|| layer == Layer.TypeChangingEffects_4;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,82 +70,68 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl {
|
|||
boolean result = false;
|
||||
for (UUID permanentId : targetPointer.getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(permanentId);
|
||||
if (permanent != null) {
|
||||
switch (layer) {
|
||||
case TextChangingEffects_3:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (loseName) {
|
||||
permanent.setName(token.getName());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeChangingEffects_4:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (loseAllAbilities) {
|
||||
permanent.getSubtype(game).retainAll(SubType.getLandTypes());
|
||||
permanent.getCardType().clear(); // remove all CardTypes
|
||||
permanent.getSubtype(game).addAll(token.getSubtype(game));
|
||||
} else {
|
||||
if (removeSubtypes) {
|
||||
permanent.getSubtype(game).clear();
|
||||
}
|
||||
for (SubType t : token.getSubtype(game)) {
|
||||
if (!permanent.hasSubtype(t, game)) {
|
||||
permanent.getSubtype(game).add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (SuperType t : token.getSuperType()) {
|
||||
if (!permanent.getSuperType().contains(t)) {
|
||||
permanent.addSuperType(t);
|
||||
}
|
||||
}
|
||||
|
||||
for (CardType t : token.getCardType()) {
|
||||
if (!permanent.getCardType().contains(t)) {
|
||||
permanent.addCardType(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ColorChangingEffects_5:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (loseAllAbilities) {
|
||||
permanent.getColor(game).setBlack(false);
|
||||
permanent.getColor(game).setGreen(false);
|
||||
permanent.getColor(game).setBlue(false);
|
||||
permanent.getColor(game).setWhite(false);
|
||||
permanent.getColor(game).setBlack(false);
|
||||
}
|
||||
if (token.getColor(game).hasColor()) {
|
||||
permanent.getColor(game).setColor(token.getColor(game));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
if (loseAllAbilities && !keepAbilities) {
|
||||
permanent.removeAllAbilities(source.getSourceId(), game);
|
||||
}
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (!token.getAbilities().isEmpty()) {
|
||||
for (Ability ability : token.getAbilities()) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PTChangingEffects_7:
|
||||
if (sublayer == SubLayer.SetPT_7b) { // CDA can only define a characteristic of either the card or token it comes from.
|
||||
permanent.getToughness().setValue(token.getToughness().getValue());
|
||||
permanent.getPower().setValue(token.getPower().getValue());
|
||||
}
|
||||
}
|
||||
result = true;
|
||||
if (permanent == null) {
|
||||
continue;
|
||||
}
|
||||
switch (layer) {
|
||||
case TextChangingEffects_3:
|
||||
if (loseName) {
|
||||
permanent.setName(token.getName());
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeChangingEffects_4:
|
||||
for (CardType t : token.getCardType()) {
|
||||
permanent.addCardType(t);
|
||||
}
|
||||
if (loseAllAbilities || removeSubtypes) {
|
||||
permanent.removeAllCreatureTypes(game);
|
||||
}
|
||||
for (SubType t : token.getSubtype(game)) {
|
||||
permanent.addSubType(game, t);
|
||||
}
|
||||
|
||||
for (SuperType t : token.getSuperType()) {
|
||||
if (!permanent.getSuperType().contains(t)) {
|
||||
permanent.addSuperType(t);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ColorChangingEffects_5:
|
||||
if (loseAllAbilities) {
|
||||
permanent.getColor(game).setWhite(false);
|
||||
permanent.getColor(game).setBlue(false);
|
||||
permanent.getColor(game).setBlack(false);
|
||||
permanent.getColor(game).setRed(false);
|
||||
permanent.getColor(game).setGreen(false);
|
||||
}
|
||||
if (token.getColor(game).hasColor()) {
|
||||
permanent.getColor(game).setColor(token.getColor(game));
|
||||
}
|
||||
break;
|
||||
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
if (loseAllAbilities && !keepAbilities) {
|
||||
permanent.removeAllAbilities(source.getSourceId(), game);
|
||||
}
|
||||
if (sublayer == SubLayer.NA) {
|
||||
if (!token.getAbilities().isEmpty()) {
|
||||
for (Ability ability : token.getAbilities()) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PTChangingEffects_7:
|
||||
if (sublayer == SubLayer.SetPT_7b) { // CDA can only define a characteristic of either the card or token it comes from.
|
||||
permanent.getToughness().setValue(token.getToughness().getValue());
|
||||
permanent.getPower().setValue(token.getPower().getValue());
|
||||
}
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
if (!result && this.duration == Duration.Custom) {
|
||||
this.discard();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -19,15 +14,15 @@ import java.util.UUID;
|
|||
*/
|
||||
public class BecomesCreatureTypeTargetEffect extends ContinuousEffectImpl {
|
||||
|
||||
protected SubTypeList subtypes = new SubTypeList();
|
||||
protected boolean loseOther; // loses other creature types
|
||||
private final SubTypeList subtypes = new SubTypeList();
|
||||
private final boolean loseOther; // loses other creature types
|
||||
|
||||
public BecomesCreatureTypeTargetEffect(Duration duration, SubType subtype) {
|
||||
this(duration, createArrayList(subtype));
|
||||
this(duration, new SubTypeList(subtype));
|
||||
}
|
||||
|
||||
public BecomesCreatureTypeTargetEffect(Duration duration, SubType subtype, boolean loseOther) {
|
||||
this(duration, createArrayList(subtype), loseOther);
|
||||
this(duration, new SubTypeList(subtype), loseOther);
|
||||
}
|
||||
|
||||
public BecomesCreatureTypeTargetEffect(Duration duration, SubTypeList subtypes) {
|
||||
|
|
@ -35,67 +30,45 @@ public class BecomesCreatureTypeTargetEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
|
||||
public BecomesCreatureTypeTargetEffect(Duration duration, SubTypeList subtypes, boolean loseOther) {
|
||||
super(duration, Outcome.Detriment);
|
||||
this.subtypes = subtypes;
|
||||
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment);
|
||||
this.subtypes.addAll(subtypes);
|
||||
this.staticText = setText();
|
||||
this.loseOther = loseOther;
|
||||
}
|
||||
|
||||
private static SubTypeList createArrayList(SubType subtype) {
|
||||
SubTypeList subtypes = new SubTypeList();
|
||||
subtypes.add(subtype);
|
||||
return subtypes;
|
||||
}
|
||||
|
||||
public BecomesCreatureTypeTargetEffect(final BecomesCreatureTypeTargetEffect effect) {
|
||||
super(effect);
|
||||
this.subtypes.addAll(effect.subtypes);
|
||||
this.loseOther = effect.loseOther;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BecomesCreatureTypeTargetEffect copy() {
|
||||
return new BecomesCreatureTypeTargetEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
public boolean apply(Game game, Ability source) {
|
||||
boolean flag = false;
|
||||
for (UUID targetPermanent : targetPointer.getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(targetPermanent);
|
||||
if (permanent != null) {
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
if (loseOther) {
|
||||
permanent.getSubtype(game).retainAll(SubType.getLandTypes());
|
||||
permanent.getSubtype(game).addAll(subtypes);
|
||||
} else {
|
||||
for (SubType subtype : subtypes) {
|
||||
if (!permanent.hasSubtype(subtype, game)) {
|
||||
permanent.getSubtype(game).add(subtype);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (duration == Duration.Custom) {
|
||||
discard();
|
||||
}
|
||||
if (permanent == null) {
|
||||
continue;
|
||||
}
|
||||
flag = true;
|
||||
if (loseOther) {
|
||||
permanent.removeAllCreatureTypes(game);
|
||||
}
|
||||
for (SubType subtype : subtypes) {
|
||||
permanent.addSubType(game, subtype);
|
||||
}
|
||||
}
|
||||
if (!flag && duration == Duration.Custom) {
|
||||
discard();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLayer(Layer layer) {
|
||||
return layer == Layer.TypeChangingEffects_4;
|
||||
}
|
||||
|
||||
private String setText() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Target creature becomes that type");
|
||||
|
|
|
|||
|
|
@ -1,33 +1,21 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.DependencyType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public class BecomesEnchantmentSourceEffect extends ContinuousEffectImpl implements SourceEffect {
|
||||
|
||||
public BecomesEnchantmentSourceEffect() {
|
||||
super(Duration.Custom, Outcome.AddAbility);
|
||||
super(Duration.Custom, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.AddAbility);
|
||||
staticText = "{this} becomes an Enchantment";
|
||||
dependencyTypes.add(DependencyType.EnchantmentAddingRemoving);
|
||||
|
||||
}
|
||||
|
||||
public BecomesEnchantmentSourceEffect(final BecomesEnchantmentSourceEffect effect) {
|
||||
|
|
@ -45,35 +33,17 @@ public class BecomesEnchantmentSourceEffect extends ContinuousEffectImpl impleme
|
|||
affectedObjectList.add(new MageObjectReference(source.getSourceId(), game));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
Permanent permanent = affectedObjectList.get(0).getPermanent(game);
|
||||
if (permanent != null) {
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
permanent.getCardType().clear();
|
||||
permanent.getSubtype(game).clear();
|
||||
if (!permanent.getCardType().contains(CardType.ENCHANTMENT)) {
|
||||
permanent.getCardType().add(CardType.ENCHANTMENT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
this.discard();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
Permanent permanent = affectedObjectList.get(0).getPermanent(game);
|
||||
if (permanent == null) {
|
||||
this.discard();
|
||||
return false;
|
||||
}
|
||||
permanent.getCardType().clear();
|
||||
permanent.getCardType().add(CardType.ENCHANTMENT);
|
||||
permanent.getSubtype(game).retainAll(SubType.getEnchantmentTypes());
|
||||
permanent.setIsAllCreatureTypes(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLayer(Layer layer) {
|
||||
return Layer.TypeChangingEffects_4 == layer;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple
|
|||
permanent.getSuperType().clear();
|
||||
permanent.getCardType().clear();
|
||||
permanent.addCardType(CardType.CREATURE);
|
||||
permanent.getSubtype(game).clear();
|
||||
permanent.removeAllSubTypes(game);
|
||||
permanent.getManaCost().clear();
|
||||
break;
|
||||
case ColorChangingEffects_5:
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen
|
|||
permanent.getSuperType().clear();
|
||||
permanent.getCardType().clear();
|
||||
permanent.addCardType(CardType.CREATURE);
|
||||
permanent.getSubtype(game).clear();
|
||||
permanent.removeAllSubTypes(game);
|
||||
break;
|
||||
case ColorChangingEffects_5:
|
||||
permanent.getColor(game).setColor(new ObjectColor());
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package mage.abilities.effects.common.continuous;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.*;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
|
@ -18,33 +19,26 @@ import mage.util.SubTypeList;
|
|||
*/
|
||||
public class BecomesSubtypeAllEffect extends ContinuousEffectImpl {
|
||||
|
||||
protected SubTypeList subtypes = new SubTypeList();
|
||||
protected boolean loseOther; // loses other subtypes
|
||||
protected FilterCreaturePermanent filter;
|
||||
private final SubTypeList subtypes = new SubTypeList();
|
||||
private final boolean loseOther; // loses other subtypes
|
||||
private final FilterCreaturePermanent filter;
|
||||
|
||||
public BecomesSubtypeAllEffect(Duration duration, SubType subtype) {
|
||||
this(duration, createArrayList(subtype));
|
||||
this(duration, new SubTypeList(subtype));
|
||||
}
|
||||
|
||||
public BecomesSubtypeAllEffect(Duration duration, SubTypeList subtypes) {
|
||||
this(duration, subtypes, new FilterCreaturePermanent("All creatures"), true);
|
||||
this(duration, subtypes, StaticFilters.FILTER_PERMANENT_CREATURE, true);
|
||||
}
|
||||
|
||||
public BecomesSubtypeAllEffect(Duration duration,
|
||||
SubTypeList subtypes, FilterCreaturePermanent filter, boolean loseOther) {
|
||||
super(duration, Outcome.Detriment);
|
||||
this.subtypes = subtypes;
|
||||
public BecomesSubtypeAllEffect(Duration duration, SubTypeList subtypes, FilterCreaturePermanent filter, boolean loseOther) {
|
||||
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment);
|
||||
this.subtypes.addAll(subtypes);
|
||||
this.staticText = setText();
|
||||
this.loseOther = loseOther;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
private static SubTypeList createArrayList(SubType subtype) {
|
||||
SubTypeList subtypes = new SubTypeList();
|
||||
subtypes.add(subtype);
|
||||
return subtypes;
|
||||
}
|
||||
|
||||
public BecomesSubtypeAllEffect(final BecomesSubtypeAllEffect effect) {
|
||||
super(effect);
|
||||
this.subtypes.addAll(effect.subtypes);
|
||||
|
|
@ -52,47 +46,32 @@ public class BecomesSubtypeAllEffect extends ContinuousEffectImpl {
|
|||
this.filter = effect.filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BecomesSubtypeAllEffect copy() {
|
||||
return new BecomesSubtypeAllEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source,
|
||||
Game game) {
|
||||
public boolean apply(Game game, Ability source) {
|
||||
boolean flag = false;
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
|
||||
if (permanent != null) {
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
if (loseOther) {
|
||||
permanent.getSubtype(game).retainAll(SubType.getLandTypes());
|
||||
permanent.getSubtype(game).addAll(subtypes);
|
||||
} else {
|
||||
for (SubType subtype : subtypes) {
|
||||
if (!permanent.hasSubtype(subtype, game)) {
|
||||
permanent.getSubtype(game).add(subtype);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (duration == Duration.Custom) {
|
||||
discard();
|
||||
if (permanent == null) {
|
||||
continue;
|
||||
}
|
||||
flag = true;
|
||||
if (loseOther) {
|
||||
permanent.removeAllCreatureTypes(game);
|
||||
}
|
||||
for (SubType subtype : subtypes) {
|
||||
permanent.addSubType(game, subtype);
|
||||
}
|
||||
}
|
||||
if (!flag && duration == Duration.Custom) {
|
||||
discard();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLayer(Layer layer) {
|
||||
return layer == Layer.TypeChangingEffects_4;
|
||||
}
|
||||
|
||||
private String setText() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Target creature becomes that type");
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import mage.abilities.costs.AlternativeCostSourceAbility;
|
|||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.cards.AdventureCardSpell;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ModalDoubleFacesCardHalf;
|
||||
import mage.cards.SplitCardHalf;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
|
|
@ -22,7 +23,7 @@ import java.util.UUID;
|
|||
public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImpl {
|
||||
|
||||
private final AlternativeCostSourceAbility alternativeCastingCostAbility;
|
||||
|
||||
|
||||
public CastFromHandWithoutPayingManaCostEffect() {
|
||||
this(StaticFilters.FILTER_CARDS_NON_LAND, true);
|
||||
}
|
||||
|
|
@ -38,7 +39,7 @@ public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImp
|
|||
condition = new CompoundCondition(SourceIsSpellCondition.instance, IsBeingCastFromHandCondition.instance);
|
||||
} else {
|
||||
condition = SourceIsSpellCondition.instance;
|
||||
}
|
||||
}
|
||||
this.alternativeCastingCostAbility = new AlternativeCostSourceAbility(null, condition, null, filter, true);
|
||||
this.staticText = "You may cast " + filter.getMessage()
|
||||
+ (fromHand ? " from your hand" : "")
|
||||
|
|
@ -88,9 +89,9 @@ enum IsBeingCastFromHandCondition implements Condition {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
MageObject object = game.getObject(source.getSourceId());
|
||||
if (object instanceof SplitCardHalf || object instanceof AdventureCardSpell) {
|
||||
UUID splitCardId = ((Card) object).getMainCard().getId();
|
||||
object = game.getObject(splitCardId);
|
||||
if (object instanceof SplitCardHalf || object instanceof AdventureCardSpell || object instanceof ModalDoubleFacesCardHalf) {
|
||||
UUID mainCardId = ((Card) object).getMainCard().getId();
|
||||
object = game.getObject(mainCardId);
|
||||
}
|
||||
if (object instanceof Spell) { // needed to check if it can be cast by alternate cost
|
||||
Spell spell = (Spell) object;
|
||||
|
|
|
|||
|
|
@ -40,10 +40,8 @@ public class CreaturesBecomeOtherTypeEffect extends ContinuousEffectImpl {
|
|||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
if (layer == Layer.TypeChangingEffects_4) {
|
||||
for (Permanent object: game.getBattlefield().getActivePermanents(this.filter, source.getControllerId(), game)) {
|
||||
if (!object.hasSubtype(this.subType, game)) {
|
||||
object.getSubtype(game).add(this.subType);
|
||||
}
|
||||
for (Permanent object : game.getBattlefield().getActivePermanents(this.filter, source.getControllerId(), game)) {
|
||||
object.addSubType(game, this.subType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class GainAllCreatureTypesAttachedEffect extends ContinuousEffectImpl {
|
||||
|
||||
public GainAllCreatureTypesAttachedEffect() {
|
||||
super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral);
|
||||
staticText = "and is every creature type";
|
||||
}
|
||||
|
||||
public GainAllCreatureTypesAttachedEffect(final GainAllCreatureTypesAttachedEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GainAllCreatureTypesAttachedEffect copy() {
|
||||
return new GainAllCreatureTypesAttachedEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent equipment = game.getPermanent(source.getSourceId());
|
||||
if (equipment == null) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanent(equipment.getAttachedTo());
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
permanent.setIsAllCreatureTypes(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class GainAllCreatureTypesTargetEffect extends ContinuousEffectImpl {
|
||||
|
||||
public GainAllCreatureTypesTargetEffect(Duration duration) {
|
||||
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral);
|
||||
}
|
||||
|
||||
public GainAllCreatureTypesTargetEffect(final GainAllCreatureTypesTargetEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GainAllCreatureTypesTargetEffect copy() {
|
||||
return new GainAllCreatureTypesTargetEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
int affectedTargets = 0;
|
||||
for (UUID permanentId : targetPointer.getTargets(game, source)) {
|
||||
Permanent target = game.getPermanent(permanentId);
|
||||
if (target != null) {
|
||||
target.setIsAllCreatureTypes(true);
|
||||
affectedTargets++;
|
||||
}
|
||||
}
|
||||
return affectedTargets > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
return "target " + mode.getTargets().get(0).getTargetName() + " gains all creature types " + duration.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.util.SubTypeList;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class HasSubtypesSourceEffect extends ContinuousEffectImpl {
|
||||
|
||||
private final SubTypeList subtypes = new SubTypeList();
|
||||
|
||||
public HasSubtypesSourceEffect(SubType... subTypes) {
|
||||
super(Duration.EndOfGame, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit);
|
||||
subtypes.add(subTypes);
|
||||
this.staticText = setText();
|
||||
}
|
||||
|
||||
public HasSubtypesSourceEffect(final HasSubtypesSourceEffect effect) {
|
||||
super(effect);
|
||||
this.subtypes.addAll(effect.subtypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasSubtypesSourceEffect copy() {
|
||||
return new HasSubtypesSourceEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||
if (sourceObject == null) {
|
||||
return false;
|
||||
}
|
||||
for (SubType subType : subtypes) {
|
||||
sourceObject.addSubType(game, subType);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String setText() {
|
||||
String s = "{this} is also ";
|
||||
switch (subtypes.size()) {
|
||||
case 0:
|
||||
throw new UnsupportedOperationException("Can't have zero subtypes");
|
||||
case 1:
|
||||
s += subtypes.get(0).getIndefiniteArticle() + " " + subtypes.get(0);
|
||||
break;
|
||||
case 2:
|
||||
s += subtypes.get(0).getIndefiniteArticle() + " " + subtypes.get(0);
|
||||
s += " and ";
|
||||
s += subtypes.get(1).getIndefiniteArticle() + " " + subtypes.get(1);
|
||||
break;
|
||||
default:
|
||||
for (int i = 0; i < subtypes.size(); i++) {
|
||||
if (i == 0) {
|
||||
s += subtypes.get(0).getIndefiniteArticle() + " " + subtypes.get(0) + ", ";
|
||||
} else if (i == subtypes.size() - 1) {
|
||||
s += "and " + subtypes.get(0);
|
||||
} else {
|
||||
s += subtypes.get(0) + ", ";
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,10 @@ package mage.abilities.effects.common.continuous;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.*;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
|
|
@ -29,8 +32,8 @@ public class LoseAllCreatureTypesTargetEffect extends ContinuousEffectImpl {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (permanent != null) {
|
||||
permanent.setIsAllCreatureTypes(false);
|
||||
return permanent.getSubtype(game).retainAll(SubType.getLandTypes());
|
||||
permanent.removeAllCreatureTypes(game);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public class LoseCreatureTypeSourceEffect extends ContinuousEffectImpl implement
|
|||
* @param lessThan
|
||||
*/
|
||||
public LoseCreatureTypeSourceEffect(DynamicValue dynamicValue, int lessThan) {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment);
|
||||
super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment);
|
||||
this.dynamicValue = dynamicValue;
|
||||
this.lessThan = lessThan;
|
||||
setText();
|
||||
|
|
@ -53,31 +53,22 @@ public class LoseCreatureTypeSourceEffect extends ContinuousEffectImpl implement
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (dynamicValue.calculate(game, source, this) >= lessThan) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
permanent.getCardType().remove(CardType.CREATURE);
|
||||
permanent.getSubtype(game).retainAll(SubType.getLandTypes());
|
||||
if (permanent.isAttacking() || permanent.getBlocking() > 0) {
|
||||
permanent.removeFromCombat(game);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
permanent.getCardType().remove(CardType.CREATURE);
|
||||
if (!permanent.isTribal()) {
|
||||
permanent.removeAllCreatureTypes(game);
|
||||
}
|
||||
if (permanent.isAttacking() || permanent.getBlocking() > 0) {
|
||||
permanent.removeFromCombat(game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setText() {
|
||||
|
|
@ -86,10 +77,4 @@ public class LoseCreatureTypeSourceEffect extends ContinuousEffectImpl implement
|
|||
sb.append(CardUtil.numberToText(lessThan)).append(", {this} isn't a creature");
|
||||
staticText = sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLayer(Layer layer) {
|
||||
return layer == Layer.TypeChangingEffects_4;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,20 +22,6 @@ public class SetCardSubtypeAttachedEffect extends ContinuousEffectImpl {
|
|||
this.setText();
|
||||
}
|
||||
|
||||
/*public SetCardSubtypeAttachedEffect(SubType setSubtype, Duration duration, AttachmentType attachmentType) {
|
||||
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit);
|
||||
this.setSubtypes.add(setSubtype);
|
||||
this.attachmentType = attachmentType;
|
||||
setText();
|
||||
}
|
||||
|
||||
public SetCardSubtypeAttachedEffect(List<String> setSubtypes, Duration duration, AttachmentType attachmentType) {
|
||||
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit);
|
||||
this.setSubtypes.addAll(setSubtypes);
|
||||
this.attachmentType = attachmentType;
|
||||
setText();
|
||||
}*/
|
||||
|
||||
public SetCardSubtypeAttachedEffect(final SetCardSubtypeAttachedEffect effect) {
|
||||
super(effect);
|
||||
this.setSubtypes = effect.setSubtypes;
|
||||
|
|
@ -45,13 +31,15 @@ public class SetCardSubtypeAttachedEffect extends ContinuousEffectImpl {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent equipment = game.getPermanent(source.getSourceId());
|
||||
if (equipment != null && equipment.getAttachedTo() != null) {
|
||||
Permanent target = game.getPermanent(equipment.getAttachedTo());
|
||||
if (target != null) {
|
||||
target.getSubtype(game).retainAll(SubType.getLandTypes());
|
||||
target.getSubtype(game).addAll(setSubtypes);
|
||||
}
|
||||
if (equipment == null || equipment.getAttachedTo() == null) {
|
||||
return true;
|
||||
}
|
||||
Permanent target = game.getPermanent(equipment.getAttachedTo());
|
||||
if (target == null) {
|
||||
return true;
|
||||
}
|
||||
target.removeAllCreatureTypes(game);
|
||||
target.getSubtype(game).addAll(setSubtypes);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* IMPORTANT: This only adds the chosen subtype while the source permanent is entering the battlefield.
|
||||
* You should also use @link{mage.abilities.effects.common.continuous.AddChosenSubtypeEffect} to make the subtype persist.
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class EnterAttributeAddChosenSubtypeEffect extends OneShotEffect {
|
||||
|
|
@ -35,9 +35,7 @@ public class EnterAttributeAddChosenSubtypeEffect extends OneShotEffect {
|
|||
Permanent permanent = game.getPermanentEntering(source.getSourceId());
|
||||
SubType subtype = (SubType) game.getState().getValue(source.getSourceId() + "_type");
|
||||
if (permanent != null && subtype != null) {
|
||||
if (!permanent.getSubtype(game).contains(subtype)) {
|
||||
permanent.getSubtype(game).add(subtype);
|
||||
}
|
||||
permanent.addSubType(game, subtype);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ public class FatesealEffect extends OneShotEffect {
|
|||
}
|
||||
// move cards to the top of the library
|
||||
controller.putCardsOnTopOfLibrary(cards, game, source, true);
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.FATESEAL, opponent.getId(), source.getSourceId(), source.getControllerId()));
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.FATESEALED, opponent.getId(), source.getSourceId(), source.getControllerId()));
|
||||
controller.setTopCardRevealed(revealed);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,31 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.*;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ModalDoubleFacesCardHalf;
|
||||
import mage.cards.SplitCardHalf;
|
||||
import mage.constants.*;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Aftermath
|
||||
*
|
||||
* <p>
|
||||
* TODO: Implement once we get details on the comprehensive rules meaning of the
|
||||
* ability
|
||||
*
|
||||
* <p>
|
||||
* Current text is a shell copied from Flashback
|
||||
*
|
||||
* @author stravant
|
||||
|
|
@ -113,9 +120,7 @@ class AftermathCantCastFromHand extends ContinuousRuleModifyingEffectImpl {
|
|||
Card card = game.getCard(event.getSourceId());
|
||||
if (card != null && card.getId().equals(source.getSourceId())) {
|
||||
Zone zone = game.getState().getZone(card.getId());
|
||||
if (zone != null && (zone != Zone.GRAVEYARD)) {
|
||||
return true;
|
||||
}
|
||||
return zone != null && (zone != Zone.GRAVEYARD);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -155,14 +160,16 @@ class AftermathExileAsResolvesFromGraveyard extends ReplacementEffectImpl {
|
|||
sourceCard = ((SplitCardHalf) sourceCard).getParentCard();
|
||||
sourceId = sourceCard.getId();
|
||||
}
|
||||
if (sourceCard instanceof ModalDoubleFacesCardHalf) {
|
||||
sourceCard = ((ModalDoubleFacesCardHalf) sourceCard).getParentCard();
|
||||
sourceId = sourceCard.getId();
|
||||
}
|
||||
|
||||
if (event.getTargetId().equals(sourceId)) {
|
||||
// Moving this spell from stack to yard
|
||||
Spell spell = game.getStack().getSpell(source.getSourceId());
|
||||
if (spell != null && spell.getFromZone() == Zone.GRAVEYARD) {
|
||||
// And this spell was cast from the graveyard, so we need to exile it
|
||||
return true;
|
||||
}
|
||||
// And this spell was cast from the graveyard, so we need to exile it
|
||||
return spell != null && spell.getFromZone() == Zone.GRAVEYARD;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
@ -174,6 +181,9 @@ class AftermathExileAsResolvesFromGraveyard extends ReplacementEffectImpl {
|
|||
if (sourceCard instanceof SplitCardHalf) {
|
||||
sourceCard = ((SplitCardHalf) sourceCard).getParentCard();
|
||||
}
|
||||
if (sourceCard instanceof ModalDoubleFacesCardHalf) {
|
||||
sourceCard = ((ModalDoubleFacesCardHalf) sourceCard).getParentCard();
|
||||
}
|
||||
if (sourceCard != null) {
|
||||
Player player = game.getPlayer(sourceCard.getOwnerId());
|
||||
if (player != null) {
|
||||
|
|
|
|||
|
|
@ -5,14 +5,18 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.ExileZone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterLandCard;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInExile;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -25,7 +29,8 @@ public class CascadeAbility extends TriggeredAbilityImpl {
|
|||
+ "nonland card that costs less."
|
||||
+ " You may cast it without paying its mana cost. "
|
||||
+ "Put the exiled cards on the bottom in a random order.)</i>";
|
||||
private boolean withReminder;
|
||||
private final boolean withReminder;
|
||||
private static final FilterCard filter = new FilterLandCard("land card (to put onto the battlefield)");
|
||||
|
||||
public CascadeAbility() {
|
||||
this(true);
|
||||
|
|
@ -36,7 +41,7 @@ public class CascadeAbility extends TriggeredAbilityImpl {
|
|||
this.withReminder = withReminder;
|
||||
}
|
||||
|
||||
public CascadeAbility(final CascadeAbility ability) {
|
||||
private CascadeAbility(final CascadeAbility ability) {
|
||||
super(ability);
|
||||
this.withReminder = ability.withReminder;
|
||||
}
|
||||
|
|
@ -71,11 +76,11 @@ public class CascadeAbility extends TriggeredAbilityImpl {
|
|||
|
||||
class CascadeEffect extends OneShotEffect {
|
||||
|
||||
public CascadeEffect() {
|
||||
CascadeEffect() {
|
||||
super(Outcome.PutCardInPlay);
|
||||
}
|
||||
|
||||
public CascadeEffect(CascadeEffect effect) {
|
||||
private CascadeEffect(CascadeEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
|
@ -86,8 +91,7 @@ class CascadeEffect extends OneShotEffect {
|
|||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
ExileZone exile = game.getExile().createZone(source.getSourceId(),
|
||||
controller.getName() + " Cascade");
|
||||
Cards cards = new CardsImpl();
|
||||
card = game.getCard(source.getSourceId());
|
||||
if (card == null) {
|
||||
return false;
|
||||
|
|
@ -98,33 +102,43 @@ class CascadeEffect extends OneShotEffect {
|
|||
if (card == null) {
|
||||
break;
|
||||
}
|
||||
controller.moveCardsToExile(card, source, game, true, exile.getId(), exile.getName());
|
||||
cards.add(card);
|
||||
controller.moveCards(card, Zone.EXILED, source, game);
|
||||
} while (controller.canRespond()
|
||||
&& (card.isLand()
|
||||
|| !cardThatCostsLess(sourceCost, card, game)));
|
||||
&& (card.isLand() || card.getConvertedManaCost() >= sourceCost));
|
||||
|
||||
controller.getLibrary().reset(); // set back empty draw state if that caused an empty draw
|
||||
|
||||
if (card != null) {
|
||||
if (controller.chooseUse(outcome, "Use cascade effect on " + card.getLogName() + '?', source, game)) {
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE);
|
||||
controller.cast(controller.chooseAbilityForCast(card, game, true),
|
||||
game, true, new ApprovingObject(source, game));
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null);
|
||||
}
|
||||
GameEvent event = GameEvent.getEvent(
|
||||
GameEvent.EventType.CASCADE_LAND, source.getSourceId(),
|
||||
source.getSourceId(), source.getControllerId(), 0
|
||||
);
|
||||
game.replaceEvent(event);
|
||||
if (event.getAmount() > 0) {
|
||||
TargetCardInExile target = new TargetCardInExile(
|
||||
0, event.getAmount(), StaticFilters.FILTER_CARD_LAND, null, true
|
||||
);
|
||||
controller.choose(Outcome.PutCardInPlay, cards, target, game);
|
||||
controller.moveCards(
|
||||
new CardsImpl(target.getTargets()).getCards(game), Zone.BATTLEFIELD,
|
||||
source, game, true, false, false, null
|
||||
);
|
||||
}
|
||||
if (card != null && controller.chooseUse(
|
||||
outcome, "Use cascade effect on " + card.getLogName() + '?', source, game
|
||||
)) {
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE);
|
||||
controller.cast(controller.chooseAbilityForCast(card, game, true),
|
||||
game, true, new ApprovingObject(source, game));
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null);
|
||||
}
|
||||
// Move the remaining cards to the buttom of the library in a random order
|
||||
return controller.putCardsOnBottomOfLibrary(new CardsImpl(exile), game, source, false);
|
||||
cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED);
|
||||
return controller.putCardsOnBottomOfLibrary(cards, game, source, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CascadeEffect copy() {
|
||||
return new CascadeEffect(this);
|
||||
}
|
||||
|
||||
private boolean cardThatCostsLess(int value, Card card, Game game) {
|
||||
|
||||
return card.getConvertedManaCost() < value;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.ObjectColor;
|
||||
|
|
@ -19,7 +18,6 @@ import mage.players.Player;
|
|||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class EmbalmAbility extends ActivatedAbilityImpl {
|
||||
|
|
@ -89,9 +87,7 @@ class EmbalmEffect extends OneShotEffect {
|
|||
EmptyToken token = new EmptyToken();
|
||||
CardUtil.copyTo(token).from(card); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer)
|
||||
token.getColor(game).setColor(ObjectColor.WHITE);
|
||||
if (!token.hasSubtype(SubType.ZOMBIE, game)) {
|
||||
token.getSubtype(game).add(0, SubType.ZOMBIE);
|
||||
}
|
||||
token.addSubType(game, SubType.ZOMBIE);
|
||||
token.getManaCost().clear();
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.EMBALMED_CREATURE, token.getId(), source.getSourceId(), controller.getId()));
|
||||
token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId(), false, false, null);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.ObjectColor;
|
||||
|
|
@ -19,7 +18,6 @@ import mage.players.Player;
|
|||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author igoudt
|
||||
*/
|
||||
public class EternalizeAbility extends ActivatedAbilityImpl {
|
||||
|
|
@ -94,9 +92,7 @@ class EternalizeEffect extends OneShotEffect {
|
|||
EmptyToken token = new EmptyToken();
|
||||
CardUtil.copyTo(token).from(card); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer)
|
||||
token.getColor(game).setColor(ObjectColor.BLACK);
|
||||
if (!token.hasSubtype(SubType.ZOMBIE, game)) {
|
||||
token.getSubtype(game).add(0, SubType.ZOMBIE);
|
||||
}
|
||||
token.addSubType(game, SubType.ZOMBIE);
|
||||
token.getManaCost().clear();
|
||||
token.removePTCDA();
|
||||
token.getPower().modifyBaseValue(4);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import mage.abilities.costs.Costs;
|
|||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
|
|
@ -68,12 +69,18 @@ public class FlashbackAbility extends SpellAbility {
|
|||
}
|
||||
// Flashback can never cast a split card by Fuse, because Fuse only works from hand
|
||||
// https://tappedout.net/mtg-questions/snapcaster-mage-and-flashback-on-a-fuse-card-one-or-both-halves-legal-targets/
|
||||
if (card.isSplitCard()) {
|
||||
if (card instanceof SplitCard) {
|
||||
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||
return ((SplitCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||
return ((SplitCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||
}
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
if (((ModalDoubleFacesCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||
return ((ModalDoubleFacesCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||
} else if (((ModalDoubleFacesCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||
return ((ModalDoubleFacesCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||
}
|
||||
}
|
||||
return card.getSpellAbility().canActivate(playerId, game);
|
||||
}
|
||||
|
|
@ -87,12 +94,18 @@ public class FlashbackAbility extends SpellAbility {
|
|||
if (card != null) {
|
||||
if (spellAbilityToResolve == null) {
|
||||
SpellAbility spellAbilityCopy = null;
|
||||
if (card.isSplitCard()) {
|
||||
if (card instanceof SplitCard) {
|
||||
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||
spellAbilityCopy = ((SplitCard) card).getLeftHalfCard().getSpellAbility().copy();
|
||||
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||
spellAbilityCopy = ((SplitCard) card).getRightHalfCard().getSpellAbility().copy();
|
||||
}
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
if (((ModalDoubleFacesCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||
spellAbilityCopy = ((ModalDoubleFacesCard) card).getLeftHalfCard().getSpellAbility().copy();
|
||||
} else if (((ModalDoubleFacesCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||
spellAbilityCopy = ((ModalDoubleFacesCard) card).getRightHalfCard().getSpellAbility().copy();
|
||||
}
|
||||
} else {
|
||||
spellAbilityCopy = card.getSpellAbility().copy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,9 +52,10 @@ public class TransformAbility extends SimpleStaticAbility {
|
|||
for (CardType type : sourceCard.getCardType()) {
|
||||
permanent.addCardType(type);
|
||||
}
|
||||
permanent.getSubtype(game).clear();
|
||||
permanent.removeAllSubTypes(game);
|
||||
permanent.setIsAllCreatureTypes(sourceCard.isAllCreatureTypes());
|
||||
for (SubType type : sourceCard.getSubtype(game)) {
|
||||
permanent.getSubtype(game).add(type);
|
||||
permanent.addSubType(game, type);
|
||||
}
|
||||
permanent.getSuperType().clear();
|
||||
for (SuperType type : sourceCard.getSuperType()) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package mage.cards;
|
||||
|
||||
import mage.abilities.Modes;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package mage.cards;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Abilities;
|
||||
|
|
@ -16,6 +14,9 @@ import mage.game.Game;
|
|||
import mage.game.GameState;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface Card extends MageObject {
|
||||
|
||||
UUID getOwnerId();
|
||||
|
|
@ -29,8 +30,9 @@ public interface Card extends MageObject {
|
|||
/**
|
||||
* For cards: return all basic and dynamic abilities
|
||||
* For permanents: return all basic and dynamic abilities
|
||||
*
|
||||
* @param game
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
Abilities<Ability> getAbilities(Game game);
|
||||
|
||||
|
|
@ -62,8 +64,6 @@ public interface Card extends MageObject {
|
|||
|
||||
String getFlipCardName();
|
||||
|
||||
boolean isSplitCard();
|
||||
|
||||
boolean isTransformable();
|
||||
|
||||
void setTransformable(boolean transformable);
|
||||
|
|
@ -76,21 +76,23 @@ public interface Card extends MageObject {
|
|||
|
||||
void addInfo(String key, String value, Game game);
|
||||
|
||||
// WARNING, don't add new move/remove methods (if you add then you must override it in all multi-part cards like Split or MDF)
|
||||
|
||||
/**
|
||||
* Moves the card to the specified zone
|
||||
*
|
||||
* @param zone
|
||||
* @param sourceId
|
||||
* @param game
|
||||
* @param flag If zone
|
||||
* <ul>
|
||||
* <li>LIBRARY: <ul><li>true - put on top</li><li>false - put on
|
||||
* bottom</li></ul></li>
|
||||
* <li>BATTLEFIELD: <ul><li>true - tapped</li><li>false -
|
||||
* untapped</li></ul></li>
|
||||
* <li>GRAVEYARD: <ul><li>true - not from Battlefield</li><li>false - from
|
||||
* Battlefield</li></ul></li>
|
||||
* </ul>
|
||||
* @param flag If zone
|
||||
* <ul>
|
||||
* <li>LIBRARY: <ul><li>true - put on top</li><li>false - put on
|
||||
* bottom</li></ul></li>
|
||||
* <li>BATTLEFIELD: <ul><li>true - tapped</li><li>false -
|
||||
* untapped</li></ul></li>
|
||||
* <li>GRAVEYARD: <ul><li>true - not from Battlefield</li><li>false - from
|
||||
* Battlefield</li></ul></li>
|
||||
* </ul>
|
||||
* @return true if card was moved to zone
|
||||
*/
|
||||
boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag);
|
||||
|
|
@ -100,8 +102,8 @@ public interface Card extends MageObject {
|
|||
/**
|
||||
* Moves the card to an exile zone
|
||||
*
|
||||
* @param exileId set to null for generic exile zone
|
||||
* @param name used for exile zone with the specified exileId
|
||||
* @param exileId set to null for generic exile zone
|
||||
* @param name used for exile zone with the specified exileId
|
||||
* @param sourceId
|
||||
* @param game
|
||||
* @return true if card was moved to zone
|
||||
|
|
@ -112,6 +114,7 @@ public interface Card extends MageObject {
|
|||
|
||||
boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId);
|
||||
|
||||
// WARNING, don't add new move/remove methods (if you add then you must override it in all multi-parts card like Split Half or MDF Half)
|
||||
boolean removeFromZone(Game game, Zone fromZone, UUID sourceId);
|
||||
|
||||
boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
package mage.cards;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectImpl;
|
||||
import mage.Mana;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.*;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.HintUtils;
|
||||
import mage.abilities.keyword.FlashbackAbility;
|
||||
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
||||
import mage.cards.repository.PluginClassloaderRegistery;
|
||||
|
|
@ -22,6 +19,7 @@ import mage.game.events.ZoneChangeEvent;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.GameLog;
|
||||
import mage.util.SubTypeList;
|
||||
import mage.watchers.Watcher;
|
||||
|
|
@ -57,10 +55,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
protected boolean flipCard;
|
||||
protected String flipCardName;
|
||||
protected boolean usesVariousArt = false;
|
||||
protected boolean splitCard;
|
||||
protected boolean morphCard;
|
||||
protected boolean modalDFC; // modal double faces card
|
||||
|
||||
protected List<UUID> attachments = new ArrayList<>();
|
||||
|
||||
public CardImpl(UUID ownerId, CardSetInfo setInfo, CardType[] cardTypes, String costs) {
|
||||
|
|
@ -139,9 +134,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
flipCard = card.flipCard;
|
||||
flipCardName = card.flipCardName;
|
||||
usesVariousArt = card.usesVariousArt;
|
||||
splitCard = card.splitCard;
|
||||
morphCard = card.morphCard;
|
||||
modalDFC = card.modalDFC;
|
||||
|
||||
this.attachments.addAll(card.attachments);
|
||||
}
|
||||
|
|
@ -223,52 +216,16 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
game.getState().getCardState(objectId).addInfo(key, value);
|
||||
}
|
||||
|
||||
protected static final List<String> rulesError = ImmutableList.of("Exception occurred in rules generation");
|
||||
|
||||
@Override
|
||||
public List<String> getRules() {
|
||||
try {
|
||||
return getAbilities().getRules(this.getName());
|
||||
} catch (Exception e) {
|
||||
logger.info("Exception in rules generation for card: " + this.getName(), e);
|
||||
}
|
||||
return rulesError;
|
||||
Abilities<Ability> sourceAbilities = this.getAbilities();
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(this.getId(), this.getName(), sourceAbilities, sourceAbilities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRules(Game game) {
|
||||
try {
|
||||
List<String> rules = getAbilities(game).getRules(getName());
|
||||
if (game != null) {
|
||||
// debug state
|
||||
for (String data : game.getState().getCardState(objectId).getInfo().values()) {
|
||||
rules.add(data);
|
||||
}
|
||||
// ability hints
|
||||
List<String> abilityHints = new ArrayList<>();
|
||||
if (HintUtils.ABILITY_HINTS_ENABLE) {
|
||||
for (Ability ability : abilities) {
|
||||
for (Hint hint : ability.getHints()) {
|
||||
String s = hint.getText(game, ability);
|
||||
if (s != null && !s.isEmpty()) {
|
||||
abilityHints.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// restrict hints only for permanents, not cards
|
||||
// total hints
|
||||
if (!abilityHints.isEmpty()) {
|
||||
rules.add(HintUtils.HINT_START_MARK);
|
||||
HintUtils.appendHints(rules, abilityHints);
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception in rules generation for card: " + this.getName(), e);
|
||||
}
|
||||
return rulesError;
|
||||
Abilities<Ability> sourceAbilities = this.getAbilities(game);
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(), sourceAbilities, sourceAbilities);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -314,7 +271,8 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
// workaround to add dynamic flashback ability from main card to all parts (example: Snapcaster Mage gives flashback to split card)
|
||||
if (!this.getId().equals(this.getMainCard().getId())) {
|
||||
CardState mainCardState = game.getState().getCardState(this.getMainCard().getId());
|
||||
if (mainCardState != null
|
||||
if (this.getSpellAbility() != null // lands can't be casted (haven't spell ability), so ignore it
|
||||
&& mainCardState != null
|
||||
&& !mainCardState.hasLostAllAbilities()
|
||||
&& mainCardState.getAbilities().containsClass(FlashbackAbility.class)) {
|
||||
FlashbackAbility flash = new FlashbackAbility(this.getManaCost(), this.isInstant() ? TimingRule.INSTANT : TimingRule.SORCERY);
|
||||
|
|
@ -563,13 +521,22 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
stackObject = game.getStack().getSpell(this.getId(), false);
|
||||
}
|
||||
|
||||
if (stackObject == null && (this instanceof SplitCard)) { // handle if half of Split cast is on the stack
|
||||
// handle half of Split Cards on stack
|
||||
if (stackObject == null && (this instanceof SplitCard)) {
|
||||
stackObject = game.getStack().getSpell(((SplitCard) this).getLeftHalfCard().getId(), false);
|
||||
if (stackObject == null) {
|
||||
stackObject = game.getStack().getSpell(((SplitCard) this).getRightHalfCard().getId(), false);
|
||||
}
|
||||
}
|
||||
|
||||
// handle half of Modal Double Faces Cards on stack
|
||||
if (stackObject == null && (this instanceof ModalDoubleFacesCard)) {
|
||||
stackObject = game.getStack().getSpell(((ModalDoubleFacesCard) this).getLeftHalfCard().getId(), false);
|
||||
if (stackObject == null) {
|
||||
stackObject = game.getStack().getSpell(((ModalDoubleFacesCard) this).getRightHalfCard().getId(), false);
|
||||
}
|
||||
}
|
||||
|
||||
if (stackObject == null && (this instanceof AdventureCard)) {
|
||||
stackObject = game.getStack().getSpell(((AdventureCard) this).getSpellCard().getId(), false);
|
||||
}
|
||||
|
|
@ -687,10 +654,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
|
||||
@Override
|
||||
public final Card getSecondCardFace() {
|
||||
// TODO: remove when MDFCs are implemented
|
||||
if (modalDFC) {
|
||||
return null;
|
||||
}
|
||||
// init second side card on first call
|
||||
if (secondSideCardClazz == null && secondSideCard == null) {
|
||||
return null;
|
||||
|
|
@ -726,11 +689,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
return flipCardName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSplitCard() {
|
||||
return splitCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUsesVariousArt() {
|
||||
return usesVariousArt;
|
||||
|
|
|
|||
281
Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java
Normal file
281
Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
package mage.cards;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Abilities;
|
||||
import mage.abilities.AbilitiesImpl;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCosts;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.SubTypeList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public abstract class ModalDoubleFacesCard extends CardImpl {
|
||||
|
||||
protected Card leftHalfCard; // main card in all zone
|
||||
protected Card rightHalfCard; // second side card, can be only in stack and battlefield zones
|
||||
|
||||
public ModalDoubleFacesCard(UUID ownerId, CardSetInfo setInfo,
|
||||
CardType[] typesLeft, SubType[] subTypesLeft, String costsLeft,
|
||||
String secondSideName, CardType[] typesRight, SubType[] subTypesRight, String costsRight) {
|
||||
super(ownerId, setInfo, typesLeft, costsLeft + costsRight, SpellAbilityType.MODAL);
|
||||
// main card name must be same as left side
|
||||
leftHalfCard = new ModalDoubleFacesCardHalfImpl(this.getOwnerId(), new CardSetInfo(setInfo.getName(), setInfo.getExpansionSetCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()),
|
||||
typesLeft, subTypesLeft, costsLeft, this, SpellAbilityType.MODAL_LEFT);
|
||||
rightHalfCard = new ModalDoubleFacesCardHalfImpl(this.getOwnerId(), new CardSetInfo(secondSideName, setInfo.getExpansionSetCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()),
|
||||
typesRight, subTypesRight, costsRight, this, SpellAbilityType.MODAL_RIGHT);
|
||||
}
|
||||
|
||||
public ModalDoubleFacesCard(ModalDoubleFacesCard card) {
|
||||
super(card);
|
||||
this.leftHalfCard = card.getLeftHalfCard().copy();
|
||||
((ModalDoubleFacesCardHalf) leftHalfCard).setParentCard(this);
|
||||
this.rightHalfCard = card.rightHalfCard.copy();
|
||||
((ModalDoubleFacesCardHalf) rightHalfCard).setParentCard(this);
|
||||
}
|
||||
|
||||
public ModalDoubleFacesCardHalf getLeftHalfCard() {
|
||||
return (ModalDoubleFacesCardHalf) leftHalfCard;
|
||||
}
|
||||
|
||||
public ModalDoubleFacesCardHalf getRightHalfCard() {
|
||||
return (ModalDoubleFacesCardHalf) rightHalfCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assignNewId() {
|
||||
super.assignNewId();
|
||||
leftHalfCard.assignNewId();
|
||||
rightHalfCard.assignNewId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCopy(boolean isCopy, MageObject copiedFrom) {
|
||||
super.setCopy(isCopy, copiedFrom);
|
||||
leftHalfCard.setCopy(isCopy, copiedFrom);
|
||||
rightHalfCard.setCopy(isCopy, copiedFrom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, List<UUID> appliedEffects) {
|
||||
if (super.moveToZone(toZone, sourceId, game, flag, appliedEffects)) {
|
||||
game.getState().setZone(leftHalfCard.getId(), toZone);
|
||||
game.getState().setZone(rightHalfCard.getId(), toZone);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZone(Zone zone, Game game) {
|
||||
super.setZone(zone, game);
|
||||
game.setZone(leftHalfCard.getId(), zone);
|
||||
game.setZone(rightHalfCard.getId(), zone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) {
|
||||
if (super.moveToExile(exileId, name, sourceId, game, appliedEffects)) {
|
||||
Zone currentZone = game.getState().getZone(getId());
|
||||
game.getState().setZone(leftHalfCard.getId(), currentZone);
|
||||
game.getState().setZone(rightHalfCard.getId(), currentZone);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
|
||||
// zone contains only one main card
|
||||
return super.removeFromZone(game, fromZone, sourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) {
|
||||
if (isCopy()) { // same as meld cards
|
||||
super.updateZoneChangeCounter(game, event);
|
||||
return;
|
||||
}
|
||||
super.updateZoneChangeCounter(game, event);
|
||||
leftHalfCard.updateZoneChangeCounter(game, event);
|
||||
rightHalfCard.updateZoneChangeCounter(game, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) {
|
||||
switch (ability.getSpellAbilityType()) {
|
||||
case MODAL_LEFT:
|
||||
return this.leftHalfCard.cast(game, fromZone, ability, controllerId);
|
||||
case MODAL_RIGHT:
|
||||
return this.rightHalfCard.cast(game, fromZone, ability, controllerId);
|
||||
default:
|
||||
if (this.leftHalfCard.getSpellAbility() != null)
|
||||
this.leftHalfCard.getSpellAbility().setControllerId(controllerId);
|
||||
if (this.rightHalfCard.getSpellAbility() != null)
|
||||
this.rightHalfCard.getSpellAbility().setControllerId(controllerId);
|
||||
return super.cast(game, fromZone, ability, controllerId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ArrayList<CardType> getCardType() {
|
||||
// CardImpl's constructor can call some code on init, so you must check left/right before
|
||||
// it's a bad workaround
|
||||
return leftHalfCard != null ? leftHalfCard.getCardType() : cardType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubTypeList getSubtype(Game game) {
|
||||
// rules: While a double-faced card isn’t on the stack or battlefield, consider only the characteristics of its front face.
|
||||
|
||||
// CardImpl's constructor can call some code on init, so you must check left/right before
|
||||
return leftHalfCard != null ? leftHalfCard.getSubtype(game) : subtype;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSubtype(SubType subtype, Game game) {
|
||||
return leftHalfCard.hasSubtype(subtype, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<SuperType> getSuperType() {
|
||||
return EnumSet.noneOf(SuperType.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Abilities<Ability> getAbilities() {
|
||||
return getInnerAbilities(false);
|
||||
}
|
||||
|
||||
public Abilities<Ability> getSharedAbilities(Game game) {
|
||||
// no shared abilities for mdf cards (e.g. must be left or right only)
|
||||
return new AbilitiesImpl<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Abilities<Ability> getAbilities(Game game) {
|
||||
return getInnerAbilities(game, false);
|
||||
}
|
||||
|
||||
private Abilities<Ability> getInnerAbilities(Game game, boolean showOnlyMainSide) {
|
||||
Abilities<Ability> allAbilites = new AbilitiesImpl<>();
|
||||
|
||||
// ignore default spell ability from main card (only halfes are actual)
|
||||
for (Ability ability : super.getAbilities(game)) {
|
||||
if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.MODAL) {
|
||||
continue;
|
||||
}
|
||||
allAbilites.add(ability);
|
||||
}
|
||||
|
||||
allAbilites.addAll(leftHalfCard.getAbilities(game));
|
||||
if (!showOnlyMainSide) {
|
||||
allAbilites.addAll(rightHalfCard.getAbilities(game));
|
||||
}
|
||||
|
||||
return allAbilites;
|
||||
}
|
||||
|
||||
private Abilities<Ability> getInnerAbilities(boolean showOnlyMainSide) {
|
||||
Abilities<Ability> allAbilites = new AbilitiesImpl<>();
|
||||
|
||||
// ignore default spell ability from main card (only halfes are actual)
|
||||
for (Ability ability : super.getAbilities()) {
|
||||
if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.MODAL) {
|
||||
continue;
|
||||
}
|
||||
allAbilites.add(ability);
|
||||
}
|
||||
|
||||
allAbilites.addAll(leftHalfCard.getAbilities());
|
||||
if (!showOnlyMainSide) {
|
||||
allAbilites.addAll(rightHalfCard.getAbilities());
|
||||
}
|
||||
|
||||
return allAbilites;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRules() {
|
||||
// rules must show only main side (another side visible by toggle/transform button in GUI)
|
||||
// card hints from both sides
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(this.getId(), this.getName(),
|
||||
this.getInnerAbilities(true), this.getInnerAbilities(false)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRules(Game game) {
|
||||
// rules must show only main side (another side visible by toggle/transform button in GUI)
|
||||
// card hints from both sides
|
||||
return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(),
|
||||
this.getInnerAbilities(game, true), this.getInnerAbilities(game, false)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
return super.hasAbility(ability, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectColor getColor(Game game) {
|
||||
return leftHalfCard.getColor(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectColor getFrameColor(Game game) {
|
||||
return leftHalfCard.getFrameColor(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwnerId(UUID ownerId) {
|
||||
super.setOwnerId(ownerId);
|
||||
abilities.setControllerId(ownerId);
|
||||
leftHalfCard.getAbilities().setControllerId(ownerId);
|
||||
leftHalfCard.setOwnerId(ownerId);
|
||||
rightHalfCard.getAbilities().setControllerId(ownerId);
|
||||
rightHalfCard.setOwnerId(ownerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManaCosts<ManaCost> getManaCost() {
|
||||
return leftHalfCard.getManaCost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConvertedManaCost() {
|
||||
// Rules:
|
||||
// The converted mana cost of a modal double-faced card is based on the characteristics of the
|
||||
// face that’s being considered. On the stack and battlefield, consider whichever face is up.
|
||||
// In all other zones, consider only the front face. This is different than how the converted
|
||||
// mana cost of a transforming double-faced card is determined.
|
||||
|
||||
// on stack or battlefield it must be half card with own cost
|
||||
return leftHalfCard.getConvertedManaCost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MageInt getPower() {
|
||||
return leftHalfCard.getPower();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MageInt getToughness() {
|
||||
return leftHalfCard.getToughness();
|
||||
}
|
||||
}
|
||||
18
Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java
Normal file
18
Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package mage.cards;
|
||||
|
||||
import mage.MageInt;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public interface ModalDoubleFacesCardHalf extends Card {
|
||||
|
||||
@Override
|
||||
ModalDoubleFacesCardHalf copy();
|
||||
|
||||
void setParentCard(ModalDoubleFacesCard card);
|
||||
|
||||
ModalDoubleFacesCard getParentCard();
|
||||
|
||||
void setPT(MageInt power, MageInt toughtness);
|
||||
}
|
||||
109
Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java
Normal file
109
Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
package mage.cards;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class ModalDoubleFacesCardHalfImpl extends CardImpl implements ModalDoubleFacesCardHalf {
|
||||
|
||||
ModalDoubleFacesCard parentCard;
|
||||
|
||||
public ModalDoubleFacesCardHalfImpl(UUID ownerId, CardSetInfo setInfo, CardType[] cardTypes, SubType[] cardSubTypes,
|
||||
String costs, ModalDoubleFacesCard parentCard, SpellAbilityType spellAbilityType) {
|
||||
super(ownerId, setInfo, cardTypes, costs, spellAbilityType);
|
||||
this.subtype.addAll(Arrays.asList(cardSubTypes));
|
||||
this.parentCard = parentCard;
|
||||
}
|
||||
|
||||
public ModalDoubleFacesCardHalfImpl(final ModalDoubleFacesCardHalfImpl card) {
|
||||
super(card);
|
||||
this.parentCard = card.parentCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getOwnerId() {
|
||||
return parentCard.getOwnerId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImageName() {
|
||||
// TODO: own name?
|
||||
return parentCard.getImageName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExpansionSetCode() {
|
||||
// TODO: own set code?
|
||||
return parentCard.getExpansionSetCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCardNumber() {
|
||||
// TODO: own card number?
|
||||
return parentCard.getCardNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, List<UUID> appliedEffects) {
|
||||
return parentCard.moveToZone(toZone, sourceId, game, flag, appliedEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) {
|
||||
return parentCard.moveToExile(exileId, name, sourceId, game, appliedEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
|
||||
return parentCard.removeFromZone(game, fromZone, sourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModalDoubleFacesCard getMainCard() {
|
||||
return parentCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZone(Zone zone, Game game) {
|
||||
game.setZone(parentCard.getId(), zone);
|
||||
game.setZone(parentCard.getLeftHalfCard().getId(), zone);
|
||||
game.setZone(parentCard.getRightHalfCard().getId(), zone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModalDoubleFacesCardHalfImpl copy() {
|
||||
return new ModalDoubleFacesCardHalfImpl(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParentCard(ModalDoubleFacesCard card) {
|
||||
this.parentCard = card;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModalDoubleFacesCard getParentCard() {
|
||||
return this.parentCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPT(MageInt power, MageInt toughness) {
|
||||
this.power = power;
|
||||
this.toughness = toughness;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdName() {
|
||||
// id must send to main card (popup card hint in game logs)
|
||||
return getName() + " [" + parentCard.getId().toString().substring(0, 3) + ']';
|
||||
}
|
||||
}
|
||||
|
|
@ -32,7 +32,6 @@ public abstract class SplitCard extends CardImpl {
|
|||
String[] names = setInfo.getName().split(" // ");
|
||||
leftHalfCard = new SplitCardHalfImpl(this.getOwnerId(), new CardSetInfo(names[0], setInfo.getExpansionSetCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()), typesLeft, costsLeft, this, SpellAbilityType.SPLIT_LEFT);
|
||||
rightHalfCard = new SplitCardHalfImpl(this.getOwnerId(), new CardSetInfo(names[1], setInfo.getExpansionSetCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()), typesRight, costsRight, this, SpellAbilityType.SPLIT_RIGHT);
|
||||
this.splitCard = true;
|
||||
}
|
||||
|
||||
public SplitCard(SplitCard card) {
|
||||
|
|
@ -187,4 +186,15 @@ public abstract class SplitCard extends CardImpl {
|
|||
rightHalfCard.getAbilities().setControllerId(ownerId);
|
||||
rightHalfCard.setOwnerId(ownerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConvertedManaCost() {
|
||||
// 202.3d The converted mana cost of a split card not on the stack or of a fused split spell on the
|
||||
// stack is determined from the combined mana costs of its halves. Otherwise, while a split card is
|
||||
// on the stack, the converted mana cost of the spell is determined by the mana cost of the half
|
||||
// that was chosen to be cast. See rule 708, “Split Cards.”
|
||||
|
||||
// split card and it's halfes contains own mana costs, so no need to rewrite logic
|
||||
return super.getConvertedManaCost();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package mage.cards;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public interface SplitCardHalf extends Card {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package mage.cards;
|
||||
|
||||
import mage.constants.CardType;
|
||||
|
|
@ -14,7 +9,6 @@ import java.util.List;
|
|||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class SplitCardHalfImpl extends CardImpl implements SplitCardHalf {
|
||||
|
|
@ -61,6 +55,11 @@ public class SplitCardHalfImpl extends CardImpl implements SplitCardHalf {
|
|||
return splitCardParent.moveToExile(exileId, name, sourceId, game, appliedEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
|
||||
return splitCardParent.removeFromZone(game, fromZone, sourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SplitCard getMainCard() {
|
||||
return splitCardParent;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import mage.abilities.costs.mana.ManaCost;
|
|||
import mage.abilities.costs.mana.ManaCosts;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.repository.CardInfo;
|
||||
import mage.cards.repository.CardRepository;
|
||||
import org.apache.log4j.Logger;
|
||||
|
|
@ -20,6 +21,7 @@ import java.util.List;
|
|||
public class MockCard extends CardImpl {
|
||||
|
||||
static public String ADVENTURE_NAME_SEPARATOR = " // ";
|
||||
static public String MODAL_DOUBLE_FACES_NAME_SEPARATOR = " // ";
|
||||
|
||||
// Needs to be here, as it is normally calculated from the
|
||||
// PlaneswalkerEntersWithLoyaltyAbility of the card... but the MockCard
|
||||
|
|
@ -30,6 +32,7 @@ public class MockCard extends CardImpl {
|
|||
protected ManaCosts<ManaCost> manaCostLeft;
|
||||
protected ManaCosts<ManaCost> manaCostRight;
|
||||
protected String adventureSpellName;
|
||||
protected boolean isModalDoubleFacesCard;
|
||||
|
||||
public MockCard(CardInfo card) {
|
||||
super(null, card.getName());
|
||||
|
|
@ -53,7 +56,6 @@ public class MockCard extends CardImpl {
|
|||
this.frameColor = card.getFrameColor();
|
||||
this.frameStyle = card.getFrameStyle();
|
||||
|
||||
this.splitCard = card.isSplitCard();
|
||||
this.flipCard = card.isFlipCard();
|
||||
|
||||
this.transformable = card.isDoubleFaced();
|
||||
|
|
@ -66,10 +68,16 @@ public class MockCard extends CardImpl {
|
|||
this.adventureSpellName = card.getAdventureSpellName();
|
||||
}
|
||||
|
||||
if (card.isModalDoubleFacesCard()) {
|
||||
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card.getCard();
|
||||
CardInfo mdfSecondSide = new CardInfo(mdfCard.getRightHalfCard());
|
||||
this.secondSideCard = new MockCard(mdfSecondSide);
|
||||
this.isModalDoubleFacesCard = true;
|
||||
}
|
||||
|
||||
if (this.isPlaneswalker()) {
|
||||
String startingLoyaltyString = card.getStartingLoyalty();
|
||||
if (startingLoyaltyString.isEmpty()) {
|
||||
//Logger.getLogger(MockCard.class).warn("Planeswalker `" + this.name + "` has empty starting loyalty.");
|
||||
} else {
|
||||
try {
|
||||
this.startingLoyalty = Integer.parseInt(startingLoyaltyString);
|
||||
|
|
@ -117,8 +125,14 @@ public class MockCard extends CardImpl {
|
|||
}
|
||||
|
||||
public String getFullName(boolean showSecondName) {
|
||||
if (!showSecondName) {
|
||||
return getName();
|
||||
}
|
||||
|
||||
if (adventureSpellName != null) {
|
||||
return getName() + ADVENTURE_NAME_SEPARATOR + adventureSpellName;
|
||||
} else if (isModalDoubleFacesCard) {
|
||||
return getName() + MODAL_DOUBLE_FACES_NAME_SEPARATOR + this.secondSideCard.getName();
|
||||
} else {
|
||||
return getName();
|
||||
}
|
||||
|
|
@ -145,4 +159,10 @@ public class MockCard extends CardImpl {
|
|||
private Ability textAbilityFromString(final String text) {
|
||||
return new MockAbility(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransformable() {
|
||||
// must enable toggle mode in deck editor (switch between card sides);
|
||||
return super.isTransformable() || this.isModalDoubleFacesCard;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ public class MockSplitCard extends SplitCard {
|
|||
this.usesVariousArt = card.usesVariousArt();
|
||||
|
||||
this.color = card.getColor();
|
||||
this.splitCard = card.isSplitCard();
|
||||
this.flipCard = card.isFlipCard();
|
||||
|
||||
this.transformable = card.isDoubleFaced();
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ package mage.cards.repository;
|
|||
import com.j256.ormlite.field.DataType;
|
||||
import com.j256.ormlite.field.DatabaseField;
|
||||
import com.j256.ormlite.table.DatabaseTable;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
|
|
@ -17,6 +15,9 @@ import mage.util.CardUtil;
|
|||
import mage.util.SubTypeList;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author North
|
||||
*/
|
||||
|
|
@ -103,6 +104,12 @@ public class CardInfo {
|
|||
protected boolean adventureCard;
|
||||
@DatabaseField
|
||||
protected String adventureSpellName;
|
||||
@DatabaseField
|
||||
protected boolean modalDoubleFacesCard;
|
||||
@DatabaseField
|
||||
protected String modalDoubleFacesSecondSideName;
|
||||
|
||||
// if you add new field with card side name then update CardRepository.addNewNames too
|
||||
|
||||
public enum ManaCostSide {
|
||||
LEFT, RIGHT, ALL
|
||||
|
|
@ -121,7 +128,7 @@ public class CardInfo {
|
|||
this.toughness = card.getToughness().toString();
|
||||
this.convertedManaCost = card.getConvertedManaCost();
|
||||
this.rarity = card.getRarity();
|
||||
this.splitCard = card.isSplitCard();
|
||||
this.splitCard = card instanceof SplitCard;
|
||||
this.splitCardFuse = card.getSpellAbility() != null && card.getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED;
|
||||
this.splitCardAftermath = card.getSpellAbility() != null && card.getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_AFTERMATH;
|
||||
|
||||
|
|
@ -140,6 +147,11 @@ public class CardInfo {
|
|||
this.adventureSpellName = ((AdventureCard) card).getSpellCard().getName();
|
||||
}
|
||||
|
||||
if (card instanceof ModalDoubleFacesCard) {
|
||||
this.modalDoubleFacesCard = true;
|
||||
this.modalDoubleFacesSecondSideName = ((ModalDoubleFacesCard) card).getRightHalfCard().getName();
|
||||
}
|
||||
|
||||
this.frameStyle = card.getFrameStyle().toString();
|
||||
this.frameColor = card.getFrameColor(null).toString();
|
||||
this.variousArt = card.getUsesVariousArt();
|
||||
|
|
@ -153,13 +165,17 @@ public class CardInfo {
|
|||
this.setSubtypes(card.getSubtype(null).stream().map(SubType::toString).collect(Collectors.toList()));
|
||||
this.setSuperTypes(card.getSuperType());
|
||||
|
||||
// mana cost can contains multiple cards (split left/right, card/adventure)
|
||||
// mana cost can contains multiple cards (split left/right, modal double faces, card/adventure)
|
||||
if (card instanceof SplitCard) {
|
||||
List<String> manaCostLeft = ((SplitCard) card).getLeftHalfCard().getManaCost().getSymbols();
|
||||
List<String> manaCostRight = ((SplitCard) card).getRightHalfCard().getManaCost().getSymbols();
|
||||
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
List<String> manaCostLeft = ((ModalDoubleFacesCard) card).getLeftHalfCard().getManaCost().getSymbols();
|
||||
List<String> manaCostRight = ((ModalDoubleFacesCard) card).getRightHalfCard().getManaCost().getSymbols();
|
||||
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
|
||||
} else if (card instanceof AdventureCard) {
|
||||
List<String> manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCost().getSymbols(); // Spell from left like MTGA
|
||||
List<String> manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCost().getSymbols();
|
||||
List<String> manaCostRight = card.getManaCost().getSymbols();
|
||||
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
|
||||
} else {
|
||||
|
|
@ -181,6 +197,12 @@ public class CardInfo {
|
|||
length += rule.length();
|
||||
rulesList.add(rule);
|
||||
}
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
// mdf card return main side's rules only (GUI can toggle it to another side)
|
||||
for (String rule : card.getRules()) {
|
||||
length += rule.length();
|
||||
rulesList.add(rule);
|
||||
}
|
||||
} else {
|
||||
for (String rule : card.getRules()) {
|
||||
length += rule.length();
|
||||
|
|
@ -222,7 +244,6 @@ public class CardInfo {
|
|||
}
|
||||
}
|
||||
if (this.startingLoyalty == null) {
|
||||
//Logger.getLogger(CardInfo.class).warn("Planeswalker `" + card.getName() + "` missing starting loyalty");
|
||||
this.startingLoyalty = "";
|
||||
}
|
||||
} else {
|
||||
|
|
@ -447,4 +468,12 @@ public class CardInfo {
|
|||
public String getAdventureSpellName() {
|
||||
return adventureSpellName;
|
||||
}
|
||||
|
||||
public boolean isModalDoubleFacesCard() {
|
||||
return modalDoubleFacesCard;
|
||||
}
|
||||
|
||||
public String getModalDoubleFacesSecondSideName() {
|
||||
return modalDoubleFacesSecondSideName;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ public enum CardRepository {
|
|||
private static final String JDBC_URL = "jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE";
|
||||
private static final String VERSION_ENTITY_NAME = "card";
|
||||
// raise this if db structure was changed
|
||||
private static final long CARD_DB_VERSION = 52;
|
||||
private static final long CARD_DB_VERSION = 53;
|
||||
// raise this if new cards were added to the server
|
||||
private static final long CARD_CONTENT_VERSION = 232;
|
||||
private static final long CARD_CONTENT_VERSION = 233;
|
||||
private Dao<CardInfo, Object> cardDao;
|
||||
private Set<String> classNames;
|
||||
private final RepositoryEventSource eventSource = new RepositoryEventSource();
|
||||
|
|
@ -136,16 +136,10 @@ public enum CardRepository {
|
|||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name");
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName");
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
int result = card.getName().indexOf(" // ");
|
||||
if (result > 0) {
|
||||
names.add(card.getName().substring(0, result));
|
||||
names.add(card.getName().substring(result + 4));
|
||||
} else {
|
||||
names.add(card.getName());
|
||||
}
|
||||
addNewNames(card, names);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CardRepository.class).error("Error getting names from DB : " + ex);
|
||||
|
|
@ -153,21 +147,39 @@ public enum CardRepository {
|
|||
return names;
|
||||
}
|
||||
|
||||
private void addNewNames(CardInfo card, Set<String> namesList) {
|
||||
// require before call: qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName"...);
|
||||
|
||||
// normal names
|
||||
int result = card.getName().indexOf(" // ");
|
||||
if (result > 0) {
|
||||
namesList.add(card.getName().substring(0, result));
|
||||
namesList.add(card.getName().substring(result + 4));
|
||||
} else {
|
||||
namesList.add(card.getName());
|
||||
}
|
||||
|
||||
// additional names from double side cards
|
||||
if (card.getSecondSideName() != null && !card.getSecondSideName().isEmpty()) {
|
||||
namesList.add(card.getSecondSideName());
|
||||
}
|
||||
if (card.getModalDoubleFacesSecondSideName() != null && !card.getModalDoubleFacesSecondSideName().isEmpty()) {
|
||||
namesList.add(card.getModalDoubleFacesSecondSideName());
|
||||
}
|
||||
if (card.getFlipCardName() != null && !card.getFlipCardName().isEmpty()) {
|
||||
namesList.add(card.getFlipCardName());
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getNonLandNames() {
|
||||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name");
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName");
|
||||
qb.where().not().like("types", new SelectArg('%' + CardType.LAND.name() + '%'));
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
int result = card.getName().indexOf(" // ");
|
||||
if (result > 0) {
|
||||
names.add(card.getName().substring(0, result));
|
||||
names.add(card.getName().substring(result + 4));
|
||||
} else {
|
||||
names.add(card.getName());
|
||||
}
|
||||
addNewNames(card, names);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CardRepository.class).error("Error getting non-land names from DB : " + ex);
|
||||
|
|
@ -188,7 +200,7 @@ public enum CardRepository {
|
|||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name");
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName");
|
||||
Where where = qb.where();
|
||||
where.and(
|
||||
where.not().like("supertypes", '%' + SuperType.BASIC.name() + '%'),
|
||||
|
|
@ -196,13 +208,7 @@ public enum CardRepository {
|
|||
);
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
int result = card.getName().indexOf(" // ");
|
||||
if (result > 0) {
|
||||
names.add(card.getName().substring(0, result));
|
||||
names.add(card.getName().substring(result + 4));
|
||||
} else {
|
||||
names.add(card.getName());
|
||||
}
|
||||
addNewNames(card, names);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CardRepository.class).error("Error getting non-land names from DB : " + ex);
|
||||
|
|
@ -215,17 +221,11 @@ public enum CardRepository {
|
|||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name");
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName");
|
||||
qb.where().not().like("supertypes", new SelectArg('%' + SuperType.BASIC.name() + '%'));
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
int result = card.getName().indexOf(" // ");
|
||||
if (result > 0) {
|
||||
names.add(card.getName().substring(0, result));
|
||||
names.add(card.getName().substring(result + 4));
|
||||
} else {
|
||||
names.add(card.getName());
|
||||
}
|
||||
addNewNames(card, names);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CardRepository.class).error("Error getting non-land names from DB : " + ex);
|
||||
|
|
@ -238,17 +238,11 @@ public enum CardRepository {
|
|||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name");
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName");
|
||||
qb.where().like("types", new SelectArg('%' + CardType.CREATURE.name() + '%'));
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
int result = card.getName().indexOf(" // ");
|
||||
if (result > 0) {
|
||||
names.add(card.getName().substring(0, result));
|
||||
names.add(card.getName().substring(result + 4));
|
||||
} else {
|
||||
names.add(card.getName());
|
||||
}
|
||||
addNewNames(card, names);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CardRepository.class).error("Error getting creature names from DB : " + ex);
|
||||
|
|
@ -261,17 +255,11 @@ public enum CardRepository {
|
|||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name");
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName");
|
||||
qb.where().like("types", new SelectArg('%' + CardType.ARTIFACT.name() + '%'));
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
int result = card.getName().indexOf(" // ");
|
||||
if (result > 0) {
|
||||
names.add(card.getName().substring(0, result));
|
||||
names.add(card.getName().substring(result + 4));
|
||||
} else {
|
||||
names.add(card.getName());
|
||||
}
|
||||
addNewNames(card, names);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CardRepository.class).error("Error getting artifact names from DB : " + ex);
|
||||
|
|
@ -284,7 +272,7 @@ public enum CardRepository {
|
|||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name");
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName");
|
||||
Where where = qb.where();
|
||||
where.and(
|
||||
where.not().like("types", '%' + CardType.CREATURE.name() + '%'),
|
||||
|
|
@ -292,13 +280,7 @@ public enum CardRepository {
|
|||
);
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
int result = card.getName().indexOf(" // ");
|
||||
if (result > 0) {
|
||||
names.add(card.getName().substring(0, result));
|
||||
names.add(card.getName().substring(result + 4));
|
||||
} else {
|
||||
names.add(card.getName());
|
||||
}
|
||||
addNewNames(card, names);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CardRepository.class).error("Error getting non-land and non-creature names from DB : " + ex);
|
||||
|
|
@ -310,7 +292,7 @@ public enum CardRepository {
|
|||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name");
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacesSecondSideName", "secondSideName", "flipCardName");
|
||||
Where where = qb.where();
|
||||
where.and(
|
||||
where.not().like("types", '%' + CardType.ARTIFACT.name() + '%'),
|
||||
|
|
@ -318,13 +300,7 @@ public enum CardRepository {
|
|||
);
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
int result = card.getName().indexOf(" // ");
|
||||
if (result > 0) {
|
||||
names.add(card.getName().substring(0, result));
|
||||
names.add(card.getName().substring(result + 4));
|
||||
} else {
|
||||
names.add(card.getName());
|
||||
}
|
||||
addNewNames(card, names);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CardRepository.class).error("Error getting non-artifact non-land names from DB : " + ex);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public final class CardScanner {
|
|||
new CardSetInfo(setInfo.getName(), set.getCode(), setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo()),
|
||||
errorsList);
|
||||
if (card != null) {
|
||||
cardsToAdd.add(new CardInfo(card));
|
||||
cardsToAdd.add(new CardInfo(card)); // normal, transformed, adventure, modal double faces -- all must have single face in db
|
||||
if (card instanceof SplitCard) {
|
||||
SplitCard splitCard = (SplitCard) card;
|
||||
cardsToAdd.add(new CardInfo(splitCard.getLeftHalfCard()));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package mage.constants;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public enum SpellAbilityType {
|
||||
|
|
@ -13,7 +12,9 @@ public enum SpellAbilityType {
|
|||
SPLIT_FUSED("Split SpellAbility"),
|
||||
SPLIT_LEFT("LeftSplit SpellAbility"),
|
||||
SPLIT_RIGHT("RightSplit SpellAbility"),
|
||||
MODE("Mode SpellAbility"),
|
||||
MODAL("Modal SpellAbility"), // used for modal double faces cards
|
||||
MODAL_LEFT("LeftModal SpellAbility"),
|
||||
MODAL_RIGHT("RightModal SpellAbility"),
|
||||
SPLICE("Spliced SpellAbility"),
|
||||
ADVENTURE_SPELL("Adventure SpellAbility");
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,8 @@ package mage.constants;
|
|||
import mage.MageObject;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
import mage.util.SubTypeList;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public enum SubType {
|
||||
|
|
@ -468,6 +465,22 @@ public enum SubType {
|
|||
}
|
||||
}
|
||||
|
||||
private static final Set<SubType> landTypes = new HashSet<>();
|
||||
private static final Map<SubTypeSet, Set<SubType>> subTypeSetMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (SubTypeSet subTypeSet : SubTypeSet.values()) {
|
||||
subTypeSetMap.put(
|
||||
subTypeSet,
|
||||
Arrays.stream(values())
|
||||
.filter(subType -> subType.getSubTypeSet() == subTypeSet)
|
||||
.collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
landTypes.addAll(subTypeSetMap.get(SubTypeSet.BasicLandType));
|
||||
landTypes.addAll(subTypeSetMap.get(SubTypeSet.NonBasicLandType));
|
||||
}
|
||||
|
||||
private final SubTypeSet subTypeSet;
|
||||
private final String description;
|
||||
private final boolean customSet;
|
||||
|
|
@ -534,49 +547,46 @@ public enum SubType {
|
|||
return subTypeSet;
|
||||
}
|
||||
|
||||
public static Set<SubType> getArtifactTypes() {
|
||||
Set<SubType> subTypes = EnumSet.noneOf(SubType.class);
|
||||
for (SubType subType : values()) {
|
||||
if (subType.getSubTypeSet() == SubTypeSet.ArtifactType) {
|
||||
subTypes.add(subType);
|
||||
}
|
||||
public boolean canGain(MageObject mageObject) {
|
||||
switch (subTypeSet) {
|
||||
case CreatureType:
|
||||
return mageObject.isCreature() || mageObject.isTribal();
|
||||
case BasicLandType:
|
||||
case NonBasicLandType:
|
||||
return mageObject.isLand();
|
||||
case EnchantmentType:
|
||||
return mageObject.isEnchantment();
|
||||
case ArtifactType:
|
||||
return mageObject.isArtifact();
|
||||
case PlaneswalkerType:
|
||||
return mageObject.isPlaneswalker();
|
||||
}
|
||||
return subTypes;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Set<SubType> getArtifactTypes() {
|
||||
return subTypeSetMap.get(SubTypeSet.ArtifactType);
|
||||
}
|
||||
|
||||
public static Set<SubType> getEnchantmentTypes() {
|
||||
return subTypeSetMap.get(SubTypeSet.EnchantmentType);
|
||||
}
|
||||
|
||||
public static Set<SubType> getPlaneswalkerTypes() {
|
||||
Set<SubType> subTypes = EnumSet.noneOf(SubType.class);
|
||||
for (SubType subType : values()) {
|
||||
if (subType.getSubTypeSet() == SubTypeSet.PlaneswalkerType) {
|
||||
subTypes.add(subType);
|
||||
}
|
||||
}
|
||||
return subTypes;
|
||||
return subTypeSetMap.get(SubTypeSet.PlaneswalkerType);
|
||||
|
||||
}
|
||||
|
||||
public static Set<SubType> getCreatureTypes() {
|
||||
Set<SubType> subTypes = EnumSet.noneOf(SubType.class);
|
||||
for (SubType subType : values()) {
|
||||
if (subType.getSubTypeSet() == SubTypeSet.CreatureType) {
|
||||
subTypes.add(subType);
|
||||
}
|
||||
}
|
||||
return subTypes;
|
||||
return subTypeSetMap.get(SubTypeSet.CreatureType);
|
||||
|
||||
}
|
||||
|
||||
public static Set<SubType> getBasicLands() {
|
||||
return Arrays.stream(values())
|
||||
.filter(p -> p.getSubTypeSet() == SubTypeSet.BasicLandType)
|
||||
.collect(Collectors.toSet());
|
||||
return subTypeSetMap.get(SubTypeSet.BasicLandType);
|
||||
}
|
||||
|
||||
public static SubTypeList getLandTypes() {
|
||||
SubTypeList landTypes = new SubTypeList();
|
||||
for (SubType subType : values()) {
|
||||
if (subType.getSubTypeSet() == SubTypeSet.BasicLandType || subType.getSubTypeSet() == SubTypeSet.NonBasicLandType) {
|
||||
landTypes.add(subType);
|
||||
}
|
||||
}
|
||||
public static Set<SubType> getLandTypes() {
|
||||
return landTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import mage.game.Game;
|
|||
*/
|
||||
public enum CounterType {
|
||||
|
||||
AEGIS("aegis"),
|
||||
AGE("age"),
|
||||
AIM("aim"),
|
||||
ARROW("arrow"),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
package mage.filter.predicate.mageobject;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.cards.ModalDoubleFacesCardHalf;
|
||||
import mage.cards.SplitCardHalf;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.predicate.Predicate;
|
||||
|
|
@ -18,8 +18,13 @@ public enum MulticoloredPredicate implements Predicate<MageObject> {
|
|||
// 708.3. Each split card that consists of two halves with different colored mana symbols in their mana costs
|
||||
// is a multicolored card while it's not a spell on the stack. While it's a spell on the stack, it's only the
|
||||
// color or colors of the half or halves being cast. #
|
||||
if (input instanceof SplitCardHalf && game.getState().getZone(input.getId()) != Zone.STACK) {
|
||||
if (input instanceof SplitCardHalf
|
||||
&& game.getState().getZone(input.getId()) != Zone.STACK) {
|
||||
return 1 < ((SplitCardHalf) input).getMainCard().getColor(game).getColorCount();
|
||||
} else if (input instanceof ModalDoubleFacesCardHalf
|
||||
&& (game.getState().getZone(input.getId()) != Zone.STACK && game.getState().getZone(input.getId()) != Zone.BATTLEFIELD)) {
|
||||
// While a double-faced card isn’t on the stack or battlefield, consider only the characteristics of its front face.
|
||||
return 1 < ((ModalDoubleFacesCardHalf) input).getMainCard().getColor(game).getColorCount();
|
||||
} else {
|
||||
return 1 < input.getColor(game).getColorCount();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package mage.filter.predicate.mageobject;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.filter.predicate.Predicate;
|
||||
|
|
@ -32,10 +33,15 @@ public class NamePredicate implements Predicate<MageObject> {
|
|||
}
|
||||
// If a player names a card, the player may name either half of a split card, but not both.
|
||||
// A split card has the chosen name if one of its two names matches the chosen name.
|
||||
// Same for modal double faces cards
|
||||
if (input instanceof SplitCard) {
|
||||
return CardUtil.haveSameNames(name, ((SplitCard) input).getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
|
||||
CardUtil.haveSameNames(name, ((SplitCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
|
||||
CardUtil.haveSameNames(name, input.getName(), this.ignoreMtgRuleForEmptyNames);
|
||||
} else if (input instanceof ModalDoubleFacesCard) {
|
||||
return CardUtil.haveSameNames(name, ((ModalDoubleFacesCard) input).getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
|
||||
CardUtil.haveSameNames(name, ((ModalDoubleFacesCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
|
||||
CardUtil.haveSameNames(name, input.getName(), this.ignoreMtgRuleForEmptyNames);
|
||||
} else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) {
|
||||
SplitCard card = (SplitCard) ((Spell) input).getCard();
|
||||
return CardUtil.haveSameNames(name, card.getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package mage.filter.predicate.other;
|
|||
|
||||
import mage.cards.AdventureCard;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.cards.mock.MockCard;
|
||||
import mage.constants.SubType;
|
||||
|
|
@ -52,6 +53,8 @@ public class CardTextPredicate implements Predicate<Card> {
|
|||
String fullName = input.getName();
|
||||
if (input instanceof MockCard) {
|
||||
fullName = ((MockCard) input).getFullName(true);
|
||||
} else if (input instanceof ModalDoubleFacesCard) {
|
||||
fullName = input.getName() + MockCard.MODAL_DOUBLE_FACES_NAME_SEPARATOR + ((ModalDoubleFacesCard) input).getRightHalfCard().getName();
|
||||
} else if (input instanceof AdventureCard) {
|
||||
fullName = input.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + ((AdventureCard) input).getSpellCard().getName();
|
||||
}
|
||||
|
|
@ -67,14 +70,14 @@ public class CardTextPredicate implements Predicate<Card> {
|
|||
}
|
||||
}
|
||||
|
||||
//separate by spaces
|
||||
// separate by spaces
|
||||
String[] tokens = text.toLowerCase(Locale.ENGLISH).split(" ");
|
||||
for (String token : tokens) {
|
||||
boolean found = false;
|
||||
if (!token.isEmpty()) {
|
||||
// then try to find in rules
|
||||
if (inRules) {
|
||||
if (input.isSplitCard()) {
|
||||
if (input instanceof SplitCard) {
|
||||
for (String rule : ((SplitCard) input).getLeftHalfCard().getRules(game)) {
|
||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
||||
found = true;
|
||||
|
|
@ -88,6 +91,22 @@ public class CardTextPredicate implements Predicate<Card> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (input instanceof ModalDoubleFacesCard) {
|
||||
for (String rule : ((ModalDoubleFacesCard) input).getLeftHalfCard().getRules(game)) {
|
||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (String rule : ((ModalDoubleFacesCard) input).getRightHalfCard().getRules(game)) {
|
||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (input instanceof AdventureCard) {
|
||||
for (String rule : ((AdventureCard) input).getSpellCard().getRules(game)) {
|
||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
||||
|
|
@ -96,6 +115,7 @@ public class CardTextPredicate implements Predicate<Card> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (String rule : input.getRules(game)) {
|
||||
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
|
||||
found = true;
|
||||
|
|
|
|||
|
|
@ -1,21 +1,20 @@
|
|||
package mage.filter.predicate.permanent;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
public enum CommanderPredicate implements Predicate<Permanent> {
|
||||
public enum CommanderPredicate implements Predicate<MageObject> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
Player owner = game.getPlayer(input.getOwnerId());
|
||||
return owner != null
|
||||
&& game.getCommandersIds(owner).contains(input.getId());
|
||||
public boolean apply(MageObject input, Game game) {
|
||||
Player owner = game.getPlayer(game.getOwnerId(input.getId()));
|
||||
return owner != null && game.getCommandersIds(owner).contains(input.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package mage.filter.predicate.permanent;
|
||||
|
||||
import mage.filter.predicate.Predicate;
|
||||
|
|
@ -10,10 +5,10 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class EnteredThisTurnPredicate implements Predicate<Permanent> {
|
||||
public enum EnteredThisTurnPredicate implements Predicate<Permanent> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
|
|
|
|||
|
|
@ -244,17 +244,29 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
card.setOwnerId(ownerId);
|
||||
gameCards.put(card.getId(), card);
|
||||
state.addCard(card);
|
||||
if (card.isSplitCard()) {
|
||||
if (card instanceof SplitCard) {
|
||||
// left
|
||||
Card leftCard = ((SplitCard) card).getLeftHalfCard();
|
||||
leftCard.setOwnerId(ownerId);
|
||||
gameCards.put(leftCard.getId(), leftCard);
|
||||
state.addCard(leftCard);
|
||||
// right
|
||||
Card rightCard = ((SplitCard) card).getRightHalfCard();
|
||||
rightCard.setOwnerId(ownerId);
|
||||
gameCards.put(rightCard.getId(), rightCard);
|
||||
state.addCard(rightCard);
|
||||
}
|
||||
if (card instanceof AdventureCard) {
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
// left
|
||||
Card leftCard = ((ModalDoubleFacesCard) card).getLeftHalfCard();
|
||||
leftCard.setOwnerId(ownerId);
|
||||
gameCards.put(leftCard.getId(), leftCard);
|
||||
state.addCard(leftCard);
|
||||
// right
|
||||
Card rightCard = ((ModalDoubleFacesCard) card).getRightHalfCard();
|
||||
rightCard.setOwnerId(ownerId);
|
||||
gameCards.put(rightCard.getId(), rightCard);
|
||||
state.addCard(rightCard);
|
||||
} else if (card instanceof AdventureCard) {
|
||||
Card spellCard = ((AdventureCard) card).getSpellCard();
|
||||
spellCard.setOwnerId(ownerId);
|
||||
gameCards.put(spellCard.getId(), spellCard);
|
||||
|
|
@ -1911,7 +1923,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
Iterator<Card> copiedCards = this.getState().getCopiedCards().iterator();
|
||||
while (copiedCards.hasNext()) {
|
||||
Card card = copiedCards.next();
|
||||
if (card instanceof SplitCardHalf || card instanceof AdventureCardSpell) {
|
||||
if (card instanceof SplitCardHalf || card instanceof AdventureCardSpell || card instanceof ModalDoubleFacesCardHalf) {
|
||||
continue; // only the main card is moves, not the halves (cause halfes is not copied - it uses original card -- TODO: need to fix (bugs with same card copy)?
|
||||
}
|
||||
Zone zone = state.getZone(card.getId());
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
package mage.game;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import static java.util.Collections.emptyList;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.*;
|
||||
|
|
@ -12,6 +8,7 @@ import mage.abilities.effects.ContinuousEffects;
|
|||
import mage.abilities.effects.Effect;
|
||||
import mage.cards.AdventureCard;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.Zone;
|
||||
import mage.designations.Designation;
|
||||
|
|
@ -40,6 +37,12 @@ import mage.util.ThreadLocalStringBuilder;
|
|||
import mage.watchers.Watcher;
|
||||
import mage.watchers.Watchers;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
* <p>
|
||||
|
|
@ -626,6 +629,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
// public void addMessage(String message) {
|
||||
// this.messages.add(message);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Returns a list of all players of the game ignoring range or if a player
|
||||
* has lost or left the game.
|
||||
|
|
@ -799,7 +803,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
for (Map.Entry<ZoneChangeData, List<GameEvent>> entry : eventsByKey.entrySet()) {
|
||||
Set<Card> movedCards = new LinkedHashSet<>();
|
||||
Set<PermanentToken> movedTokens = new LinkedHashSet<>();
|
||||
for (Iterator<GameEvent> it = entry.getValue().iterator(); it.hasNext();) {
|
||||
for (Iterator<GameEvent> it = entry.getValue().iterator(); it.hasNext(); ) {
|
||||
GameEvent event = it.next();
|
||||
ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
|
||||
UUID targetId = castEvent.getTargetId();
|
||||
|
|
@ -835,10 +839,14 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
// TODO Watchers?
|
||||
// TODO Abilities?
|
||||
if (card.isSplitCard()) {
|
||||
if (card instanceof SplitCard) {
|
||||
removeCopiedCard(((SplitCard) card).getLeftHalfCard());
|
||||
removeCopiedCard(((SplitCard) card).getRightHalfCard());
|
||||
}
|
||||
if (card instanceof ModalDoubleFacesCard) {
|
||||
removeCopiedCard(((ModalDoubleFacesCard) card).getLeftHalfCard());
|
||||
removeCopiedCard(((ModalDoubleFacesCard) card).getRightHalfCard());
|
||||
}
|
||||
if (card instanceof AdventureCard) {
|
||||
removeCopiedCard(((AdventureCard) card).getSpellCard());
|
||||
}
|
||||
|
|
@ -1194,21 +1202,34 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
|
||||
public Card copyCard(Card cardToCopy, Ability source, Game game) {
|
||||
// main card
|
||||
Card copiedCard = cardToCopy.copy();
|
||||
copiedCard.assignNewId();
|
||||
copiedCard.setOwnerId(source.getControllerId());
|
||||
copiedCard.setCopy(true, cardToCopy);
|
||||
copiedCards.put(copiedCard.getId(), copiedCard);
|
||||
addCard(copiedCard);
|
||||
if (copiedCard.isSplitCard()) {
|
||||
|
||||
// other faces
|
||||
if (copiedCard instanceof SplitCard) {
|
||||
// left
|
||||
Card leftCard = ((SplitCard) copiedCard).getLeftHalfCard(); // TODO: must be new ID (bugs with same card copy)?
|
||||
copiedCards.put(leftCard.getId(), leftCard);
|
||||
addCard(leftCard);
|
||||
// right
|
||||
Card rightCard = ((SplitCard) copiedCard).getRightHalfCard();
|
||||
copiedCards.put(rightCard.getId(), rightCard);
|
||||
addCard(rightCard);
|
||||
}
|
||||
if (copiedCard instanceof AdventureCard) {
|
||||
} else if (copiedCard instanceof ModalDoubleFacesCard) {
|
||||
// left
|
||||
Card leftCard = ((ModalDoubleFacesCard) copiedCard).getLeftHalfCard(); // TODO: must be new ID (bugs with same card copy)?
|
||||
copiedCards.put(leftCard.getId(), leftCard);
|
||||
addCard(leftCard);
|
||||
// right
|
||||
Card rightCard = ((ModalDoubleFacesCard) copiedCard).getRightHalfCard();
|
||||
copiedCards.put(rightCard.getId(), rightCard);
|
||||
addCard(rightCard);
|
||||
} else if (copiedCard instanceof AdventureCard) {
|
||||
Card spellCard = ((AdventureCard) copiedCard).getSpellCard();
|
||||
copiedCards.put(spellCard.getId(), spellCard);
|
||||
addCard(spellCard);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
package mage.game;
|
||||
|
||||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.MeldCard;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.*;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
|
|
@ -19,7 +17,6 @@ import mage.players.Player;
|
|||
import mage.target.TargetCard;
|
||||
|
||||
import java.util.*;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
|
||||
/**
|
||||
* Created by samuelsandeen on 9/6/16.
|
||||
|
|
@ -54,7 +51,7 @@ public final class ZonesHandler {
|
|||
|
||||
public static List<ZoneChangeInfo> moveCards(List<ZoneChangeInfo> zoneChangeInfos, Game game) {
|
||||
// Handle Unmelded Meld Cards
|
||||
for (ListIterator<ZoneChangeInfo> itr = zoneChangeInfos.listIterator(); itr.hasNext();) {
|
||||
for (ListIterator<ZoneChangeInfo> itr = zoneChangeInfos.listIterator(); itr.hasNext(); ) {
|
||||
ZoneChangeInfo info = itr.next();
|
||||
MeldCard card = game.getMeldCard(info.event.getTargetId());
|
||||
// Copies should be handled as normal cards.
|
||||
|
|
@ -101,42 +98,57 @@ public final class ZonesHandler {
|
|||
ZoneChangeEvent event = info.event;
|
||||
Zone toZone = event.getToZone();
|
||||
Card targetCard = getTargetCard(game, event.getTargetId());
|
||||
Cards cards = null;
|
||||
// If we're moving a token it shouldn't be put into any zone as an object.
|
||||
|
||||
Cards cardsToMove = null; // moving real cards
|
||||
Cards cardsToUpdate = null; // updating all card's parts
|
||||
// if we're moving a token it shouldn't be put into any zone as an object.
|
||||
if (!(targetCard instanceof Permanent) && targetCard != null) {
|
||||
if (targetCard instanceof MeldCard) {
|
||||
cards = ((MeldCard) targetCard).getHalves();
|
||||
// meld/group cards must be independent (use can choose order)
|
||||
cardsToMove = ((MeldCard) targetCard).getHalves();
|
||||
cardsToUpdate = cardsToMove;
|
||||
} else if (targetCard instanceof ModalDoubleFacesCard
|
||||
|| targetCard instanceof ModalDoubleFacesCardHalf) {
|
||||
// mdf cards must be moved as single object, but each half must be updated separetly
|
||||
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) targetCard.getMainCard();
|
||||
cardsToMove = new CardsImpl(mdfCard);
|
||||
cardsToUpdate = new CardsImpl(mdfCard);
|
||||
cardsToUpdate.add(mdfCard.getLeftHalfCard());
|
||||
cardsToUpdate.add(mdfCard.getRightHalfCard());
|
||||
} else {
|
||||
cards = new CardsImpl(targetCard);
|
||||
cardsToMove = new CardsImpl(targetCard);
|
||||
cardsToUpdate = cardsToMove;
|
||||
}
|
||||
Player owner = game.getPlayer(targetCard.getOwnerId());
|
||||
switch (toZone) {
|
||||
case HAND:
|
||||
for (Card card : cards.getCards(game)) {
|
||||
for (Card card : cardsToMove.getCards(game)) {
|
||||
game.getPlayer(card.getOwnerId()).getHand().add(card);
|
||||
}
|
||||
break;
|
||||
case GRAVEYARD:
|
||||
for (Card card : chooseOrder(
|
||||
"order to put in graveyard (last chosen will be on top)", cards, owner, game)) {
|
||||
"order to put in graveyard (last chosen will be on top)", cardsToMove, owner, game)) {
|
||||
game.getPlayer(card.getOwnerId()).getGraveyard().add(card);
|
||||
}
|
||||
break;
|
||||
case LIBRARY:
|
||||
if (info instanceof ZoneChangeInfo.Library && ((ZoneChangeInfo.Library) info).top) {
|
||||
// on top
|
||||
for (Card card : chooseOrder(
|
||||
"order to put on top of library (last chosen will be topmost)", cards, owner, game)) {
|
||||
"order to put on top of library (last chosen will be topmost)", cardsToMove, owner, game)) {
|
||||
game.getPlayer(card.getOwnerId()).getLibrary().putOnTop(card, game);
|
||||
}
|
||||
} else { // buttom
|
||||
} else {
|
||||
// on bottom
|
||||
for (Card card : chooseOrder(
|
||||
"order to put on bottom of library (last chosen will be bottommost)", cards, owner, game)) {
|
||||
"order to put on bottom of library (last chosen will be bottommost)", cardsToMove, owner, game)) {
|
||||
game.getPlayer(card.getOwnerId()).getLibrary().putOnBottom(card, game);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXILED:
|
||||
for (Card card : cards.getCards(game)) {
|
||||
for (Card card : cardsToMove.getCards(game)) {
|
||||
if (info instanceof ZoneChangeInfo.Exile && ((ZoneChangeInfo.Exile) info).id != null) {
|
||||
ZoneChangeInfo.Exile exileInfo = (ZoneChangeInfo.Exile) info;
|
||||
game.getExile().createZone(exileInfo.id, exileInfo.name).add(card);
|
||||
|
|
@ -147,13 +159,13 @@ public final class ZonesHandler {
|
|||
break;
|
||||
case COMMAND:
|
||||
// There should never be more than one card here.
|
||||
for (Card card : cards.getCards(game)) {
|
||||
for (Card card : cardsToMove.getCards(game)) {
|
||||
game.addCommander(new Commander(card));
|
||||
}
|
||||
break;
|
||||
case STACK:
|
||||
// There should never be more than one card here.
|
||||
for (Card card : cards.getCards(game)) {
|
||||
for (Card card : cardsToMove.getCards(game)) {
|
||||
Spell spell;
|
||||
if (info instanceof ZoneChangeInfo.Stack && ((ZoneChangeInfo.Stack) info).spell != null) {
|
||||
spell = ((ZoneChangeInfo.Stack) info).spell;
|
||||
|
|
@ -174,28 +186,39 @@ public final class ZonesHandler {
|
|||
throw new UnsupportedOperationException("to Zone " + toZone.toString() + " not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
// update zone in main
|
||||
game.setZone(event.getTargetId(), event.getToZone());
|
||||
if (targetCard instanceof MeldCard && cards != null) {
|
||||
|
||||
// update zone in other parts (meld cards, mdf half cards)
|
||||
if (cardsToUpdate != null) {
|
||||
for (Card card : cardsToUpdate.getCards(game)) {
|
||||
if (!card.getId().equals(event.getTargetId())) {
|
||||
game.setZone(card.getId(), event.getToZone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset meld status
|
||||
if (targetCard instanceof MeldCard) {
|
||||
if (event.getToZone() != Zone.BATTLEFIELD) {
|
||||
((MeldCard) targetCard).setMelded(false, game);
|
||||
}
|
||||
for (Card card : cards.getCards(game)) {
|
||||
game.setZone(card.getId(), event.getToZone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Card getTargetCard(Game game, UUID targetId) {
|
||||
if (game.getCard(targetId) != null) {
|
||||
return game.getCard(targetId);
|
||||
Card card = game.getCard(targetId);
|
||||
if (card != null) {
|
||||
return card;
|
||||
}
|
||||
if (game.getMeldCard(targetId) != null) {
|
||||
return game.getMeldCard(targetId);
|
||||
|
||||
card = game.getMeldCard(targetId);
|
||||
if (card != null) {
|
||||
return card;
|
||||
}
|
||||
if (game.getPermanent(targetId) != null) {
|
||||
return game.getPermanent(targetId);
|
||||
}
|
||||
return null;
|
||||
|
||||
return game.getPermanent(targetId);
|
||||
}
|
||||
|
||||
private static boolean maybeRemoveFromSourceZone(ZoneChangeInfo info, Game game) {
|
||||
|
|
@ -203,7 +226,7 @@ public final class ZonesHandler {
|
|||
if (info instanceof ZoneChangeInfo.Unmelded) {
|
||||
ZoneChangeInfo.Unmelded unmelded = (ZoneChangeInfo.Unmelded) info;
|
||||
MeldCard meld = game.getMeldCard(info.event.getTargetId());
|
||||
for (Iterator<ZoneChangeInfo> itr = unmelded.subInfo.iterator(); itr.hasNext();) {
|
||||
for (Iterator<ZoneChangeInfo> itr = unmelded.subInfo.iterator(); itr.hasNext(); ) {
|
||||
ZoneChangeInfo subInfo = itr.next();
|
||||
if (!maybeRemoveFromSourceZone(subInfo, game)) {
|
||||
itr.remove();
|
||||
|
|
@ -232,10 +255,10 @@ public final class ZonesHandler {
|
|||
if (info.faceDown) {
|
||||
card.setFaceDown(true, game);
|
||||
} else if (info.event.getToZone().equals(Zone.BATTLEFIELD)) {
|
||||
if (!card.isPermanent()
|
||||
if (!card.isPermanent()
|
||||
&& (!card.isTransformable() || Boolean.FALSE.equals(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId())))) {
|
||||
// Non permanents (Instants, Sorceries, ... stay in the zone they are if an abilty/effect tries to move it to the battlefield
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!game.replaceEvent(event)) {
|
||||
|
|
@ -248,9 +271,10 @@ public final class ZonesHandler {
|
|||
Permanent permanent;
|
||||
if (card instanceof MeldCard) {
|
||||
permanent = new PermanentMeld(card, event.getPlayerId(), game);
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
throw new IllegalStateException("Unexpected trying of move mdf card to battlefield instead half");
|
||||
} else if (card instanceof Permanent) {
|
||||
// This should never happen.
|
||||
permanent = (Permanent) card;
|
||||
throw new IllegalStateException("Unexpected trying of move permanent to battlefield instead card");
|
||||
} else {
|
||||
permanent = new PermanentCard(card, event.getPlayerId(), game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ public class GameEvent implements Serializable {
|
|||
CLASH, CLASHED,
|
||||
DAMAGE_PLAYER,
|
||||
MILL_CARDS,
|
||||
MILLED_CARD,
|
||||
/* DAMAGED_PLAYER
|
||||
targetId the id of the damaged player
|
||||
sourceId sourceId of the ability which caused the damage
|
||||
|
|
@ -249,13 +250,17 @@ public class GameEvent implements Serializable {
|
|||
SHUFFLE_LIBRARY, LIBRARY_SHUFFLED,
|
||||
ENCHANT_PLAYER, ENCHANTED_PLAYER,
|
||||
CAN_TAKE_MULLIGAN,
|
||||
FLIP_COIN, COIN_FLIPPED, SCRY, SURVEIL, SURVEILED, FATESEAL,
|
||||
SCRY, SCRIED,
|
||||
SURVEIL, SURVEILED,
|
||||
FATESEALED,
|
||||
FLIP_COIN, COIN_FLIPPED,
|
||||
ROLL_DICE, DICE_ROLLED,
|
||||
ROLL_PLANAR_DIE, PLANAR_DIE_ROLLED,
|
||||
PLANESWALK, PLANESWALKED,
|
||||
PAID_CUMULATIVE_UPKEEP,
|
||||
DIDNT_PAY_CUMULATIVE_UPKEEP,
|
||||
LIFE_PAID,
|
||||
CASCADE_LAND,
|
||||
//permanent events
|
||||
ENTERS_THE_BATTLEFIELD_SELF, /* 616.1a If any of the replacement and/or prevention effects are self-replacement effects (see rule 614.15),
|
||||
one of them must be chosen. If not, proceed to rule 616.1b. */
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
// ability hints
|
||||
List<String> abilityHints = new ArrayList<>();
|
||||
if (HintUtils.ABILITY_HINTS_ENABLE) {
|
||||
for (Ability ability : abilities) {
|
||||
for (Ability ability : getAbilities(game)) {
|
||||
for (Hint hint : ability.getHints()) {
|
||||
String s = hint.getText(game, ability);
|
||||
if (s != null && !s.isEmpty()) {
|
||||
|
|
@ -341,7 +341,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
|
||||
return rules;
|
||||
} catch (Exception e) {
|
||||
return rulesError;
|
||||
return CardUtil.RULES_ERROR_INFO;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
|
||||
|
||||
package mage.game.permanent.token;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.keyword.ChangelingAbility;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public final class CribSwapShapeshifterWhiteToken extends TokenImpl {
|
||||
|
|
@ -19,6 +17,7 @@ public final class CribSwapShapeshifterWhiteToken extends TokenImpl {
|
|||
subtype.add(SubType.SHAPESHIFTER);
|
||||
power = new MageInt(1);
|
||||
toughness = new MageInt(1);
|
||||
setIsAllCreatureTypes(true);
|
||||
addAbility(ChangelingAbility.getInstance());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package mage.game.permanent.token;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class SalamnderWarriorToken extends TokenImpl {
|
||||
|
||||
public SalamnderWarriorToken() {
|
||||
super("Salamander Warrior", "4/3 blue Salamander Warrior creature token");
|
||||
cardType.add(CardType.CREATURE);
|
||||
color.setBlue(true);
|
||||
subtype.add(SubType.SALAMANDER);
|
||||
subtype.add(SubType.WARRIOR);
|
||||
power = new MageInt(4);
|
||||
toughness = new MageInt(3);
|
||||
}
|
||||
|
||||
public SalamnderWarriorToken(final SalamnderWarriorToken token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
public SalamnderWarriorToken copy() {
|
||||
return new SalamnderWarriorToken(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ public final class ShapeshifterToken extends TokenImpl {
|
|||
subtype.add(SubType.SHAPESHIFTER);
|
||||
power = new MageInt(2);
|
||||
toughness = new MageInt(2);
|
||||
setIsAllCreatureTypes(true);
|
||||
addAbility(ChangelingAbility.getInstance());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,12 @@ public class Spell extends StackObjImpl implements Card {
|
|||
+ " as Adventure spell of " + GameLog.getColoredObjectIdName(adventureCard);
|
||||
}
|
||||
|
||||
if (card instanceof ModalDoubleFacesCardHalf) {
|
||||
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card.getMainCard();
|
||||
return GameLog.replaceNameByColoredName(card, getSpellAbility().toString(), mdfCard)
|
||||
+ " as mdf side of " + GameLog.getColoredObjectIdName(mdfCard);
|
||||
}
|
||||
|
||||
return GameLog.replaceNameByColoredName(card, getSpellAbility().toString());
|
||||
}
|
||||
|
||||
|
|
@ -247,7 +253,7 @@ public class Spell extends StackObjImpl implements Card {
|
|||
// Otherwise effects like evolve trigger from creature comes into play event
|
||||
card.getCardType().remove(CardType.CREATURE);
|
||||
if (!card.getSubtype(game).contains(SubType.AURA)) {
|
||||
card.getSubtype(game).add(SubType.AURA);
|
||||
card.addSubType(game, SubType.AURA);
|
||||
}
|
||||
}
|
||||
UUID permId = null;
|
||||
|
|
@ -271,9 +277,7 @@ public class Spell extends StackObjImpl implements Card {
|
|||
Permanent permanent = game.getPermanent(permId);
|
||||
if (permanent instanceof PermanentCard) {
|
||||
permanent.setSpellAbility(ability); // otherwise spell ability without bestow will be set
|
||||
if (!card.getCardType().contains(CardType.CREATURE)) {
|
||||
card.addCardType(CardType.CREATURE);
|
||||
}
|
||||
card.addCardType(CardType.CREATURE);
|
||||
card.getSubtype(game).remove(SubType.AURA);
|
||||
}
|
||||
}
|
||||
|
|
@ -714,11 +718,6 @@ public class Spell extends StackObjImpl implements Card {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSplitCard() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransformable() {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1309,7 +1309,14 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
card.getId(), card.getId(), playerId, activationStatus.getApprovingObject());
|
||||
landEventAfter.setZone(cardZoneBefore);
|
||||
game.fireEvent(landEventAfter);
|
||||
game.fireInformEvent(getLogName() + " plays " + card.getLogName());
|
||||
|
||||
String playText = getLogName() + " plays " + card.getLogName();
|
||||
if (card instanceof ModalDoubleFacesCardHalf) {
|
||||
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card.getMainCard();
|
||||
playText = getLogName() + " plays " + GameLog.replaceNameByColoredName(card, card.getName(), mdfCard)
|
||||
+ " as MDF side of " + GameLog.getColoredObjectIdName(mdfCard);
|
||||
}
|
||||
game.fireInformEvent(playText);
|
||||
// game.removeBookmark(bookmark);
|
||||
resetStoredBookmark(game); // prevent undo after playing a land
|
||||
return true;
|
||||
|
|
@ -1602,6 +1609,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
needId1 = object.getId();
|
||||
needId2 = ((SplitCard) object).getLeftHalfCard().getId();
|
||||
needId3 = ((SplitCard) object).getRightHalfCard().getId();
|
||||
} else if (object instanceof ModalDoubleFacesCard) {
|
||||
needId1 = object.getId();
|
||||
needId2 = ((ModalDoubleFacesCard) object).getLeftHalfCard().getId();
|
||||
needId3 = ((ModalDoubleFacesCard) object).getRightHalfCard().getId();
|
||||
} else if (object instanceof AdventureCard) {
|
||||
needId1 = object.getId();
|
||||
needId2 = ((AdventureCard) object).getMainCard().getId();
|
||||
|
|
@ -3402,10 +3413,16 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
// BASIC abilities
|
||||
if (object instanceof SplitCard) {
|
||||
SplitCard splitCard = (SplitCard) object;
|
||||
getPlayableFromObjectSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(game), availableMana, output);
|
||||
getPlayableFromObjectSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(game), availableMana, output);
|
||||
getPlayableFromObjectSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output);
|
||||
SplitCard mainCard = (SplitCard) object;
|
||||
getPlayableFromObjectSingle(game, fromZone, mainCard.getLeftHalfCard(), mainCard.getLeftHalfCard().getAbilities(game), availableMana, output);
|
||||
getPlayableFromObjectSingle(game, fromZone, mainCard.getRightHalfCard(), mainCard.getRightHalfCard().getAbilities(game), availableMana, output);
|
||||
getPlayableFromObjectSingle(game, fromZone, mainCard, mainCard.getSharedAbilities(game), availableMana, output);
|
||||
}
|
||||
if (object instanceof ModalDoubleFacesCard) {
|
||||
ModalDoubleFacesCard mainCard = (ModalDoubleFacesCard) object;
|
||||
getPlayableFromObjectSingle(game, fromZone, mainCard.getLeftHalfCard(), mainCard.getLeftHalfCard().getAbilities(game), availableMana, output);
|
||||
getPlayableFromObjectSingle(game, fromZone, mainCard.getRightHalfCard(), mainCard.getRightHalfCard().getAbilities(game), availableMana, output);
|
||||
getPlayableFromObjectSingle(game, fromZone, mainCard, mainCard.getSharedAbilities(game), availableMana, output);
|
||||
} else if (object instanceof AdventureCard) {
|
||||
// adventure must use different card characteristics for different spells (main or adventure)
|
||||
AdventureCard adventureCard = (AdventureCard) object;
|
||||
|
|
@ -4414,6 +4431,9 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
Cards cards = new CardsImpl(this.getLibrary().getTopCards(game, event.getAmount()));
|
||||
this.moveCards(cards, Zone.GRAVEYARD, source, game);
|
||||
for (Card card : cards.getCards(game)) {
|
||||
game.fireEvent(GameEvent.getEvent(EventType.MILLED_CARD, card.getId(), source.getSourceId(), getId()));
|
||||
}
|
||||
return cards;
|
||||
}
|
||||
|
||||
|
|
@ -4525,9 +4545,14 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean scry(int value, Ability source, Game game) {
|
||||
game.informPlayers(getLogName() + " scries " + value);
|
||||
GameEvent event = new GameEvent(EventType.SCRY, getId(), source == null
|
||||
? null : source.getSourceId(), getId(), value, true);
|
||||
if (game.replaceEvent(event)) {
|
||||
return false;
|
||||
}
|
||||
game.informPlayers(getLogName() + " scries " + event.getAmount());
|
||||
Cards cards = new CardsImpl();
|
||||
cards.addAll(getLibrary().getTopCards(game, value));
|
||||
cards.addAll(getLibrary().getTopCards(game, event.getAmount()));
|
||||
if (!cards.isEmpty()) {
|
||||
TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY,
|
||||
new FilterCard("card" + (cards.size() == 1 ? "" : "s")
|
||||
|
|
@ -4537,8 +4562,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
cards.removeAll(target.getTargets());
|
||||
putCardsOnTopOfLibrary(cards, game, source, true);
|
||||
}
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.SCRY, getId(), source == null
|
||||
? null : source.getSourceId(), getId(), value, true));
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.SCRIED, getId(), source == null
|
||||
? null : source.getSourceId(), getId(), event.getAmount(), true));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package mage.target.common;
|
||||
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
|
||||
/**
|
||||
* @author North
|
||||
|
|
@ -17,11 +17,11 @@ public class TargetCreaturePermanentAmount extends TargetPermanentAmount {
|
|||
this(amount, StaticFilters.FILTER_PERMANENT_CREATURE);
|
||||
}
|
||||
|
||||
public TargetCreaturePermanentAmount(int amount, FilterCreaturePermanent filter) {
|
||||
public TargetCreaturePermanentAmount(int amount, FilterPermanent filter) {
|
||||
super(amount, filter);
|
||||
}
|
||||
|
||||
public TargetCreaturePermanentAmount(DynamicValue amount, FilterCreaturePermanent filter) {
|
||||
public TargetCreaturePermanentAmount(DynamicValue amount, FilterPermanent filter) {
|
||||
super(amount, filter);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public class TargetCreaturePermanentWithDifferentTypes extends TargetCreaturePer
|
|||
Permanent selectedCreature = game.getPermanent(targetId);
|
||||
if (selectedCreature != null
|
||||
&& !creature.getId().equals(selectedCreature.getId())) {
|
||||
if (creature.shareSubtypes(selectedCreature, game)) {
|
||||
if (creature.shareCreatureTypes(selectedCreature, game)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
package mage.util;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import mage.MageObject;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Abilities;
|
||||
|
|
@ -15,8 +10,12 @@ import mage.abilities.SpellAbility;
|
|||
import mage.abilities.costs.VariableCost;
|
||||
import mage.abilities.costs.mana.*;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.HintUtils;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.MeldCard;
|
||||
import mage.cards.ModalDoubleFacesCard;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.*;
|
||||
import mage.filter.Filter;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
|
|
@ -30,19 +29,31 @@ import mage.game.stack.Spell;
|
|||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.util.functions.CopyTokenFunction;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author nantuko
|
||||
*/
|
||||
public final class CardUtil {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CardUtil.class);
|
||||
|
||||
public static final List<String> RULES_ERROR_INFO = ImmutableList.of("Exception occurred in rules generation");
|
||||
|
||||
private static final String SOURCE_EXILE_ZONE_TEXT = "SourceExileZone";
|
||||
|
||||
static final String[] numberStrings = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"};
|
||||
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"};
|
||||
|
||||
static final String[] ordinalStrings = {"first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eightth", "ninth",
|
||||
"tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth", "twentieth"};
|
||||
"tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth", "twentieth"};
|
||||
|
||||
public static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
|
||||
|
||||
|
|
@ -877,4 +888,71 @@ public final class CardUtil {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return card name for same name searching
|
||||
*
|
||||
* @param card
|
||||
* @return
|
||||
*/
|
||||
public static String getCardNameForSameNameSearch(Card card) {
|
||||
// it's ok to return one name only cause NamePredicate can find same card by first name
|
||||
if (card instanceof SplitCard) {
|
||||
return ((SplitCard) card).getLeftHalfCard().getName();
|
||||
} else if (card instanceof ModalDoubleFacesCard) {
|
||||
return ((ModalDoubleFacesCard) card).getLeftHalfCard().getName();
|
||||
} else {
|
||||
return card.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> getCardRulesWithAdditionalInfo(UUID cardId, String cardName,
|
||||
Abilities<Ability> rulesSource, Abilities<Ability> hintAbilities) {
|
||||
return getCardRulesWithAdditionalInfo(null, cardId, cardName, rulesSource, hintAbilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare rules list from abilities
|
||||
*
|
||||
* @param rulesSource abilities list to show as rules
|
||||
* @param hintsSource abilities list to show as card hints only (you can add additional hints here; exameple: from second or transformed side)
|
||||
*/
|
||||
public static List<String> getCardRulesWithAdditionalInfo(Game game, UUID cardId, String cardName,
|
||||
Abilities<Ability> rulesSource, Abilities<Ability> hintsSource) {
|
||||
try {
|
||||
List<String> rules = rulesSource.getRules(cardName);
|
||||
|
||||
if (game != null) {
|
||||
|
||||
// debug state
|
||||
for (String data : game.getState().getCardState(cardId).getInfo().values()) {
|
||||
rules.add(data);
|
||||
}
|
||||
|
||||
// ability hints
|
||||
List<String> abilityHints = new ArrayList<>();
|
||||
if (HintUtils.ABILITY_HINTS_ENABLE) {
|
||||
for (Ability ability : hintsSource) {
|
||||
for (Hint hint : ability.getHints()) {
|
||||
String s = hint.getText(game, ability);
|
||||
if (s != null && !s.isEmpty()) {
|
||||
abilityHints.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// restrict hints only for permanents, not cards
|
||||
// total hints
|
||||
if (!abilityHints.isEmpty()) {
|
||||
rules.add(HintUtils.HINT_START_MARK);
|
||||
HintUtils.appendHints(rules, abilityHints);
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception in rules generation for card: " + cardName, e);
|
||||
}
|
||||
return RULES_ERROR_INFO;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public class AddSubtypeApplier extends ApplyToPermanent {
|
|||
@Override
|
||||
public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) {
|
||||
if (!permanent.hasSubtype(subtype, game)) {
|
||||
permanent.getSubtype(game).add(subtype);
|
||||
permanent.addSubType(game, subtype);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ public class AddSubtypeApplier extends ApplyToPermanent {
|
|||
@Override
|
||||
public boolean apply(Game game, MageObject mageObject, Ability source, UUID copyToObjectId) {
|
||||
if (!mageObject.hasSubtype(subtype, game)) {
|
||||
mageObject.getSubtype(game).add(subtype);
|
||||
mageObject.addSubType(game, subtype);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.watchers.common;
|
||||
|
||||
import mage.abilities.keyword.ChangelingAbility;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.game.Game;
|
||||
|
|
@ -30,22 +28,24 @@ public class ProwlWatcher extends Watcher {
|
|||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == EventType.DAMAGED_PLAYER) {
|
||||
DamagedPlayerEvent dEvent = (DamagedPlayerEvent) event;
|
||||
if (dEvent.isCombatDamage()) {
|
||||
Permanent creature = game.getPermanent(dEvent.getSourceId());
|
||||
if (creature != null && !allSubtypes.contains(creature.getControllerId())) {
|
||||
if (creature.getAbilities().containsKey(ChangelingAbility.getInstance().getId()) || creature.isAllCreatureTypes()) {
|
||||
allSubtypes.add(creature.getControllerId());
|
||||
} else {
|
||||
Set<SubType> subtypes = damagingSubtypes.getOrDefault(creature.getControllerId(), new LinkedHashSet<>());
|
||||
|
||||
subtypes.addAll(creature.getSubtype(game));
|
||||
damagingSubtypes.put(creature.getControllerId(), subtypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.getType() != EventType.DAMAGED_PLAYER) {
|
||||
return;
|
||||
}
|
||||
DamagedPlayerEvent dEvent = (DamagedPlayerEvent) event;
|
||||
if (!dEvent.isCombatDamage()) {
|
||||
return;
|
||||
}
|
||||
Permanent creature = game.getPermanent(dEvent.getSourceId());
|
||||
if (creature == null || allSubtypes.contains(creature.getControllerId())) {
|
||||
return;
|
||||
}
|
||||
if (creature.isAllCreatureTypes()) {
|
||||
allSubtypes.add(creature.getControllerId());
|
||||
return;
|
||||
}
|
||||
damagingSubtypes
|
||||
.computeIfAbsent(creature.getControllerId(), m -> new LinkedHashSet<>())
|
||||
.addAll(creature.getSubtype(game));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue