mirror of
https://github.com/magefree/mage.git
synced 2025-12-26 05:22:02 -08:00
Merge branch 'master' into fix-tapped-for-mana-event
This commit is contained in:
commit
60781604f3
1600 changed files with 42766 additions and 10461 deletions
|
|
@ -1,5 +1,8 @@
|
|||
package mage.abilities;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.CostAdjuster;
|
||||
|
|
@ -23,10 +26,6 @@ import mage.target.Targets;
|
|||
import mage.target.targetadjustment.TargetAdjuster;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Practically everything in the game is started from an Ability. This interface
|
||||
* describes what an Ability is composed of at the highest level.
|
||||
|
|
@ -47,8 +46,10 @@ public interface Ability extends Controllable, Serializable {
|
|||
*
|
||||
* @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility,
|
||||
* mage.game.Game)
|
||||
* @see mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility)
|
||||
* @see mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility)
|
||||
* @see
|
||||
* mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility)
|
||||
* @see
|
||||
* mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility)
|
||||
*/
|
||||
void newId();
|
||||
|
||||
|
|
@ -57,8 +58,10 @@ public interface Ability extends Controllable, Serializable {
|
|||
*
|
||||
* @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility,
|
||||
* mage.game.Game)
|
||||
* @see mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility)
|
||||
* @see mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility)
|
||||
* @see
|
||||
* mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility)
|
||||
* @see
|
||||
* mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility)
|
||||
*/
|
||||
void newOriginalId();
|
||||
|
||||
|
|
@ -264,15 +267,16 @@ public interface Ability extends Controllable, Serializable {
|
|||
/**
|
||||
* Activates this ability prompting the controller to pay any mandatory
|
||||
*
|
||||
* @param game A reference the {@link Game} for which this ability should be
|
||||
* activated within.
|
||||
* @param game A reference the {@link Game} for which this ability should be
|
||||
* activated within.
|
||||
* @param noMana Whether or not {@link ManaCosts} have to be paid.
|
||||
* @return True if this ability was successfully activated.
|
||||
* @see mage.players.PlayerImpl#cast(mage.abilities.SpellAbility,
|
||||
* mage.game.Game, boolean)
|
||||
* @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility,
|
||||
* mage.game.Game)
|
||||
* @see mage.players.PlayerImpl#triggerAbility(mage.abilities.TriggeredAbility,
|
||||
* @see
|
||||
* mage.players.PlayerImpl#triggerAbility(mage.abilities.TriggeredAbility,
|
||||
* mage.game.Game)
|
||||
*/
|
||||
boolean activate(Game game, boolean noMana);
|
||||
|
|
@ -286,7 +290,8 @@ public interface Ability extends Controllable, Serializable {
|
|||
*
|
||||
* @param game The {@link Game} for which this ability resolves within.
|
||||
* @return Whether or not this ability successfully resolved.
|
||||
* @see mage.players.PlayerImpl#playManaAbility(mage.abilities.mana.ManaAbility,
|
||||
* @see
|
||||
* mage.players.PlayerImpl#playManaAbility(mage.abilities.mana.ManaAbility,
|
||||
* mage.game.Game)
|
||||
* @see mage.players.PlayerImpl#specialAction(mage.abilities.SpecialAction,
|
||||
* mage.game.Game)
|
||||
|
|
@ -461,15 +466,6 @@ public interface Ability extends Controllable, Serializable {
|
|||
*/
|
||||
String getGameLogMessage(Game game);
|
||||
|
||||
/**
|
||||
* Used to deactivate cost modification logic of ability activation for some
|
||||
* special handling (e.g. FlashbackAbility gets cost modifiaction twice
|
||||
* because of how it's handled now)
|
||||
*
|
||||
* @param active execute no cost modification
|
||||
*/
|
||||
void setCostModificationActive(boolean active);
|
||||
|
||||
boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
package mage.abilities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.Mana;
|
||||
import mage.abilities.costs.*;
|
||||
|
|
@ -34,11 +38,6 @@ import mage.util.ThreadLocalStringBuilder;
|
|||
import mage.watchers.Watcher;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
|
|
@ -57,7 +56,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
protected ManaCosts<ManaCost> manaCostsToPay;
|
||||
protected Costs<Cost> costs;
|
||||
protected Costs<Cost> optionalCosts;
|
||||
protected Modes modes;
|
||||
protected Modes modes; // access to it by GetModes only (it's can be override by some abilities)
|
||||
protected Zone zone;
|
||||
protected String name;
|
||||
protected AbilityWord abilityWord;
|
||||
|
|
@ -65,11 +64,10 @@ public abstract class AbilityImpl implements Ability {
|
|||
protected boolean ruleAtTheTop = false;
|
||||
protected boolean ruleVisible = true;
|
||||
protected boolean ruleAdditionalCostsVisible = true;
|
||||
protected boolean costModificationActive = true;
|
||||
protected boolean activated = false;
|
||||
protected boolean worksFaceDown = false;
|
||||
protected int sourceObjectZoneChangeCounter;
|
||||
protected List<Watcher> watchers = new ArrayList<>();
|
||||
protected List<Watcher> watchers = new ArrayList<>(); // access to it by GetWatchers only (it's can be override by some abilities)
|
||||
protected List<Ability> subAbilities = null;
|
||||
protected boolean canFizzle = true;
|
||||
protected TargetAdjuster targetAdjuster = null;
|
||||
|
|
@ -101,7 +99,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
this.manaCostsToPay = ability.manaCostsToPay.copy();
|
||||
this.costs = ability.costs.copy();
|
||||
this.optionalCosts = ability.optionalCosts.copy();
|
||||
for (Watcher watcher : ability.watchers) {
|
||||
for (Watcher watcher : ability.getWatchers()) {
|
||||
watchers.add(watcher.copy());
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +113,6 @@ public abstract class AbilityImpl implements Ability {
|
|||
this.ruleAtTheTop = ability.ruleAtTheTop;
|
||||
this.ruleVisible = ability.ruleVisible;
|
||||
this.ruleAdditionalCostsVisible = ability.ruleAdditionalCostsVisible;
|
||||
this.costModificationActive = ability.costModificationActive;
|
||||
this.worksFaceDown = ability.worksFaceDown;
|
||||
this.abilityWord = ability.abilityWord;
|
||||
this.sourceObjectZoneChangeCounter = ability.sourceObjectZoneChangeCounter;
|
||||
|
|
@ -262,8 +259,9 @@ public abstract class AbilityImpl implements Ability {
|
|||
this.getManaCostsToPay().clear();
|
||||
}
|
||||
}
|
||||
if (modes.getAdditionalCost() != null) {
|
||||
modes.getAdditionalCost().addOptionalAdditionalModeCosts(this, game);
|
||||
|
||||
if (getModes().getAdditionalCost() != null) {
|
||||
getModes().getAdditionalCost().addOptionalAdditionalModeCosts(this, game);
|
||||
}
|
||||
// 20130201 - 601.2b
|
||||
// If the spell has alternative or additional costs that will be paid as it's being cast such
|
||||
|
|
@ -312,12 +310,12 @@ public abstract class AbilityImpl implements Ability {
|
|||
// each target the spell requires. A spell may require some targets only if an alternative or
|
||||
// additional cost (such as a buyback or kicker cost), or a particular mode, was chosen for it;
|
||||
// otherwise, the spell is cast as though it did not require those targets. If the spell has a
|
||||
// variable number of targets, the player announces how many targets he or she will choose before
|
||||
// he or she announces those targets. The same target can't be chosen multiple times for any one
|
||||
// variable number of targets, the player announces how many targets they will choose before
|
||||
// they announce those targets. The same target can't be chosen multiple times for any one
|
||||
// instance of the word "target" on the spell. However, if the spell uses the word "target" in
|
||||
// multiple places, the same object, player, or zone can be chosen once for each instance of the
|
||||
// word "target" (as long as it fits the targeting criteria). If any effects say that an object
|
||||
// or player must be chosen as a target, the player chooses targets so that he or she obeys the
|
||||
// or player must be chosen as a target, the player chooses targets so that they obey the
|
||||
// maximum possible number of such effects without violating any rules or effects that say that
|
||||
// an object or player can't be chosen as a target. The chosen players, objects, and/or zones
|
||||
// each become a target of that spell. (Any abilities that trigger when those players, objects,
|
||||
|
|
@ -354,30 +352,9 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
|
||||
//20101001 - 601.2e
|
||||
if (costModificationActive) {
|
||||
|
||||
// TODO: replace all AdjustingSourceCosts abilities to continuus effect, see Affinity example
|
||||
//20100716 - 601.2e
|
||||
if (sourceObject != null) {
|
||||
sourceObject.adjustCosts(this, game);
|
||||
if (sourceObject instanceof Card) {
|
||||
for (Ability ability : ((Card) sourceObject).getAbilities(game)) {
|
||||
if (ability instanceof AdjustingSourceCosts) {
|
||||
((AdjustingSourceCosts) ability).adjustCosts(this, game);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Ability ability : sourceObject.getAbilities()) {
|
||||
if (ability instanceof AdjustingSourceCosts) {
|
||||
((AdjustingSourceCosts) ability).adjustCosts(this, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceObject != null) {
|
||||
sourceObject.adjustCosts(this, game); // still needed
|
||||
game.getContinuousEffects().costModification(this, game);
|
||||
} else {
|
||||
costModificationActive = true;
|
||||
}
|
||||
|
||||
UUID activatorId = controllerId;
|
||||
|
|
@ -519,14 +496,14 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
/**
|
||||
* 601.2b If a cost that will be paid as the spell is being cast includes
|
||||
* Phyrexian mana symbols, the player announces whether he or she intends to
|
||||
* pay 2 life or the corresponding colored mana cost for each of those
|
||||
* symbols.
|
||||
* Phyrexian mana symbols, the player announces whether they intend to pay 2
|
||||
* life or the corresponding colored mana cost for each of those symbols.
|
||||
*/
|
||||
private void handlePhyrexianManaCosts(Game game, UUID sourceId, Player controller) {
|
||||
Iterator<ManaCost> costIterator = manaCostsToPay.iterator();
|
||||
while (costIterator.hasNext()) {
|
||||
ManaCost cost = costIterator.next();
|
||||
|
||||
if (cost instanceof PhyrexianManaCost) {
|
||||
PhyrexianManaCost phyrexianManaCost = (PhyrexianManaCost) cost;
|
||||
PayLifeCost payLifeCost = new PayLifeCost(2);
|
||||
|
|
@ -633,7 +610,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
@Override
|
||||
public void setControllerId(UUID controllerId) {
|
||||
this.controllerId = controllerId;
|
||||
for (Watcher watcher : watchers) {
|
||||
for (Watcher watcher : getWatchers()) {
|
||||
watcher.setControllerId(controllerId);
|
||||
}
|
||||
|
||||
|
|
@ -661,7 +638,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
subAbility.setSourceId(sourceId);
|
||||
}
|
||||
}
|
||||
for (Watcher watcher : watchers) {
|
||||
for (Watcher watcher : getWatchers()) {
|
||||
watcher.setSourceId(sourceId);
|
||||
}
|
||||
|
||||
|
|
@ -734,7 +711,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
watcher.setSourceId(this.sourceId);
|
||||
watcher.setControllerId(this.controllerId);
|
||||
watchers.add(watcher);
|
||||
getWatchers().add(watcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -866,7 +843,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
if (getModes().getMode() != null) {
|
||||
return getModes().getMode().getTargets();
|
||||
}
|
||||
return null;
|
||||
return new Targets();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1167,11 +1144,6 @@ public abstract class AbilityImpl implements Ability {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCostModificationActive(boolean active) {
|
||||
this.costModificationActive = active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getWorksFaceDown() {
|
||||
return worksFaceDown;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -62,14 +61,6 @@ public interface ActivatedAbility extends Ability {
|
|||
@Override
|
||||
ActivatedAbility copy();
|
||||
|
||||
/**
|
||||
* Set a flag to know, that the ability is only created adn used to check
|
||||
* what's playbable for the player.
|
||||
*/
|
||||
void setCheckPlayableMode();
|
||||
|
||||
boolean isCheckPlayableMode();
|
||||
|
||||
void setMaxActivationsPerTurn(int maxActivationsPerTurn);
|
||||
|
||||
int getMaxActivationsPerTurn(Game game);
|
||||
|
|
|
|||
|
|
@ -46,11 +46,9 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
|
|||
protected TimingRule timing = TimingRule.INSTANT;
|
||||
protected TargetController mayActivate = TargetController.YOU;
|
||||
protected UUID activatorId;
|
||||
protected boolean checkPlayableMode;
|
||||
|
||||
protected ActivatedAbilityImpl(AbilityType abilityType, Zone zone) {
|
||||
super(abilityType, zone);
|
||||
this.checkPlayableMode = false;
|
||||
}
|
||||
|
||||
public ActivatedAbilityImpl(final ActivatedAbilityImpl ability) {
|
||||
|
|
@ -58,7 +56,6 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
|
|||
timing = ability.timing;
|
||||
mayActivate = ability.mayActivate;
|
||||
activatorId = ability.activatorId;
|
||||
checkPlayableMode = ability.checkPlayableMode;
|
||||
maxActivationsPerTurn = ability.maxActivationsPerTurn;
|
||||
condition = ability.condition;
|
||||
}
|
||||
|
|
@ -262,16 +259,6 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
|
|||
this.timing = timing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCheckPlayableMode() {
|
||||
checkPlayableMode = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCheckPlayableMode() {
|
||||
return checkPlayableMode;
|
||||
}
|
||||
|
||||
protected boolean hasMoreActivationsThisTurn(Game game) {
|
||||
if (getMaxActivationsPerTurn(game) == Integer.MAX_VALUE) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import mage.filter.FilterPlayer;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetOpponent;
|
||||
import mage.util.RandomUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
|
@ -18,15 +19,19 @@ import java.util.*;
|
|||
public class Modes extends LinkedHashMap<UUID, Mode> {
|
||||
|
||||
private Mode currentMode; // the current mode of the selected modes
|
||||
private final List<UUID> selectedModes = new ArrayList<>();
|
||||
private final List<UUID> selectedModes = new ArrayList<>(); // all selected modes (this + duplicate)
|
||||
private final Map<UUID, Mode> duplicateModes = new LinkedHashMap<>(); // for 2x selects: copy mode and put it to duplicate list
|
||||
private final Map<UUID, UUID> duplicateToOriginalModeRefs = new LinkedHashMap<>(); // for 2x selects: stores ref from duplicate to original mode
|
||||
|
||||
private int minModes;
|
||||
private int maxModes;
|
||||
private TargetController modeChooser;
|
||||
private boolean eachModeMoreThanOnce; // each mode can be selected multiple times during one choice
|
||||
private boolean eachModeOnlyOnce; // state if each mode can be chosen only once as long as the source object exists
|
||||
private final Map<UUID, Mode> duplicateModes = new LinkedHashMap<>();
|
||||
private OptionalAdditionalModeSourceCosts optionalAdditionalModeSourceCosts = null; // only set if costs have to be paid
|
||||
private Filter maxModesFilter = null; // calculates the max number of available modes
|
||||
private boolean isRandom = false;
|
||||
private String chooseText = null;
|
||||
|
||||
public Modes() {
|
||||
this.currentMode = new Mode();
|
||||
|
|
@ -46,6 +51,8 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
for (Map.Entry<UUID, Mode> entry : modes.duplicateModes.entrySet()) {
|
||||
duplicateModes.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
duplicateToOriginalModeRefs.putAll(modes.duplicateToOriginalModeRefs);
|
||||
|
||||
this.minModes = modes.minModes;
|
||||
this.maxModes = modes.maxModes;
|
||||
this.selectedModes.addAll(modes.getSelectedModes());
|
||||
|
|
@ -56,6 +63,8 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
this.optionalAdditionalModeSourceCosts = modes.optionalAdditionalModeSourceCosts;
|
||||
this.maxModesFilter = modes.maxModesFilter; // can't change so no copy needed
|
||||
|
||||
this.isRandom = modes.isRandom;
|
||||
this.chooseText = modes.chooseText;
|
||||
if (modes.getSelectedModes().isEmpty()) {
|
||||
this.currentMode = values().iterator().next();
|
||||
} else {
|
||||
|
|
@ -111,6 +120,32 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
return selectedModes;
|
||||
}
|
||||
|
||||
public int getSelectedStats(UUID modeId) {
|
||||
int count = 0;
|
||||
if (this.selectedModes.contains(modeId)) {
|
||||
|
||||
// single select
|
||||
count++;
|
||||
|
||||
// multiple select (all 2x select generate new duplicate mode)
|
||||
UUID originalId;
|
||||
if (this.duplicateModes.containsKey(modeId)) {
|
||||
// modeId is duplicate
|
||||
originalId = this.duplicateToOriginalModeRefs.get(modeId);
|
||||
} else {
|
||||
// modeId is original
|
||||
originalId = modeId;
|
||||
}
|
||||
for (UUID id : this.duplicateToOriginalModeRefs.values()) {
|
||||
if (id.equals(originalId)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setMinModes(int minModes) {
|
||||
this.minModes = minModes;
|
||||
}
|
||||
|
|
@ -163,6 +198,12 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
if (this.size() > 1) {
|
||||
this.selectedModes.clear();
|
||||
this.duplicateModes.clear();
|
||||
this.duplicateToOriginalModeRefs.clear();
|
||||
if (this.isRandom) {
|
||||
List<Mode> modes = getAvailableModes(source, game);
|
||||
this.addSelectedMode(modes.get(RandomUtil.nextInt(modes.size())).getId());
|
||||
return true;
|
||||
}
|
||||
// check if mode modifying abilities exist
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
|
|
@ -276,9 +317,11 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
private void addSelectedMode(UUID modeId) {
|
||||
if (selectedModes.contains(modeId) && eachModeMoreThanOnce) {
|
||||
Mode duplicateMode = get(modeId).copy();
|
||||
UUID originalId = modeId;
|
||||
duplicateMode.setRandomId();
|
||||
modeId = duplicateMode.getId();
|
||||
duplicateModes.put(modeId, duplicateMode);
|
||||
duplicateToOriginalModeRefs.put(duplicateMode.getId(), originalId);
|
||||
|
||||
}
|
||||
this.selectedModes.add(modeId);
|
||||
|
|
@ -319,7 +362,7 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
nonAvailableModes = getAlreadySelectedModes(source, game);
|
||||
}
|
||||
for (Mode mode : this.values()) {
|
||||
if (isEachModeOnlyOnce() && nonAvailableModes != null && nonAvailableModes.contains(mode.getId())) {
|
||||
if (isEachModeOnlyOnce() && nonAvailableModes.contains(mode.getId())) {
|
||||
continue;
|
||||
}
|
||||
availableModes.add(mode);
|
||||
|
|
@ -332,10 +375,14 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
return this.getMode().getEffects().getText(this.getMode());
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (this.getMaxModesFilter() != null) {
|
||||
if (this.chooseText != null) {
|
||||
sb.append(chooseText);
|
||||
} else if (this.getMaxModesFilter() != null) {
|
||||
sb.append("choose one or more. Each mode must target ").append(getMaxModesFilter().getMessage());
|
||||
} else if (this.getMinModes() == 0 && this.getMaxModes() == 1) {
|
||||
sb.append("choose up to one");
|
||||
} else if (this.getMinModes() == 0 && this.getMaxModes() == 3) {
|
||||
sb.append("choose any number");
|
||||
} else if (this.getMinModes() == 1 && this.getMaxModes() > 2) {
|
||||
sb.append("choose one or more");
|
||||
} else if (this.getMinModes() == 1 && this.getMaxModes() == 2) {
|
||||
|
|
@ -355,11 +402,13 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
}
|
||||
|
||||
if (isEachModeMoreThanOnce()) {
|
||||
sb.append(". You may choose the same mode more than once.<br>");
|
||||
} else {
|
||||
sb.append(" —<br>");
|
||||
sb.append(". You may choose the same mode more than once.");
|
||||
} else if (chooseText == null) {
|
||||
sb.append(" —");
|
||||
}
|
||||
|
||||
sb.append("<br>");
|
||||
|
||||
for (Mode mode : this.values()) {
|
||||
sb.append("&bull ");
|
||||
sb.append(mode.getEffects().getTextStartingUpperCase(mode));
|
||||
|
|
@ -399,4 +448,11 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
this.optionalAdditionalModeSourceCosts = optionalAdditionalModeSourceCosts;
|
||||
}
|
||||
|
||||
public void setRandom(boolean isRandom) {
|
||||
this.isRandom = isRandom;
|
||||
}
|
||||
|
||||
public void setChooseText(String chooseText) {
|
||||
this.chooseText = chooseText;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package mage.abilities;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.costs.Cost;
|
||||
|
|
@ -15,9 +17,6 @@ import mage.game.events.GameEvent;
|
|||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
|
|
@ -63,6 +62,12 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
*/
|
||||
public boolean spellCanBeActivatedRegularlyNow(UUID playerId, Game game) {
|
||||
MageObject object = game.getObject(sourceId);
|
||||
if (object == null) {
|
||||
return false;
|
||||
}
|
||||
if (game.getState().getValue("PlayFromNotOwnHandZone" + object.getId()) != null) {
|
||||
return (Boolean) game.getState().getValue("PlayFromNotOwnHandZone" + object.getId()); // card like Chandra, Torch of Defiance +1 loyal ability)
|
||||
}
|
||||
return null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game) // check this first to allow Offering in main phase
|
||||
|| timing == TimingRule.INSTANT
|
||||
|| object.hasAbility(FlashAbility.getInstance().getId(), game)
|
||||
|
|
@ -72,7 +77,8 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
@Override
|
||||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
if (this.spellCanBeActivatedRegularlyNow(playerId, game)) {
|
||||
if (spellAbilityType == SpellAbilityType.SPLIT || spellAbilityType == SpellAbilityType.SPLIT_AFTERMATH) {
|
||||
if (spellAbilityType == SpellAbilityType.SPLIT
|
||||
|| spellAbilityType == SpellAbilityType.SPLIT_AFTERMATH) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
// fix for Gitaxian Probe and casting opponent's spells
|
||||
|
|
@ -84,7 +90,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
}
|
||||
}
|
||||
// Check if rule modifying events prevent to cast the spell in check playable mode
|
||||
if (this.isCheckPlayableMode()) {
|
||||
if (game.inCheckPlayableState()) {
|
||||
if (game.getContinuousEffects().preventedByRuleModification(
|
||||
GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, this.getId(), this.getSourceId(), playerId), this, game, true)) {
|
||||
return ActivationStatus.getFalse();
|
||||
|
|
@ -93,7 +99,8 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
// Alternate spell abilities (Flashback, Overload) can't be cast with no mana to pay option
|
||||
if (getSpellAbilityType() == SpellAbilityType.BASE_ALTERNATE) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null && getSourceId().equals(player.getCastSourceIdWithAlternateMana())) {
|
||||
if (player != null
|
||||
&& player.getCastSourceIdWithAlternateMana().contains(getSourceId())) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,22 +12,29 @@ import mage.game.permanent.Permanent;
|
|||
/**
|
||||
* Constellation
|
||||
*
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
public class ConstellationAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private final boolean thisOr;
|
||||
|
||||
public ConstellationAbility(Effect effect) {
|
||||
this(effect, false);
|
||||
}
|
||||
|
||||
public ConstellationAbility(Effect effect, boolean optional) {
|
||||
this(effect, optional, true);
|
||||
}
|
||||
|
||||
public ConstellationAbility(Effect effect, boolean optional, boolean thisOr) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
this.thisOr = thisOr;
|
||||
}
|
||||
|
||||
public ConstellationAbility(final ConstellationAbility ability) {
|
||||
super(ability);
|
||||
this.thisOr = ability.thisOr;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -42,17 +49,17 @@ public class ConstellationAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getPlayerId().equals(this.getControllerId())) {
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
if (permanent != null && permanent.isEnchantment()) {
|
||||
return true;
|
||||
}
|
||||
if (!event.getPlayerId().equals(this.getControllerId())) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
return permanent != null && permanent.isEnchantment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return new StringBuilder("<i>Constellation</i> — Whenever {this} or another enchantment enters the battlefield under your control, ").append(super.getRule()).toString();
|
||||
return "<i>Constellation</i> — Whenever "
|
||||
+ (thisOr ? "{this} or another" : "an")
|
||||
+ " enchantment enters the battlefield under your control, " + super.getRule();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,42 @@
|
|||
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.SetTargetPointer;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterStackObject;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public class BecomesTargetTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private final FilterStackObject filter;
|
||||
private final SetTargetPointer setTargetPointer;
|
||||
|
||||
public BecomesTargetTriggeredAbility(Effect effect) {
|
||||
this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY);
|
||||
}
|
||||
|
||||
public BecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter) {
|
||||
this(effect, filter, SetTargetPointer.NONE);
|
||||
}
|
||||
|
||||
public BecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer) {
|
||||
super(Zone.BATTLEFIELD, effect);
|
||||
this.filter = filter.copy();
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
}
|
||||
|
||||
public BecomesTargetTriggeredAbility(final BecomesTargetTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.filter = ability.filter.copy();
|
||||
this.setTargetPointer = ability.setTargetPointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -45,7 +52,25 @@ public class BecomesTargetTriggeredAbility extends TriggeredAbilityImpl {
|
|||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
StackObject sourceObject = game.getStack().getStackObject(event.getSourceId());
|
||||
return event.getTargetId().equals(getSourceId()) && filter.match(sourceObject, getSourceId(), getControllerId(), game);
|
||||
if (!event.getTargetId().equals(getSourceId())
|
||||
|| !filter.match(sourceObject, getSourceId(), getControllerId(), game)) {
|
||||
return false;
|
||||
}
|
||||
switch (setTargetPointer) {
|
||||
case PLAYER:
|
||||
this.getEffects().stream()
|
||||
.forEach(effect -> effect.setTargetPointer(
|
||||
new FixedTarget(sourceObject.getControllerId(), game)
|
||||
));
|
||||
break;
|
||||
case SPELL:
|
||||
this.getEffects().stream()
|
||||
.forEach(effect -> effect.setTargetPointer(
|
||||
new FixedTarget(sourceObject.getId(), game)
|
||||
));
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -10,11 +10,9 @@ import mage.constants.TargetController;
|
|||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeff
|
||||
*/
|
||||
public class BeginningOfUntapTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
|
@ -59,8 +57,8 @@ public class BeginningOfUntapTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
return yours;
|
||||
case NOT_YOU:
|
||||
Player controller = game.getPlayer(this.getControllerId());
|
||||
if (controller != null && controller.getInRange().contains(event.getPlayerId()) && !event.getPlayerId().equals(this.getControllerId())) {
|
||||
if (game.getState().getPlayersInRange(this.getControllerId(), game).contains(event.getPlayerId())
|
||||
&& !event.getPlayerId().equals(this.getControllerId())) {
|
||||
if (getTargets().isEmpty()) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
effect.setTargetPointer(new FixedTarget(event.getPlayerId()));
|
||||
|
|
@ -80,8 +78,7 @@ public class BeginningOfUntapTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
break;
|
||||
case ANY:
|
||||
controller = game.getPlayer(this.getControllerId());
|
||||
if (controller != null && controller.getInRange().contains(event.getPlayerId())) {
|
||||
if (game.getState().getPlayersInRange(this.getControllerId(), game).contains(event.getPlayerId())) {
|
||||
if (getTargets().isEmpty()) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
effect.setTargetPointer(new FixedTarget(event.getPlayerId()));
|
||||
|
|
@ -89,6 +86,7 @@ public class BeginningOfUntapTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.abilities.common;
|
||||
|
||||
import java.util.Locale;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.TargetController;
|
||||
|
|
@ -11,8 +10,9 @@ import mage.game.events.GameEvent;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Loki
|
||||
*/
|
||||
public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
|
@ -91,6 +91,7 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
break;
|
||||
case ANY:
|
||||
case ACTIVE:
|
||||
if (setTargetPointer && getTargets().isEmpty()) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
effect.setTargetPointer(new FixedTarget(event.getPlayerId()));
|
||||
|
|
@ -137,6 +138,8 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl {
|
|||
return sb.insert(0, generateZoneString()).insert(0, "At the beginning of each opponent's upkeep, ").toString();
|
||||
case ANY:
|
||||
return sb.insert(0, generateZoneString()).insert(0, "At the beginning of each upkeep, ").toString();
|
||||
case ACTIVE:
|
||||
return sb.insert(0, generateZoneString()).insert(0, "At the beginning of each player's upkeep, ").toString();
|
||||
case CONTROLLER_ATTACHED_TO:
|
||||
return sb.insert(0, generateZoneString()).insert(0, "At the beginning of the upkeep of enchanted creature's controller, ").toString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public class ControllerDivideCombatDamageAbility extends StaticAbility implement
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "You may assign {this}'s combat damage divided as you choose among defending player and/or any number of creatures he or she controls.";
|
||||
return "You may assign {this}'s combat damage divided as you choose among defending player and/or any number of creatures they control.";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import mage.game.events.GameEvent.EventType;
|
|||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public class ControllerPlaysLandTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
|
@ -35,7 +34,7 @@ public class ControllerPlaysLandTriggeredAbility extends TriggeredAbilityImpl {
|
|||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
Permanent land = game.getPermanent(event.getTargetId());
|
||||
return land.getControllerId().equals(controllerId);
|
||||
return land != null && land.getControllerId().equals(controllerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package mage.abilities.common;
|
|||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.SetTargetPointer;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
|
|
@ -33,7 +34,8 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl {
|
|||
this(effect, attachedDescription, optional, diesRuleText, SetTargetPointer.NONE);
|
||||
}
|
||||
|
||||
public DiesAttachedTriggeredAbility(Effect effect, String attachedDescription, boolean optional, boolean diesRuleText, SetTargetPointer setTargetPointer) {
|
||||
public DiesAttachedTriggeredAbility(Effect effect, String attachedDescription, boolean optional,
|
||||
boolean diesRuleText, SetTargetPointer setTargetPointer) {
|
||||
super(Zone.ALL, effect, optional); // because the trigger only triggers if the object was attached, it doesn't matter where the Attachment was moved to (e.g. by replacement effect) after the trigger triggered, so Zone.all
|
||||
this.attachedDescription = attachedDescription;
|
||||
this.diesRuleText = diesRuleText;
|
||||
|
|
@ -62,18 +64,27 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl {
|
|||
if (((ZoneChangeEvent) event).isDiesEvent()) {
|
||||
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
||||
boolean triggered = false;
|
||||
if (zEvent.getTarget() != null && zEvent.getTarget().getAttachments() != null && zEvent.getTarget().getAttachments().contains(this.getSourceId())) {
|
||||
if (zEvent.getTarget() != null
|
||||
&& zEvent.getTarget().getAttachments() != null
|
||||
&& zEvent.getTarget().getAttachments().contains(this.getSourceId())) {
|
||||
triggered = true;
|
||||
} else {
|
||||
// If both (attachment and attached went to graveyard at the same time, the attachemnets can be already removed from the attached object.)
|
||||
// So check here with the LKI of the enchantment
|
||||
// If the attachment and attachedTo went to graveyard at the same time, the trigger applies.
|
||||
// If the attachment is removed beforehand, the trigger fails.
|
||||
// IE: A player cast Planar Clensing. The attachment is Disenchanted in reponse
|
||||
// and successfully removed from the attachedTo. The trigger fails.
|
||||
Permanent attachment = game.getPermanentOrLKIBattlefield(getSourceId());
|
||||
Card attachmentCard = game.getCard(getSourceId());
|
||||
if (attachment != null
|
||||
&& zEvent.getTargetId() != null && attachment.getAttachedTo() != null
|
||||
&& zEvent.getTargetId() != null
|
||||
&& attachment.getAttachedTo() != null
|
||||
&& zEvent.getTargetId().equals(attachment.getAttachedTo())) {
|
||||
Permanent attachedTo = game.getPermanentOrLKIBattlefield(attachment.getAttachedTo());
|
||||
if (attachedTo != null
|
||||
&& attachment.getAttachedToZoneChangeCounter() == attachedTo.getZoneChangeCounter(game)) { // zoneChangeCounter is stored in Permanent
|
||||
&& game.getState().getZone(attachedTo.getId()) == (Zone.GRAVEYARD) // Demonic Vigor
|
||||
&& attachmentCard != null
|
||||
&& attachment.getAttachedToZoneChangeCounter() == attachedTo.getZoneChangeCounter(game)
|
||||
&& attachment.getZoneChangeCounter(game) == attachmentCard.getZoneChangeCounter(game)) {
|
||||
triggered = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -82,10 +93,13 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl {
|
|||
for (Effect effect : getEffects()) {
|
||||
if (zEvent.getTarget() != null) {
|
||||
effect.setValue("attachedTo", zEvent.getTarget());
|
||||
effect.setValue("zcc", zEvent.getTarget().getZoneChangeCounter(game) + 1); // zone change info from battlefield
|
||||
if (setTargetPointer == SetTargetPointer.ATTACHED_TO_CONTROLLER) {
|
||||
Permanent attachment = game.getPermanentOrLKIBattlefield(getSourceId());
|
||||
if (attachment != null && attachment.getAttachedTo() != null) {
|
||||
Permanent attachedTo = (Permanent) game.getLastKnownInformation(attachment.getAttachedTo(), Zone.BATTLEFIELD, attachment.getAttachedToZoneChangeCounter());
|
||||
if (attachment != null
|
||||
&& attachment.getAttachedTo() != null) {
|
||||
Permanent attachedTo = (Permanent) game.getLastKnownInformation(attachment.getAttachedTo(),
|
||||
Zone.BATTLEFIELD, attachment.getAttachedToZoneChangeCounter());
|
||||
if (attachedTo != null) {
|
||||
effect.setTargetPointer(new FixedTarget(attachedTo.getControllerId()));
|
||||
}
|
||||
|
|
@ -95,7 +109,6 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -111,4 +124,4 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl {
|
|||
sb.append(super.getRule());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.MageObject;
|
||||
|
|
@ -34,11 +33,11 @@ public class DiesTriggeredAbility extends ZoneChangeTriggeredAbility {
|
|||
if (before == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(before instanceof PermanentToken) && !this.hasSourceObjectAbility(game, before, event)) {
|
||||
if (!this.hasSourceObjectAbility(game, before, event)) { // the permanent does not have the ability so no trigger
|
||||
return false;
|
||||
}
|
||||
// check now it is in graveyard
|
||||
if (before.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(sourceId)) {
|
||||
// check now it is in graveyard if it is no token
|
||||
if (!(before instanceof PermanentToken) && before.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(sourceId)) {
|
||||
Zone after = game.getState().getZone(sourceId);
|
||||
return after != null && Zone.GRAVEYARD.match(after);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.watchers.common.CardsAmountDrawnThisTurnWatcher;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class DrawSecondCardTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private boolean triggeredOnce = false;
|
||||
|
||||
public DrawSecondCardTriggeredAbility(Effect effect, boolean optional) {
|
||||
super(Zone.ALL, effect, optional);
|
||||
this.addWatcher(new CardsAmountDrawnThisTurnWatcher());
|
||||
}
|
||||
|
||||
private DrawSecondCardTriggeredAbility(final DrawSecondCardTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.triggeredOnce = ability.triggeredOnce;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DREW_CARD
|
||||
|| event.getType() == GameEvent.EventType.END_PHASE_POST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.END_PHASE_POST) {
|
||||
triggeredOnce = false;
|
||||
return false;
|
||||
}
|
||||
if (event.getType() != GameEvent.EventType.DREW_CARD
|
||||
|| !event.getPlayerId().equals(controllerId)
|
||||
|| game.getPermanent(sourceId) == null) {
|
||||
return false;
|
||||
}
|
||||
if (triggeredOnce) {
|
||||
return false;
|
||||
}
|
||||
CardsAmountDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsAmountDrawnThisTurnWatcher.class);
|
||||
if (watcher == null) {
|
||||
return false;
|
||||
}
|
||||
if (watcher.getAmountCardsDrawn(controllerId) > 1) {
|
||||
triggeredOnce = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever you draw your second card each turn, " + super.getRule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DrawSecondCardTriggeredAbility copy() {
|
||||
return new DrawSecondCardTriggeredAbility(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class EntersBattlefieldUntappedTriggeredAbility extends EntersBattlefieldTriggeredAbility {
|
||||
|
||||
public EntersBattlefieldUntappedTriggeredAbility(Effect effect, boolean optional) {
|
||||
super(effect, optional);
|
||||
this.noRule = true;
|
||||
}
|
||||
|
||||
private EntersBattlefieldUntappedTriggeredAbility(final EntersBattlefieldUntappedTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntersBattlefieldUntappedTriggeredAbility copy() {
|
||||
return new EntersBattlefieldUntappedTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (!super.checkTrigger(event, game)) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
return permanent != null && !permanent.isTapped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "When {this} enters the battlefield untapped, " + super.getRule();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.effects.EntersBattlefieldEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.EscapeAbility;
|
||||
import mage.constants.AbilityType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class EscapesWithAbility extends EntersBattlefieldAbility {
|
||||
|
||||
private final int counters;
|
||||
|
||||
public EscapesWithAbility(int counters) {
|
||||
super(new EscapesWithEffect(counters), false);
|
||||
this.counters = counters;
|
||||
}
|
||||
|
||||
private EscapesWithAbility(final EscapesWithAbility ability) {
|
||||
super(ability);
|
||||
this.counters = ability.counters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EscapesWithAbility copy() {
|
||||
return new EscapesWithAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "{this} escapes with " + CardUtil.numberToText(counters, "a")
|
||||
+ " +1/+1 counter" + (counters > 1 ? 's' : "") + " on it.";
|
||||
}
|
||||
}
|
||||
|
||||
class EscapesWithEffect extends OneShotEffect {
|
||||
|
||||
private final int counter;
|
||||
|
||||
EscapesWithEffect(int counter) {
|
||||
super(Outcome.BoostCreature);
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
private EscapesWithEffect(final EscapesWithEffect effect) {
|
||||
super(effect);
|
||||
this.counter = effect.counter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent == null && source.getAbilityType() == AbilityType.STATIC) {
|
||||
permanent = game.getPermanentEntering(source.getSourceId());
|
||||
}
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
SpellAbility spellAbility = (SpellAbility) getValue(EntersBattlefieldEffect.SOURCE_CAST_SPELL_ABILITY);
|
||||
if (!(spellAbility instanceof EscapeAbility)
|
||||
|| !spellAbility.getSourceId().equals(source.getSourceId())
|
||||
|| permanent.getZoneChangeCounter(game) != spellAbility.getSourceObjectZoneChangeCounter()
|
||||
|| !spellAbility.getSourceId().equals(source.getSourceId())) {
|
||||
return false;
|
||||
}
|
||||
List<UUID> appliedEffects = (ArrayList<UUID>) this.getValue("appliedEffects");
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(counter), source, game, appliedEffects);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EscapesWithEffect copy() {
|
||||
return new EscapesWithEffect(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.MageObject;
|
||||
|
|
@ -8,6 +7,7 @@ import mage.constants.SetTargetPointer;
|
|||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
|
|
@ -44,14 +44,18 @@ public class ExploitCreatureTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||
if (event.getTargetId().equals(getSourceId()) && event.getSourceId().equals(getSourceId())) {
|
||||
if (!this.hasSourceObjectAbility(game, source, event)) {
|
||||
return false;
|
||||
Permanent sourcePermanent = null;
|
||||
if (game.getState().getZone(getSourceId()) == Zone.BATTLEFIELD) {
|
||||
sourcePermanent = game.getPermanent(getSourceId());
|
||||
} else {
|
||||
if (game.getShortLivingLKI(getSourceId(), Zone.BATTLEFIELD)) {
|
||||
sourcePermanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD);
|
||||
}
|
||||
this.setControllerId(event.getPlayerId());
|
||||
return true; // if Exploits creature sacrifices itself, exploit triggers
|
||||
}
|
||||
return super.isInUseableZone(game, source, event);
|
||||
if (sourcePermanent == null) {
|
||||
return false;
|
||||
}
|
||||
return hasSourceObjectAbility(game, sourcePermanent, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
|
|
@ -10,6 +11,7 @@ import mage.constants.Zone;
|
|||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
|
|
@ -30,7 +32,8 @@ public class GodEternalDiesTriggeredAbility extends TriggeredAbilityImpl {
|
|||
if (event.getType() == GameEvent.EventType.ZONE_CHANGE) {
|
||||
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
||||
return zEvent.getFromZone() == Zone.BATTLEFIELD
|
||||
&& (zEvent.getToZone() == Zone.GRAVEYARD || zEvent.getToZone() == Zone.EXILED);
|
||||
&& (zEvent.getToZone() == Zone.GRAVEYARD
|
||||
|| zEvent.getToZone() == Zone.EXILED);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -46,6 +49,22 @@ public class GodEternalDiesTriggeredAbility extends TriggeredAbilityImpl {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
||||
Permanent sourcePermanent = null;
|
||||
if (game.getState().getZone(getSourceId()) == Zone.BATTLEFIELD) {
|
||||
sourcePermanent = game.getPermanent(getSourceId());
|
||||
} else {
|
||||
if (game.getShortLivingLKI(getSourceId(), Zone.BATTLEFIELD)) {
|
||||
sourcePermanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD);
|
||||
}
|
||||
}
|
||||
if (sourcePermanent == null) {
|
||||
return false;
|
||||
}
|
||||
return hasSourceObjectAbility(game, sourcePermanent, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GodEternalDiesTriggeredAbility copy() {
|
||||
return new GodEternalDiesTriggeredAbility(this);
|
||||
|
|
@ -53,8 +72,8 @@ public class GodEternalDiesTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "When {this} dies or is put into exile from the battlefield, " +
|
||||
"you may put it into its owner's library third from the top.";
|
||||
return "When {this} dies or is put into exile from the battlefield, "
|
||||
+ "you may put it into its owner's library third from the top.";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,4 +108,4 @@ class GodEternalEffect extends OneShotEffect {
|
|||
}
|
||||
return player.putCardOnTopXOfLibrary(card, game, source, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.effects.Effect;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import mage.game.events.GameEvent;
|
|||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public class OpponentPlaysLandTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
|
@ -34,7 +33,7 @@ public class OpponentPlaysLandTriggeredAbility extends TriggeredAbilityImpl {
|
|||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
Permanent land = game.getPermanent(event.getTargetId());
|
||||
return game.getOpponents(controllerId).contains(land.getControllerId());
|
||||
return land != null && game.getOpponents(controllerId).contains(land.getControllerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.MageObject;
|
||||
|
|
@ -14,7 +12,6 @@ import mage.game.permanent.Permanent;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
|
@ -27,7 +24,7 @@ public class TurnedFaceUpAllTriggeredAbility extends TriggeredAbilityImpl {
|
|||
this(effect, filter, false);
|
||||
}
|
||||
|
||||
public TurnedFaceUpAllTriggeredAbility(Effect effect, FilterPermanent filter, boolean setTargetPointer) {
|
||||
public TurnedFaceUpAllTriggeredAbility(Effect effect, FilterPermanent filter, boolean setTargetPointer) {
|
||||
this(Zone.BATTLEFIELD, effect, filter, setTargetPointer, false);
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +57,7 @@ public class TurnedFaceUpAllTriggeredAbility extends TriggeredAbilityImpl {
|
|||
if (!event.getTargetId().equals(getSourceId())) {
|
||||
MageObject sourceObj = this.getSourceObject(game);
|
||||
if (sourceObj != null) {
|
||||
if (sourceObj instanceof Card && ((Card)sourceObj).isFaceDown(game)) {
|
||||
if (sourceObj instanceof Card && ((Card) sourceObj).isFaceDown(game)) {
|
||||
// if face down and it's not itself that is turned face up, it does not trigger
|
||||
return false;
|
||||
}
|
||||
|
|
@ -70,9 +67,9 @@ public class TurnedFaceUpAllTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
}
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
if (filter.match(permanent, getSourceId(), getControllerId(), game)) {
|
||||
if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) {
|
||||
if (setTargetPointer) {
|
||||
for (Effect effect: getEffects()) {
|
||||
for (Effect effect : getEffects()) {
|
||||
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.constants.AbilityType;
|
||||
import mage.constants.ColoredManaSymbol;
|
||||
import mage.game.Game;
|
||||
import mage.watchers.common.ManaSpentToCastWatcher;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public enum AdamantCondition implements Condition {
|
||||
WHITE(ColoredManaSymbol.W),
|
||||
BLUE(ColoredManaSymbol.U),
|
||||
BLACK(ColoredManaSymbol.B),
|
||||
RED(ColoredManaSymbol.R),
|
||||
GREEN(ColoredManaSymbol.G),
|
||||
ANY(null);
|
||||
|
||||
private final ColoredManaSymbol coloredManaSymbol;
|
||||
|
||||
private AdamantCondition(ColoredManaSymbol coloredManaSymbol) {
|
||||
this.coloredManaSymbol = coloredManaSymbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (source.getAbilityType() == AbilityType.SPELL) {
|
||||
if (coloredManaSymbol == null) {
|
||||
return Arrays
|
||||
.stream(ColoredManaSymbol.values())
|
||||
.map(source.getManaCostsToPay().getPayment()::getColor)
|
||||
.anyMatch(i -> i > 2);
|
||||
}
|
||||
return source.getManaCostsToPay().getPayment().getColor(coloredManaSymbol) > 2;
|
||||
}
|
||||
ManaSpentToCastWatcher watcher = game.getState().getWatcher(ManaSpentToCastWatcher.class, source.getSourceId());
|
||||
if (watcher == null) {
|
||||
return false;
|
||||
}
|
||||
Mana payment = watcher.getAndResetLastPayment();
|
||||
if (payment == null) {
|
||||
return false;
|
||||
}
|
||||
if (coloredManaSymbol == null) {
|
||||
return Arrays
|
||||
.stream(ColoredManaSymbol.values())
|
||||
.map(payment::getColor)
|
||||
.anyMatch(i -> i > 2);
|
||||
}
|
||||
return payment.getColor(coloredManaSymbol) > 2;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,23 @@
|
|||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.game.Game;
|
||||
import mage.watchers.common.PlayLandWatcher;
|
||||
|
||||
/**
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public enum PlayLandCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
PlayLandWatcher watcher = game.getState().getWatcher(PlayLandWatcher.class);
|
||||
return watcher != null
|
||||
&& watcher.landPlayed(source.getControllerId());
|
||||
}
|
||||
}
|
||||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.game.Game;
|
||||
import mage.watchers.common.PlayLandWatcher;
|
||||
|
||||
/**
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public enum PlayLandCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (game.getTurn().getPhase() == null) { // only for getFrameColor for River of Tears before game started
|
||||
return false;
|
||||
}
|
||||
PlayLandWatcher watcher = game.getState().getWatcher(PlayLandWatcher.class);
|
||||
return watcher != null
|
||||
&& watcher.landPlayed(source.getControllerId());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
package mage.abilities.costs;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* Interface for abilities that adjust source and only source costs. For the
|
||||
* cases when some permanent adjusts costs of other spells use
|
||||
* {@link mage.abilities.effects.CostModificationEffect}.
|
||||
* <p>
|
||||
* Example of such source costs adjusting:
|
||||
* {@link mage.abilities.keyword.AffinityForArtifactsAbility}
|
||||
*
|
||||
* @author nantuko
|
||||
*/
|
||||
@Deprecated
|
||||
// replace all AdjustingSourceCosts with "extends CostModificationEffectImpl with zone.ALL" (see Affinity example)
|
||||
@FunctionalInterface
|
||||
public interface AdjustingSourceCosts {
|
||||
|
||||
void adjustCosts(Ability ability, Game game);
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ import mage.game.Game;
|
|||
public interface AlternativeSourceCosts {
|
||||
|
||||
/**
|
||||
* Ask the player if he wants to use the alternative costs
|
||||
* Ask the player if they want to use the alternative costs
|
||||
*
|
||||
* @param ability ability the alternative cost is activated for
|
||||
* @param game
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ public class PayLifeCost extends CostImpl {
|
|||
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
|
||||
//118.4. If a cost or effect allows a player to pay an amount of life greater than 0,
|
||||
//the player may do so only if their life total is greater than or equal to the
|
||||
//amount of the payment. If a player pays life, the payment is subtracted from his or
|
||||
//her life total; in other words, the player loses that much life. (Players can always pay 0 life.)
|
||||
//amount of the payment. If a player pays life, the payment is subtracted from their
|
||||
//life total; in other words, the player loses that much life. (Players can always pay 0 life.)
|
||||
int lifeToPayAmount = amount.calculate(game, ability, null);
|
||||
// Paying 0 life is not considered paying any life.
|
||||
if (lifeToPayAmount > 0 && !game.getPlayer(controllerId).canPayLifeCost()) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import mage.constants.ColoredManaSymbol;
|
|||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.Filter;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.game.Game;
|
||||
import mage.players.ManaPool;
|
||||
import mage.players.Player;
|
||||
|
|
@ -117,6 +118,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
}
|
||||
|
||||
Player player = game.getPlayer(controllerId);
|
||||
handleKrrikPhyrexianManaCosts(controllerId, ability, game);
|
||||
if (!player.getManaPool().isForcedToPay()) {
|
||||
assignPayment(game, ability, player.getManaPool(), this);
|
||||
}
|
||||
|
|
@ -166,6 +168,11 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
|
||||
while (manaCostIterator.hasNext()) {
|
||||
ManaCost manaCost = manaCostIterator.next();
|
||||
PhyrexianManaCost tempPhyrexianCost = null;
|
||||
Mana mana = manaCost.getMana();
|
||||
|
||||
FilterMana phyrexianColors = player.getPhyrexianColors();
|
||||
|
||||
if (manaCost instanceof PhyrexianManaCost) {
|
||||
PhyrexianManaCost phyrexianManaCost = (PhyrexianManaCost) manaCost;
|
||||
PayLifeCost payLifeCost = new PayLifeCost(2);
|
||||
|
|
@ -179,6 +186,56 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
|
||||
tempCosts.pay(source, game, source.getSourceId(), player.getId(), false, null);
|
||||
}
|
||||
|
||||
private void handleKrrikPhyrexianManaCosts(UUID payingPlayerId, Ability source, Game game) {
|
||||
Player player = game.getPlayer(payingPlayerId);
|
||||
if (this == null || player == null) {
|
||||
return; // nothing to be done without any mana costs. prevents NRE from occurring here
|
||||
}
|
||||
Iterator<T> manaCostIterator = this.iterator();
|
||||
Costs<PayLifeCost> tempCosts = new CostsImpl<>();
|
||||
|
||||
while (manaCostIterator.hasNext()) {
|
||||
ManaCost manaCost = manaCostIterator.next();
|
||||
Mana mana = manaCost.getMana();
|
||||
PhyrexianManaCost tempPhyrexianCost = null;
|
||||
FilterMana phyrexianColors = player.getPhyrexianColors();
|
||||
|
||||
/* K'rrik, Son of Yawgmoth ability check */
|
||||
if (phyrexianColors != null) {
|
||||
int phyrexianEnabledPips = mana.count(phyrexianColors);
|
||||
if (phyrexianEnabledPips > 0) {
|
||||
/* find which color mana is in the cost and set it in the temp Phyrexian cost */
|
||||
if (phyrexianColors.isWhite() && mana.getWhite() > 0) {
|
||||
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.W);
|
||||
}
|
||||
else if (phyrexianColors.isBlue() && mana.getBlue() > 0) {
|
||||
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.U);
|
||||
}
|
||||
else if (phyrexianColors.isBlack() && mana.getBlack() > 0) {
|
||||
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.B);
|
||||
}
|
||||
else if (phyrexianColors.isRed() && mana.getRed() > 0) {
|
||||
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.R);
|
||||
}
|
||||
else if (phyrexianColors.isGreen() && mana.getGreen() > 0) {
|
||||
tempPhyrexianCost = new PhyrexianManaCost(ColoredManaSymbol.G);
|
||||
}
|
||||
|
||||
if (tempPhyrexianCost != null) {
|
||||
PayLifeCost payLifeCost = new PayLifeCost(2);
|
||||
if (payLifeCost.canPay(source, source.getSourceId(), player.getId(), game)
|
||||
&& player.chooseUse(Outcome.LoseLife, "Pay 2 life (using an active ability) instead of " + tempPhyrexianCost.getBaseText() + '?', source, game)) {
|
||||
manaCostIterator.remove();
|
||||
tempCosts.add(payLifeCost);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tempCosts.pay(source, game, source.getSourceId(), player.getId(), false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManaCosts<T> getUnpaid() {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import mage.abilities.effects.Effects;
|
|||
import mage.constants.EffectType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Adds condition to {@link mage.abilities.effects.ContinuousEffect}. Acts as
|
||||
|
|
@ -34,7 +37,6 @@ public class ConditionalInterveningIfTriggeredAbility extends TriggeredAbilityIm
|
|||
public ConditionalInterveningIfTriggeredAbility(TriggeredAbility ability, Condition condition, String text) {
|
||||
super(ability.getZone(), null);
|
||||
this.ability = ability;
|
||||
this.modes = ability.getModes();
|
||||
this.condition = condition;
|
||||
this.abilityText = text;
|
||||
}
|
||||
|
|
@ -91,6 +93,16 @@ public class ConditionalInterveningIfTriggeredAbility extends TriggeredAbilityIm
|
|||
return ability.getModes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Watcher> getWatchers() {
|
||||
return ability.getWatchers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWatcher(Watcher watcher) {
|
||||
ability.addWatcher(watcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Effects getEffects(Game game, EffectType effectType) {
|
||||
return ability.getEffects(game, effectType);
|
||||
|
|
@ -100,5 +112,4 @@ public class ConditionalInterveningIfTriggeredAbility extends TriggeredAbilityIm
|
|||
public boolean isOptional() {
|
||||
return ability.isOptional();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import mage.abilities.effects.Effects;
|
|||
import mage.constants.EffectType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Adds condition to {@link mage.abilities.effects.ContinuousEffect}. Acts as
|
||||
|
|
@ -34,7 +37,6 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
|
|||
public ConditionalTriggeredAbility(TriggeredAbility ability, Condition condition, String text) {
|
||||
super(ability.getZone(), null);
|
||||
this.ability = ability;
|
||||
this.modes = ability.getModes();
|
||||
this.condition = condition;
|
||||
this.abilityText = text;
|
||||
}
|
||||
|
|
@ -86,6 +88,16 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
|
|||
return ability.getModes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Watcher> getWatchers() {
|
||||
return ability.getWatchers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWatcher(Watcher watcher) {
|
||||
ability.addWatcher(watcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Effects getEffects(Game game, EffectType effectType) {
|
||||
return ability.getEffects(game, effectType);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -25,7 +24,7 @@ public class CardsInAllGraveyardsCount implements DynamicValue {
|
|||
this.filter = filter;
|
||||
}
|
||||
|
||||
public CardsInAllGraveyardsCount(CardsInAllGraveyardsCount dynamicValue) {
|
||||
public CardsInAllGraveyardsCount(final CardsInAllGraveyardsCount dynamicValue) {
|
||||
this.filter = dynamicValue.filter.copy();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
|
|
@ -17,7 +18,7 @@ public class CardsInControllerGraveyardCount implements DynamicValue {
|
|||
private Integer amount;
|
||||
|
||||
public CardsInControllerGraveyardCount() {
|
||||
this(new FilterCard(), 1);
|
||||
this(StaticFilters.FILTER_CARD, 1);
|
||||
}
|
||||
|
||||
public CardsInControllerGraveyardCount(FilterCard filter) {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.constants.ColoredManaSymbol;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Each colored mana symbol (e.g. {U}) in the mana costs of permanents you
|
||||
|
|
@ -20,37 +20,49 @@ import mage.game.permanent.Permanent;
|
|||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class DevotionCount implements DynamicValue {
|
||||
public enum DevotionCount implements DynamicValue {
|
||||
W(ColoredManaSymbol.W),
|
||||
U(ColoredManaSymbol.U),
|
||||
B(ColoredManaSymbol.B),
|
||||
R(ColoredManaSymbol.R),
|
||||
G(ColoredManaSymbol.G),
|
||||
WU(ColoredManaSymbol.W, ColoredManaSymbol.U),
|
||||
WB(ColoredManaSymbol.W, ColoredManaSymbol.B),
|
||||
UB(ColoredManaSymbol.U, ColoredManaSymbol.B),
|
||||
UR(ColoredManaSymbol.U, ColoredManaSymbol.R),
|
||||
BR(ColoredManaSymbol.B, ColoredManaSymbol.R),
|
||||
BG(ColoredManaSymbol.B, ColoredManaSymbol.G),
|
||||
RG(ColoredManaSymbol.R, ColoredManaSymbol.G),
|
||||
RW(ColoredManaSymbol.R, ColoredManaSymbol.W),
|
||||
GW(ColoredManaSymbol.G, ColoredManaSymbol.W),
|
||||
GU(ColoredManaSymbol.G, ColoredManaSymbol.U);
|
||||
|
||||
private ArrayList<ColoredManaSymbol> devotionColors = new ArrayList<>();
|
||||
private final ArrayList<ColoredManaSymbol> devotionColors = new ArrayList<>();
|
||||
private final Hint hint;
|
||||
|
||||
public DevotionCount(ColoredManaSymbol... devotionColor) {
|
||||
DevotionCount(ColoredManaSymbol... devotionColor) {
|
||||
this.devotionColors.addAll(Arrays.asList(devotionColor));
|
||||
}
|
||||
|
||||
public DevotionCount(final DevotionCount dynamicValue) {
|
||||
this.devotionColors = dynamicValue.devotionColors;
|
||||
this.hint = new ValueHint(this.getMessage().replace("your d", "D"), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
int devotion = 0;
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(sourceAbility.getControllerId())) {
|
||||
for (ManaCost manaCost : permanent.getManaCost()) {
|
||||
for (ColoredManaSymbol coloredManaSymbol : devotionColors) {
|
||||
if (manaCost.containsColor(coloredManaSymbol)) {
|
||||
devotion++;
|
||||
break; // count each manaCost maximum of one time (Hybrid don't count for multiple colors of devotion)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return devotion;
|
||||
return game.getBattlefield()
|
||||
.getAllActivePermanents(sourceAbility.getControllerId())
|
||||
.stream()
|
||||
.map(MageObject::getManaCost)
|
||||
.flatMap(Collection::stream)
|
||||
.mapToInt(this::checkCost)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private int checkCost(ManaCost manaCost) {
|
||||
return devotionColors.stream().anyMatch(manaCost::containsColor) ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DevotionCount copy() {
|
||||
return new DevotionCount(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -71,4 +83,8 @@ public class DevotionCount implements DynamicValue {
|
|||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public Hint getHint() {
|
||||
return hint;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,6 +99,6 @@ public class DomainValue implements DynamicValue {
|
|||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "basic land type among lands " + (countTargetPlayer ? "he or she controls" : "you control");
|
||||
return "basic land type among lands " + (countTargetPlayer ? "they control" : "you control");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package mage.abilities.dynamicvalue.common;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
|
|
@ -29,7 +30,10 @@ public class SourcePermanentPowerCount implements DynamicValue {
|
|||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(sourceAbility.getSourceId());
|
||||
Permanent sourcePermanent = game.getPermanent(sourceAbility.getSourceId());
|
||||
if (sourcePermanent == null || sourcePermanent.getZoneChangeCounter(game) > sourceAbility.getSourceObjectZoneChangeCounter()) {
|
||||
sourcePermanent = (Permanent) game.getLastKnownInformation(sourceAbility.getSourceId(), Zone.BATTLEFIELD);
|
||||
}
|
||||
if (sourcePermanent != null
|
||||
&& (allowNegativeValues || sourcePermanent.getPower().getValue() >= 0)) {
|
||||
return sourcePermanent.getPower().getValue();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package mage.abilities.effects;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Duration;
|
||||
|
|
@ -8,8 +7,9 @@ import mage.constants.EffectType;
|
|||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements AsThoughEffect {
|
||||
|
|
@ -29,10 +29,11 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
// affectedControllerId = player to check
|
||||
if (getAsThoughEffectType().equals(AsThoughEffectType.LOOK_AT_FACE_DOWN)) {
|
||||
return applies(objectId, source, playerId, game);
|
||||
} else {
|
||||
return applies(objectId, source, affectedAbility.getControllerId(), game);
|
||||
return applies(objectId, source, playerId, game);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import mage.constants.Duration;
|
|||
import mage.constants.Layer;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.game.Game;
|
||||
import mage.target.targetpointer.TargetPointer;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
|
@ -77,4 +78,7 @@ public interface ContinuousEffect extends Effect {
|
|||
boolean isTemporary();
|
||||
|
||||
void setTemporary(boolean temporary);
|
||||
|
||||
@Override
|
||||
ContinuousEffect setTargetPointer(TargetPointer targetPointer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import mage.abilities.dynamicvalue.common.StaticValue;
|
|||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.TargetPointer;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
|
@ -216,7 +217,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
|
|||
boolean canDelete = false;
|
||||
Player player = game.getPlayer(startingControllerId);
|
||||
|
||||
// discard on start of turn for leave player
|
||||
// discard on start of turn for leaved player
|
||||
// 800.4i When a player leaves the game, any continuous effects with durations that last until that player's next turn
|
||||
// or until a specific point in that turn will last until that turn would have begun.
|
||||
// They neither expire immediately nor last indefinitely.
|
||||
|
|
@ -334,4 +335,10 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
|
|||
dependendToTypes.add(dependencyType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContinuousEffect setTargetPointer(TargetPointer targetPointer) {
|
||||
super.setTargetPointer(targetPointer);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
package mage.abilities.effects;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.*;
|
||||
import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect;
|
||||
import mage.abilities.effects.common.continuous.CommanderReplacementEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.SplitCardHalf;
|
||||
import mage.cards.*;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicate;
|
||||
|
|
@ -26,11 +27,6 @@ import mage.players.Player;
|
|||
import mage.target.common.TargetCardInHand;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
|
|
@ -233,7 +229,9 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
|
||||
private List<ContinuousEffect> filterLayeredEffects(List<ContinuousEffect> effects, Layer layer) {
|
||||
return effects.stream().filter(effect -> effect.hasLayer(layer)).collect(Collectors.toList());
|
||||
return effects.stream()
|
||||
.filter(effect -> effect.hasLayer(layer))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Map<RequirementEffect, Set<Ability>> getApplicableRequirementEffects(Permanent permanent, boolean playerRealted, Game game) {
|
||||
|
|
@ -336,7 +334,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;
|
||||
|
|
@ -369,7 +367,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;
|
||||
|
|
@ -508,9 +506,20 @@ public class ContinuousEffects implements Serializable {
|
|||
UUID idToCheck;
|
||||
if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) affectedAbility.getSourceObject(game)).getParentCard().getId();
|
||||
} else if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof AdventureCardSpell
|
||||
&& type != AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE
|
||||
&& type != AsThoughEffectType.CAST_AS_INSTANT) {
|
||||
// adventure spell uses alternative characteristics for spell/stack
|
||||
idToCheck = ((AdventureCardSpell) affectedAbility.getSourceObject(game)).getParentCard().getId();
|
||||
} else {
|
||||
if (game.getObject(objectId) instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) game.getObject(objectId)).getParentCard().getId();
|
||||
Card card = game.getCard(objectId);
|
||||
if (card instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) card).getParentCard().getId();
|
||||
} else if (card instanceof AdventureCardSpell
|
||||
&& type != AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE
|
||||
&& type != AsThoughEffectType.CAST_AS_INSTANT) {
|
||||
// adventure spell uses alternative characteristics for spell/stack
|
||||
idToCheck = ((AdventureCardSpell) card).getParentCard().getId();
|
||||
} else {
|
||||
idToCheck = objectId;
|
||||
}
|
||||
|
|
@ -712,10 +721,10 @@ public class ContinuousEffects implements Serializable {
|
|||
* Checks if an event won't happen because of an rule modifying effect
|
||||
*
|
||||
* @param event
|
||||
* @param targetAbility ability the event is attached to. can be null.
|
||||
* @param targetAbility ability the event is attached to. can be null.
|
||||
* @param game
|
||||
* @param checkPlayableMode true if the event does not really happen but
|
||||
* it's checked if the event would be replaced
|
||||
* it's checked if the event would be replaced
|
||||
* @return
|
||||
*/
|
||||
public boolean preventedByRuleModification(GameEvent event, Ability targetAbility, Game game, boolean checkPlayableMode) {
|
||||
|
|
@ -729,10 +738,7 @@ public class ContinuousEffects implements Serializable {
|
|||
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
||||
effect.setValue("targetAbility", targetAbility);
|
||||
if (effect.applies(event, sourceAbility, game)) {
|
||||
if (targetAbility instanceof ActivatedAbility && ((ActivatedAbility) targetAbility).isCheckPlayableMode()) {
|
||||
checkPlayableMode = true;
|
||||
}
|
||||
if (!checkPlayableMode) {
|
||||
if (!game.inCheckPlayableState()) {
|
||||
String message = effect.getInfoMessage(sourceAbility, event, game);
|
||||
if (message != null && !message.isEmpty()) {
|
||||
if (effect.sendMessageToUser()) {
|
||||
|
|
@ -762,7 +768,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());
|
||||
|
|
@ -953,7 +959,7 @@ public class ContinuousEffects implements Serializable {
|
|||
|
||||
if (!waitingEffects.isEmpty()) {
|
||||
// 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
|
||||
appliedAbilities = appliedEffectAbilities.get(entry.getKey());
|
||||
|
|
@ -1072,11 +1078,13 @@ public class ContinuousEffects implements Serializable {
|
|||
|
||||
private boolean isAbilityStillExists(final Game game, final Ability ability, ContinuousEffect effect) {
|
||||
final Card card = game.getPermanentOrLKIBattlefield(ability.getSourceId());
|
||||
if (!(effect instanceof BecomesFaceDownCreatureEffect)) {
|
||||
if (!(effect instanceof BecomesFaceDownCreatureEffect)
|
||||
&& (effect != null && !effect.getDuration().equals(Duration.Custom))) { // Custom effects do not depend on the creating permanent
|
||||
if (card != null) {
|
||||
return card.getAbilities(game).contains(ability);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
|
|||
which give that player control of any objects or players end. Then, if that player controlled any objects on the stack
|
||||
not represented by cards, those objects cease to exist. Then, if there are any objects still controlled by that player,
|
||||
those objects are exiled. This is not a state-based action. It happens as soon as the player leaves the game.
|
||||
If the player who left the game had priority at the time he or she left, priority passes to the next player in turn
|
||||
If the player who left the game had priority at the time they left, priority passes to the next player in turn
|
||||
order who’s still in the game.
|
||||
*/
|
||||
// objects removes doing in player.leave() call... effects removes is here
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public interface ContinuousRuleModifyingEffect extends ContinuousEffect {
|
|||
|
||||
/**
|
||||
* Defines if the user should get a message about the rule modifying effect
|
||||
* if he was applied
|
||||
* if it was applied
|
||||
*
|
||||
* @return true if user should be informed
|
||||
*/
|
||||
|
|
@ -40,13 +40,13 @@ public interface ContinuousRuleModifyingEffect extends ContinuousEffect {
|
|||
|
||||
/**
|
||||
* Defines if the a message should be send to game log about the rule modifying effect
|
||||
* if he was applied
|
||||
* if it was applied
|
||||
*
|
||||
* @return true if message should go to game log
|
||||
*/
|
||||
boolean sendMessageToGameLog();
|
||||
/**
|
||||
* Returns a message text that informs the player why he can't do something.
|
||||
* Returns a message text that informs the player why they can't do something.
|
||||
*
|
||||
* @param source the ability of the effect
|
||||
* @param event
|
||||
|
|
|
|||
|
|
@ -13,18 +13,21 @@ import mage.game.events.GameEvent;
|
|||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author antoni-g
|
||||
*/
|
||||
public class PreventDamageAndRemoveCountersEffect extends PreventionEffectImpl {
|
||||
private final boolean thatMany;
|
||||
|
||||
public PreventDamageAndRemoveCountersEffect() {
|
||||
public PreventDamageAndRemoveCountersEffect(boolean thatMany) {
|
||||
super(Duration.WhileOnBattlefield, Integer.MAX_VALUE, false, false);
|
||||
staticText = "If damage would be dealt to {this}, prevent that damage and remove that many +1/+1 counters from it";
|
||||
this.thatMany = thatMany;
|
||||
staticText = "If damage would be dealt to {this} while it has a +1/+1 counter on it, " +
|
||||
"prevent that damage and remove " + (thatMany ? "that many +1/+1 counters" : "a +1/+1 counter") + " from it";
|
||||
}
|
||||
|
||||
public PreventDamageAndRemoveCountersEffect(final PreventDamageAndRemoveCountersEffect effect) {
|
||||
private PreventDamageAndRemoveCountersEffect(final PreventDamageAndRemoveCountersEffect effect) {
|
||||
super(effect);
|
||||
this.thatMany = effect.thatMany;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -42,19 +45,22 @@ public class PreventDamageAndRemoveCountersEffect extends PreventionEffectImpl {
|
|||
int damage = event.getAmount();
|
||||
preventDamageAction(event, source, game);
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
permanent.removeCounters(CounterType.P1P1.createInstance(damage), game); //MTG ruling (this) loses counters even if the damage isn't prevented
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
if (!thatMany) {
|
||||
damage = 1;
|
||||
}
|
||||
permanent.removeCounters(CounterType.P1P1.createInstance(damage), game); //MTG ruling (this) loses counters even if the damage isn't prevented
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (super.applies(event, source, game)) {
|
||||
if (event.getTargetId().equals(source.getSourceId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
return super.applies(event, source, game)
|
||||
&& permanent != null
|
||||
&& event.getTargetId().equals(source.getSourceId())
|
||||
&& permanent.getCounters(game).containsKey(CounterType.P1P1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ public class AffinityEffect extends CostModificationEffectImpl {
|
|||
SpellAbility spellAbility = (SpellAbility)abilityToModify;
|
||||
Mana mana = spellAbility.getManaCostsToPay().getMana();
|
||||
if (mana.getGeneric() > 0) {
|
||||
int count = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game);
|
||||
// the following works with Sen Triplets and in multiplayer games
|
||||
int count = game.getBattlefield().getActivePermanents(filter, abilityToModify.getControllerId(), source.getId(), game).size();
|
||||
int newCount = mana.getGeneric() - count;
|
||||
if (newCount < 0) {
|
||||
newCount = 0;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.MageSingleton;
|
||||
|
|
@ -20,6 +18,8 @@ import mage.players.PlayerList;
|
|||
import mage.target.Target;
|
||||
import mage.target.common.TargetOpponent;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
|
||||
/**
|
||||
* 1. The controller of the spell or ability chooses an opponent. (This doesn't
|
||||
* target the opponent.) 2. Each player involved in the clash reveals the top
|
||||
|
|
@ -28,15 +28,15 @@ import mage.target.common.TargetOpponent;
|
|||
* their revealed card on either the top or bottom of their library.
|
||||
* (Note that the player whose turn it is does this first, not necessarily the
|
||||
* controller of the clash spell or ability.) When the second player makes this
|
||||
* decision, he or she will know what the first player chose. Then all cards are
|
||||
* decision, they will know what the first player chose. Then all cards are
|
||||
* moved at the same time. 5. The clash is over. If one player in the clash
|
||||
* revealed a card with a higher converted mana cost than all other cards
|
||||
* revealed in the clash, that player wins the clash. 6. If any abilities
|
||||
* trigger when a player clashes, they trigger and wait to be put on the stack.
|
||||
* 7. The clash spell or ability finishes resolving. That usually involves a
|
||||
* bonus gained by the controller of the clash spell or ability if he or she won
|
||||
* bonus gained by the controller of the clash spell or ability if they won
|
||||
* the clash. 8. Abilities that triggered during the clash are put on the stack.
|
||||
*
|
||||
* <p>
|
||||
* There are no draws or losses in a clash. Either you win it or you don't. Each
|
||||
* spell or ability with clash says what happens if you (the controller of that
|
||||
* spell or ability) win the clash. Typically, if you don't win the clash,
|
||||
|
|
@ -148,7 +148,7 @@ public class ClashEffect extends OneShotEffect implements MageSingleton {
|
|||
if (cardOpponent != null && current.getId().equals(opponent.getId())) {
|
||||
topOpponent = current.chooseUse(Outcome.Detriment, "Put " + cardOpponent.getLogName() + " back on top of your library? (otherwise it goes to bottom)", source, game);
|
||||
}
|
||||
nextPlayer = playerList.getNext(game);
|
||||
nextPlayer = playerList.getNext(game, false);
|
||||
} while (nextPlayer != null && !nextPlayer.getId().equals(game.getActivePlayerId()));
|
||||
// put the cards back to library
|
||||
if (cardController != null) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.MageItem;
|
||||
|
|
@ -11,6 +10,7 @@ import mage.filter.FilterImpl;
|
|||
import mage.filter.FilterInPlay;
|
||||
import mage.filter.predicate.mageobject.FromSetPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
|
|
@ -29,7 +29,8 @@ public abstract class CopySpellForEachItCouldTargetEffect<T extends MageItem> ex
|
|||
|
||||
public CopySpellForEachItCouldTargetEffect(FilterInPlay<T> filter) {
|
||||
super(Outcome.Copy);
|
||||
this.staticText = "copy the spell for each other " + filter.getMessage() + " that spell could target. Each copy targets a different one";
|
||||
this.staticText = "copy the spell for each other " + filter.getMessage()
|
||||
+ " that spell could target. Each copy targets a different one";
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +68,8 @@ public abstract class CopySpellForEachItCouldTargetEffect<T extends MageItem> ex
|
|||
for (TargetAddress addr : TargetAddress.walk(spell)) {
|
||||
Target targetInstance = addr.getTarget(spell);
|
||||
if (targetInstance.getNumberOfTargets() > 1) {
|
||||
throw new UnsupportedOperationException("Changing Target instances with multiple targets is unsupported");
|
||||
throw new UnsupportedOperationException("Changing Target instances "
|
||||
+ "with multiple targets is unsupported");
|
||||
}
|
||||
if (changeTarget(targetInstance, game, source)) {
|
||||
targetsToBeChanged.add(addr);
|
||||
|
|
@ -142,25 +144,28 @@ public abstract class CopySpellForEachItCouldTargetEffect<T extends MageItem> ex
|
|||
FilterInPlay<T> setFilter = filter.copy();
|
||||
setFilter.add(new FromSetPredicate(targetCopyMap.keySet()));
|
||||
Target target = new TargetWithAdditionalFilter(sampleTarget, setFilter);
|
||||
target.setNotTarget(false); // it is targeted, not chosen
|
||||
target.setMinNumberOfTargets(0);
|
||||
target.setMaxNumberOfTargets(1);
|
||||
target.setTargetName(filter.getMessage() + " that " + spell.getLogName() + " could target (" + targetCopyMap.size() + " remaining)");
|
||||
|
||||
target.setTargetName(filter.getMessage() + " that " + spell.getLogName()
|
||||
+ " could target (" + targetCopyMap.size() + " remaining)");
|
||||
// shortcut if there's only one possible target remaining
|
||||
if (targetCopyMap.size() > 1
|
||||
&& target.canChoose(spell.getId(), player.getId(), game)) {
|
||||
player.choose(Outcome.Neutral, target, spell.getId(), game);
|
||||
// The original "source" is not applicable here due to the spell being a copy. ie: Zada, Hedron Grinder
|
||||
player.chooseTarget(Outcome.Neutral, target, spell.getSpellAbility(), game); // not source, but the spell that is copied
|
||||
}
|
||||
Collection<UUID> chosenIds = target.getTargets();
|
||||
if (chosenIds.isEmpty()) {
|
||||
chosenIds = targetCopyMap.keySet();
|
||||
}
|
||||
|
||||
List<UUID> toDelete = new ArrayList<>();
|
||||
for (UUID chosenId : chosenIds) {
|
||||
Spell chosenCopy = targetCopyMap.get(chosenId);
|
||||
if (chosenCopy != null) {
|
||||
game.getStack().push(chosenCopy);
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.COPIED_STACKOBJECT,
|
||||
chosenCopy.getId(), spell.getId(), source.getControllerId()));
|
||||
toDelete.add(chosenId);
|
||||
madeACopy = true;
|
||||
}
|
||||
|
|
@ -254,8 +259,8 @@ class TargetWithAdditionalFilter<T extends MageItem> extends TargetImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getMaxNumberOfTargets() {
|
||||
return originalTarget.getMaxNumberOfTargets();
|
||||
public int getMinNumberOfTargets() {
|
||||
return originalTarget.getMinNumberOfTargets();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -263,6 +268,11 @@ class TargetWithAdditionalFilter<T extends MageItem> extends TargetImpl {
|
|||
originalTarget.setMinNumberOfTargets(minNumberOfTargets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxNumberOfTargets() {
|
||||
return originalTarget.getMaxNumberOfTargets();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxNumberOfTargets(int maxNumberOfTargets) {
|
||||
originalTarget.setMaxNumberOfTargets(maxNumberOfTargets);
|
||||
|
|
@ -323,7 +333,8 @@ class TargetWithAdditionalFilter<T extends MageItem> extends TargetImpl {
|
|||
|
||||
@Override
|
||||
public FilterInPlay<T> getFilter() {
|
||||
return new CompoundFilter((FilterInPlay<T>) originalTarget.getFilter(), additionalFilter, originalTarget.getFilter().getMessage());
|
||||
return new CompoundFilter((FilterInPlay<T>) originalTarget.getFilter(),
|
||||
additionalFilter, originalTarget.getFilter().getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
|
||||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author JRHerlehy
|
||||
*/
|
||||
|
|
@ -29,7 +26,9 @@ public abstract class CouncilsDilemmaVoteEffect extends OneShotEffect {
|
|||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
if (player.chooseUse(Outcome.Vote, "Choose " + choiceOne + '?', source, game)) {
|
||||
if (player.chooseUse(Outcome.Vote,
|
||||
"Choose " + choiceOne + " or " + choiceTwo + "?",
|
||||
source.getRule(), choiceOne, choiceTwo, source, game)) {
|
||||
voteOneCount++;
|
||||
game.informPlayers(player.getName() + " has voted for " + choiceOne);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import mage.abilities.Mode;
|
|||
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
|
||||
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
|
|
@ -136,6 +135,8 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
} else {
|
||||
permanent = game.getPermanentOrLKIBattlefield(targetId);
|
||||
}
|
||||
|
||||
// can target card or permanent
|
||||
Card copyFrom;
|
||||
ApplyToPermanent applier = new EmptyApplyToPermanent();
|
||||
if (permanent != null) {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public class DrawDiscardTargetEffect extends OneShotEffect {
|
|||
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
if (player != null) {
|
||||
player.drawCards(cardsToDraw, game);
|
||||
player.discard(cardsToDiscard, source, game);
|
||||
player.discard(cardsToDiscard, false, source, game);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import mage.players.Player;
|
|||
* you can't cast spells,” and “At the beginning of each of your upkeeps for the
|
||||
* rest of the game, copy this spell except for its epic ability. If the spell
|
||||
* has any targets, you may choose new targets for the copy.” See rule 706.10.
|
||||
* 702.49b A player can't cast spells once a spell with epic he or she controls
|
||||
* 702.49b A player can't cast spells once a spell with epic they control
|
||||
* resolves, but effects (such as the epic ability itself) can still put copies
|
||||
* of spells onto the stack. *
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.AdventureCardSpell;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author phulin
|
||||
*/
|
||||
public class ExileAdventureSpellEffect extends OneShotEffect implements MageSingleton {
|
||||
|
||||
private static final ExileAdventureSpellEffect instance = new ExileAdventureSpellEffect();
|
||||
|
||||
public static ExileAdventureSpellEffect getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static UUID adventureExileId(UUID controllerId, Game game) {
|
||||
return CardUtil.getExileZoneId(controllerId.toString() + "- On an Adventure", game);
|
||||
}
|
||||
|
||||
private ExileAdventureSpellEffect() {
|
||||
super(Outcome.Exile);
|
||||
staticText = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExileAdventureSpellEffect copy() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
Spell spell = game.getStack().getSpell(source.getId());
|
||||
if (spell != null && !spell.isCopy()) {
|
||||
Card spellCard = spell.getCard();
|
||||
if (spellCard instanceof AdventureCardSpell) {
|
||||
UUID exileId = adventureExileId(controller.getId(), game);
|
||||
game.getExile().createZone(exileId, "On an Adventure");
|
||||
AdventureCardSpell adventureSpellCard = (AdventureCardSpell) spellCard;
|
||||
Card parentCard = adventureSpellCard.getParentCard();
|
||||
if (controller.moveCardsToExile(parentCard, source, game, true, exileId, "On an Adventure")) {
|
||||
ContinuousEffect effect = new AdventureCastFromExileEffect();
|
||||
effect.setTargetPointer(new FixedTarget(parentCard.getId(), game));
|
||||
game.addEffect(effect, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class AdventureCastFromExileEffect extends AsThoughEffectImpl {
|
||||
|
||||
public AdventureCastFromExileEffect() {
|
||||
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
|
||||
staticText = "Then exile this card. You may cast the creature later from exile.";
|
||||
}
|
||||
|
||||
public AdventureCastFromExileEffect(final AdventureCastFromExileEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdventureCastFromExileEffect copy() {
|
||||
return new AdventureCastFromExileEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
UUID targetId = getTargetPointer().getFirst(game, source);
|
||||
ExileZone adventureExileZone = game.getExile().getExileZone(ExileAdventureSpellEffect.adventureExileId(affectedControllerId, game));
|
||||
if (targetId == null) {
|
||||
this.discard();
|
||||
} else if (objectId.equals(targetId)
|
||||
&& affectedControllerId.equals(source.getControllerId())
|
||||
&& adventureExileZone.contains(objectId)) {
|
||||
Card card = game.getCard(objectId);
|
||||
return card != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ public class ExileReturnBattlefieldOwnerNextEndStepSourceEffect extends OneShotE
|
|||
int zcc = game.getState().getZoneChangeCounter(permanent.getId());
|
||||
boolean exiled = controller.moveCardToExileWithInfo(permanent, source.getSourceId(), permanent.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true);
|
||||
if (exiled || (returnAlways && (zcc == game.getState().getZoneChangeCounter(permanent.getId()) - 1))) {
|
||||
//create delayed triggered ability and return it from every public zone he was next moved to
|
||||
//create delayed triggered ability and return it from every public zone it was next moved to
|
||||
AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(
|
||||
new ReturnToBattlefieldUnderOwnerControlSourceEffect(returnTapped, zcc + 1));
|
||||
game.addDelayedTriggeredAbility(delayedAbility, source);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import mage.cards.Card;
|
|||
import mage.constants.Outcome;
|
||||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
|
|
@ -34,8 +35,14 @@ public class HideawayPlayEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
ExileZone zone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source));
|
||||
if (zone == null || zone.isEmpty()) {
|
||||
ExileZone zone = null;
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
zone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), permanent.getZoneChangeCounter(game)));
|
||||
}
|
||||
|
||||
if (zone == null
|
||||
|| zone.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
Card card = zone.getCards(game).iterator().next();
|
||||
|
|
|
|||
|
|
@ -1,44 +1,46 @@
|
|||
|
||||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.PreventionEffectImpl;
|
||||
import mage.constants.Duration;
|
||||
import mage.filter.FilterInPlay;
|
||||
import mage.filter.common.FilterCreatureOrPlayer;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.FilterPlayer;
|
||||
import mage.filter.common.FilterPermanentOrPlayer;
|
||||
import mage.filter.predicate.other.PlayerIdPredicate;
|
||||
import mage.filter.predicate.permanent.PermanentIdPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class PreventAllDamageToAllEffect extends PreventionEffectImpl {
|
||||
|
||||
protected FilterInPlay filter;
|
||||
|
||||
public PreventAllDamageToAllEffect(Duration duration, FilterCreaturePermanent filter) {
|
||||
this(duration, createFilter(filter));
|
||||
protected FilterPermanentOrPlayer filter;
|
||||
|
||||
public PreventAllDamageToAllEffect(Duration duration, FilterPermanent filterPermanent) {
|
||||
this(duration, createFilter(filterPermanent, null));
|
||||
}
|
||||
|
||||
public PreventAllDamageToAllEffect(Duration duration, FilterInPlay filter) {
|
||||
public PreventAllDamageToAllEffect(Duration duration, FilterPermanent filterPermanent, boolean onlyCombat) {
|
||||
this(duration, createFilter(filterPermanent, null), onlyCombat);
|
||||
}
|
||||
|
||||
public PreventAllDamageToAllEffect(Duration duration, FilterPermanentOrPlayer filter) {
|
||||
this(duration, filter, false);
|
||||
}
|
||||
|
||||
public PreventAllDamageToAllEffect(Duration duration, FilterInPlay filter, boolean onlyCombat) {
|
||||
public PreventAllDamageToAllEffect(Duration duration, FilterPermanentOrPlayer filter, boolean onlyCombat) {
|
||||
super(duration, Integer.MAX_VALUE, onlyCombat);
|
||||
this.filter = filter;
|
||||
staticText = "Prevent all "
|
||||
+ (onlyCombat ? "combat ":"")
|
||||
+ "damage that would be dealt to "
|
||||
+ (onlyCombat ? "combat " : "")
|
||||
+ "damage that would be dealt to "
|
||||
+ filter.getMessage()
|
||||
+ (duration.toString().isEmpty() ?"": ' ' + duration.toString());
|
||||
+ (duration.toString().isEmpty() ? "" : ' ' + duration.toString());
|
||||
}
|
||||
|
||||
public PreventAllDamageToAllEffect(final PreventAllDamageToAllEffect effect) {
|
||||
|
|
@ -46,13 +48,25 @@ public class PreventAllDamageToAllEffect extends PreventionEffectImpl {
|
|||
this.filter = effect.filter.copy();
|
||||
}
|
||||
|
||||
private static FilterInPlay createFilter(FilterCreaturePermanent filter) {
|
||||
FilterCreatureOrPlayer newfilter = new FilterCreatureOrPlayer(filter.getMessage());
|
||||
newfilter.setCreatureFilter(filter);
|
||||
newfilter.getPlayerFilter().add(new PlayerIdPredicate(UUID.randomUUID()));
|
||||
return newfilter;
|
||||
private static FilterPermanentOrPlayer createFilter(FilterPermanent filterPermanent, FilterPlayer filterPlayer) {
|
||||
String message = String.join(
|
||||
" and ",
|
||||
filterPermanent != null ? filterPermanent.getMessage() : "",
|
||||
filterPlayer != null ? filterPlayer.getMessage() : "");
|
||||
FilterPermanent filter1 = filterPermanent;
|
||||
if (filter1 == null) {
|
||||
filter1 = new FilterPermanent();
|
||||
filter1.add(new PermanentIdPredicate(UUID.randomUUID())); // disable filter
|
||||
}
|
||||
FilterPlayer filter2 = filterPlayer;
|
||||
if (filter2 == null) {
|
||||
filter2 = new FilterPlayer();
|
||||
filter2.add(new PlayerIdPredicate(UUID.randomUUID())); // disable filter
|
||||
}
|
||||
|
||||
return new FilterPermanentOrPlayer(message, filter1, filter2);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PreventAllDamageToAllEffect copy() {
|
||||
return new PreventAllDamageToAllEffect(this);
|
||||
|
|
@ -66,17 +80,9 @@ public class PreventAllDamageToAllEffect extends PreventionEffectImpl {
|
|||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (super.applies(event, source, game)) {
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
if (permanent != null) {
|
||||
if (filter.match(permanent, source.getSourceId(), source.getControllerId(), game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Player player = game.getPlayer(event.getTargetId());
|
||||
if (player != null && filter.match(player, source.getSourceId(), source.getControllerId(), game)) {
|
||||
return true;
|
||||
}
|
||||
MageObject object = game.getObject(event.getTargetId());
|
||||
if (object != null) {
|
||||
return filter.match(object, source.getSourceId(), source.getControllerId(), game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -9,33 +7,27 @@ import mage.game.Game;
|
|||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public class PreventCombatDamageBySourceEffect extends PreventionEffectImpl {
|
||||
|
||||
public PreventCombatDamageBySourceEffect(Duration duration) {
|
||||
super(duration, Integer.MAX_VALUE, true);
|
||||
staticText = "Prevent all combat damage that would be dealt by {this}" + duration.toString();
|
||||
super(duration, Integer.MAX_VALUE, true);
|
||||
staticText = "Prevent all combat damage that would be dealt by {this}" + duration.toString();
|
||||
}
|
||||
|
||||
public PreventCombatDamageBySourceEffect(final PreventCombatDamageBySourceEffect effect) {
|
||||
super(effect);
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreventCombatDamageBySourceEffect copy() {
|
||||
return new PreventCombatDamageBySourceEffect(this);
|
||||
return new PreventCombatDamageBySourceEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (super.applies(event, source, game)) {
|
||||
if (event.getSourceId().equals(source.getSourceId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return super.applies(event, source, game)
|
||||
&& event.getSourceId().equals(source.getSourceId());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
|
|
@ -10,14 +7,19 @@ import mage.abilities.dynamicvalue.DynamicValue;
|
|||
import mage.abilities.effects.PreventionEffectImpl;
|
||||
import mage.constants.Duration;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamageEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.PreventDamageEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.TargetAmount;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class PreventDamageToTargetMultiAmountEffect extends PreventionEffectImpl {
|
||||
|
|
@ -77,7 +79,7 @@ public class PreventDamageToTargetMultiAmountEffect extends PreventionEffectImpl
|
|||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
int targetAmount = targetAmountMap.get(event.getTargetId());
|
||||
GameEvent preventEvent = new GameEvent(GameEvent.EventType.PREVENT_DAMAGE, event.getTargetId(), source.getSourceId(), source.getControllerId(), event.getAmount(), false);
|
||||
GameEvent preventEvent = new PreventDamageEvent(event.getTargetId(), source.getSourceId(), source.getControllerId(), event.getAmount(), ((DamageEvent) event).isCombatDamage());
|
||||
if (!game.replaceEvent(preventEvent)) {
|
||||
if (event.getAmount() >= targetAmount) {
|
||||
int damage = targetAmount;
|
||||
|
|
|
|||
|
|
@ -82,9 +82,9 @@ public class PutCardFromHandOntoBattlefieldEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
if (useTargetController) {
|
||||
return "that player may put " + filter.getMessage() + " from their hand onto the battlefield";
|
||||
return "that player may put " + filter.getMessage() + " from their hand onto the battlefield" + (this.tapped ? " tapped" : "");
|
||||
} else {
|
||||
return "you may put " + filter.getMessage() + " from your hand onto the battlefield";
|
||||
return "you may put " + filter.getMessage() + " from your hand onto the battlefield" + (this.tapped ? " tapped" : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.constants.Outcome;
|
||||
|
|
@ -34,8 +32,9 @@ public class ReturnToHandAttachedEffect extends OneShotEffect {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Object object = getValue("attachedTo");
|
||||
if (object instanceof Permanent) {
|
||||
Card card = game.getCard(((Permanent)object).getId());
|
||||
if (card != null) {
|
||||
Card card = game.getCard(((Permanent) object).getId());
|
||||
if (card != null
|
||||
&& getValue("zcc").equals(game.getState().getZoneChangeCounter(card.getId()))) { // Necrogenesis, etc.
|
||||
if (card.moveToZone(Zone.HAND, source.getSourceId(), game, false)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ public class ReturnToHandChosenPermanentEffect extends OneShotEffect {
|
|||
sb.append(CardUtil.numberToText(number, "a"));
|
||||
}
|
||||
sb.append(' ').append(filter.getMessage());
|
||||
sb.append(" he or she controls");
|
||||
sb.append(" they control");
|
||||
if (number > 1) {
|
||||
sb.append(" to their owner's hand");
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -68,9 +67,10 @@ public class ReturnToHandTargetEffect extends OneShotEffect {
|
|||
for (UUID targetId : targetPointer.getTargets(game, source)) {
|
||||
MageObject mageObject = game.getObject(targetId);
|
||||
if (mageObject != null) {
|
||||
if (mageObject instanceof Spell && mageObject.isCopy()) {
|
||||
if (mageObject instanceof Spell
|
||||
&& mageObject.isCopy()) {
|
||||
copyIds.add(targetId);
|
||||
} else {
|
||||
} else if (mageObject instanceof Card) {
|
||||
cards.add((Card) mageObject);
|
||||
}
|
||||
}
|
||||
|
|
@ -83,7 +83,8 @@ public class ReturnToHandTargetEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
public String getText(Mode mode
|
||||
) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
|
|
@ -93,7 +94,8 @@ public class ReturnToHandTargetEffect extends OneShotEffect {
|
|||
Target target = mode.getTargets().get(0);
|
||||
StringBuilder sb = new StringBuilder("return ");
|
||||
if (target.getNumberOfTargets() == 0 && target.getMaxNumberOfTargets() > 0) {
|
||||
sb.append("up to ").append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(" target ").append(target.getTargetName()).append(" to their owners' hand");
|
||||
sb.append("up to ").append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(" target ")
|
||||
.append(target.getTargetName()).append(" to their owners' hand");
|
||||
return sb.toString();
|
||||
} else {
|
||||
if (target.getNumberOfTargets() > 1) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.Set;
|
||||
|
|
@ -73,7 +72,7 @@ public class RevealLibraryPutIntoHandEffect extends OneShotEffect {
|
|||
Set<Card> cardsList = cards.getCards(game);
|
||||
Cards cardsToHand = new CardsImpl();
|
||||
for (Card card : cardsList) {
|
||||
if (filter.match(card, game)) {
|
||||
if (filter.match(card, source.getSourceId(), controller.getId(), game)) {
|
||||
cardsToHand.add(card);
|
||||
cards.remove(card);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
|
|
@ -16,8 +13,11 @@ import mage.players.Player;
|
|||
import mage.target.common.TargetControlledPermanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class SacrificeAllEffect extends OneShotEffect {
|
||||
|
|
@ -86,10 +86,10 @@ public class SacrificeAllEffect extends OneShotEffect {
|
|||
sb.append("each player sacrifices ");
|
||||
if (amount.toString().equals("X")) {
|
||||
sb.append(amount.toString());
|
||||
} else {
|
||||
sb.append(CardUtil.numberToText(amount.toString(), "a"));
|
||||
sb.append(' ');
|
||||
} else if (!filter.getMessage().startsWith("a ")) {
|
||||
sb.append(CardUtil.numberToText(amount.toString(), "a "));
|
||||
}
|
||||
sb.append(' ');
|
||||
sb.append(filter.getMessage());
|
||||
staticText = sb.toString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ public class SacrificeOpponentsUnlessPayEffect extends OneShotEffect {
|
|||
sb.append(' ');
|
||||
sb.append(filter.getMessage());
|
||||
|
||||
sb.append(" unless he or she pays ");
|
||||
sb.append(" unless they pay ");
|
||||
|
||||
if (cost != null) {
|
||||
sb.append(cost.getText());
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
|
@ -16,8 +13,10 @@ import mage.game.Game;
|
|||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Styxo
|
||||
*/
|
||||
public class WishEffect extends OneShotEffect {
|
||||
|
|
@ -73,7 +72,7 @@ public class WishEffect extends OneShotEffect {
|
|||
if (controller.chooseUse(Outcome.Benefit, choiceText, source, game)) {
|
||||
Cards cards = controller.getSideboard();
|
||||
List<Card> exile = game.getExile().getAllCards(game);
|
||||
boolean noTargets = cards.isEmpty() && (alsoFromExile ? exile.isEmpty() : true);
|
||||
boolean noTargets = cards.isEmpty() && (!alsoFromExile || exile.isEmpty());
|
||||
if (noTargets) {
|
||||
game.informPlayer(controller, "You have no cards outside the game" + (alsoFromExile ? " or in exile" : "") + '.');
|
||||
return true;
|
||||
|
|
@ -96,7 +95,7 @@ public class WishEffect extends OneShotEffect {
|
|||
return true;
|
||||
}
|
||||
|
||||
TargetCard target = new TargetCard(Zone.OUTSIDE, filter);
|
||||
TargetCard target = new TargetCard(Zone.ALL, filter);
|
||||
target.setNotTarget(true);
|
||||
if (controller.choose(Outcome.Benefit, filteredCards, target, game)) {
|
||||
Card card = controller.getSideboard().get(target.getFirstTarget(), game);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public class CantBlockUnlessPayManaAllEffect extends PayCostToAttackBlockEffectI
|
|||
+ " can't block "
|
||||
+ "unless their controller pays "
|
||||
+ (manaCosts == null ? "" : manaCosts.getText())
|
||||
+ " for each blocking creature he or she controls";
|
||||
+ " for each blocking creature they control";
|
||||
}
|
||||
|
||||
public CantBlockUnlessPayManaAllEffect(CantBlockUnlessPayManaAllEffect effect) {
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl {
|
|||
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 {
|
||||
for (SubType t : token.getSubtype(game)) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import mage.abilities.effects.ContinuousEffectImpl;
|
|||
import mage.cards.Card;
|
||||
import mage.cards.SplitCardHalf;
|
||||
import mage.constants.*;
|
||||
import mage.filter.common.FilterNonlandCard;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
|
|
@ -19,13 +20,26 @@ import java.util.UUID;
|
|||
|
||||
public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImpl {
|
||||
|
||||
private final FilterCard filter;
|
||||
private final boolean fromHand;
|
||||
|
||||
public CastFromHandWithoutPayingManaCostEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment);
|
||||
staticText = "You may cast nonland cards from your hand without paying their mana costs";
|
||||
this(StaticFilters.FILTER_CARDS_NON_LAND, true);
|
||||
}
|
||||
|
||||
public CastFromHandWithoutPayingManaCostEffect(final CastFromHandWithoutPayingManaCostEffect effect) {
|
||||
public CastFromHandWithoutPayingManaCostEffect(FilterCard filter, boolean fromHand) {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment);
|
||||
this.filter = filter;
|
||||
this.fromHand = fromHand;
|
||||
staticText = "You may cast " + filter.getMessage()
|
||||
+ (fromHand ? " from your hand" : "")
|
||||
+ " without paying their mana costs";
|
||||
}
|
||||
|
||||
private CastFromHandWithoutPayingManaCostEffect(final CastFromHandWithoutPayingManaCostEffect effect) {
|
||||
super(effect);
|
||||
this.filter = effect.filter;
|
||||
this.fromHand = effect.fromHand;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -36,12 +50,19 @@ public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImp
|
|||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
controller.getAlternativeSourceCosts().add(new AlternativeCostSourceAbility(
|
||||
null, new CompoundCondition(SourceIsSpellCondition.instance, new IsBeingCastFromHandCondition()), null, new FilterNonlandCard(), true));
|
||||
return true;
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
Condition condition;
|
||||
if (fromHand) {
|
||||
condition = new CompoundCondition(SourceIsSpellCondition.instance, IsBeingCastFromHandCondition.instance);
|
||||
} else {
|
||||
condition = SourceIsSpellCondition.instance;
|
||||
}
|
||||
controller.getAlternativeSourceCosts().add(new AlternativeCostSourceAbility(
|
||||
null, condition, null, filter, true
|
||||
));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -55,7 +76,8 @@ public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImp
|
|||
}
|
||||
}
|
||||
|
||||
class IsBeingCastFromHandCondition implements Condition {
|
||||
enum IsBeingCastFromHandCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package mage.abilities.effects.common.continuous;
|
|||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.constants.Duration;
|
||||
|
|
@ -23,6 +24,7 @@ public class GainControlTargetEffect extends ContinuousEffectImpl {
|
|||
|
||||
protected UUID controllingPlayerId;
|
||||
private boolean fixedControl;
|
||||
private boolean firstControlChange = true;
|
||||
|
||||
public GainControlTargetEffect(Duration duration) {
|
||||
this(duration, false, null);
|
||||
|
|
@ -77,31 +79,44 @@ public class GainControlTargetEffect extends ContinuousEffectImpl {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
boolean targetStillExists = false;
|
||||
boolean oneTargetStillExists = false;
|
||||
for (UUID permanentId : getTargetPointer().getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(permanentId);
|
||||
if (permanent != null) {
|
||||
targetStillExists = true;
|
||||
oneTargetStillExists = true;
|
||||
if (!permanent.isControlledBy(controllingPlayerId)) {
|
||||
GameEvent loseControlEvent = GameEvent.getEvent(GameEvent.EventType.LOSE_CONTROL, permanentId, source.getId(), permanent.getControllerId());
|
||||
if (game.replaceEvent(loseControlEvent)) {
|
||||
return false;
|
||||
}
|
||||
boolean controlChanged = false;
|
||||
if (controllingPlayerId != null) {
|
||||
permanent.changeControllerId(controllingPlayerId, game);
|
||||
permanent.getAbilities().setControllerId(controllingPlayerId);
|
||||
if (permanent.changeControllerId(controllingPlayerId, game)) {
|
||||
permanent.getAbilities().setControllerId(controllingPlayerId);
|
||||
controlChanged = true;
|
||||
}
|
||||
} else {
|
||||
permanent.changeControllerId(source.getControllerId(), game);
|
||||
permanent.getAbilities().setControllerId(source.getControllerId());
|
||||
if (permanent.changeControllerId(source.getControllerId(), game)) {
|
||||
permanent.getAbilities().setControllerId(source.getControllerId());
|
||||
controlChanged = true;
|
||||
}
|
||||
}
|
||||
if (source instanceof ActivatedAbility
|
||||
&& firstControlChange && !controlChanged) {
|
||||
// If it was not possible to get control of target permanent by the activated ability the first time it took place
|
||||
// the effect failed (e.g. because of Guardian Beast) and must be discarded
|
||||
// This does not handle correctly multiple targets at once
|
||||
discard();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// no valid target exists and the controller is no longer in the game, effect can be discarded
|
||||
if (!targetStillExists
|
||||
if (!oneTargetStillExists
|
||||
|| !controller.isInGame()) {
|
||||
discard();
|
||||
}
|
||||
firstControlChange = false;
|
||||
return true;
|
||||
}
|
||||
discard(); // controller no longer exists
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.cards.Card;
|
||||
|
|
@ -11,6 +9,9 @@ import mage.constants.Outcome;
|
|||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author nantuko
|
||||
|
|
@ -47,16 +48,23 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
Card cardOnTop = game.getCard(objectId);
|
||||
if (cardOnTop != null
|
||||
&& affectedControllerId.equals(source.getControllerId())
|
||||
&& cardOnTop.isOwnedBy(source.getControllerId())
|
||||
&& (!cardOnTop.getManaCost().isEmpty() || cardOnTop.isLand())
|
||||
&& filter.match(cardOnTop, game)) {
|
||||
Player player = game.getPlayer(cardOnTop.getOwnerId());
|
||||
if (player != null && cardOnTop.equals(player.getLibrary().getFromTop(game))) {
|
||||
return true;
|
||||
}
|
||||
return applies(objectId, null, source, game, affectedControllerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
Card cardToCheck = game.getCard(objectId);
|
||||
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
|
||||
|
||||
if (cardToCheck != null
|
||||
&& playerId.equals(source.getControllerId())
|
||||
&& cardToCheck.isOwnedBy(source.getControllerId())
|
||||
&& (!cardToCheck.getManaCost().isEmpty() || cardToCheck.isLand())
|
||||
&& filter.match(cardToCheck, game)) {
|
||||
Player player = game.getPlayer(cardToCheck.getOwnerId());
|
||||
|
||||
UUID needCardID = player.getLibrary().getFromTop(game) == null ? null : player.getLibrary().getFromTop(game).getId();
|
||||
return objectId.equals(needCardID);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common.cost;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -7,12 +6,12 @@ import mage.constants.CostModificationType;
|
|||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Styxo
|
||||
*/
|
||||
public class SourceCostReductionForEachCardInGraveyardEffect extends CostModificationEffectImpl {
|
||||
|
|
@ -20,7 +19,7 @@ public class SourceCostReductionForEachCardInGraveyardEffect extends CostModific
|
|||
private FilterCard filter;
|
||||
|
||||
public SourceCostReductionForEachCardInGraveyardEffect() {
|
||||
this(new FilterCard());
|
||||
this(StaticFilters.FILTER_CARD);
|
||||
}
|
||||
|
||||
public SourceCostReductionForEachCardInGraveyardEffect(FilterCard filter) {
|
||||
|
|
@ -29,7 +28,7 @@ public class SourceCostReductionForEachCardInGraveyardEffect extends CostModific
|
|||
staticText = "{this} costs {1} less to cast for each " + filter.getMessage() + " in your graveyard";
|
||||
}
|
||||
|
||||
SourceCostReductionForEachCardInGraveyardEffect(SourceCostReductionForEachCardInGraveyardEffect effect) {
|
||||
private SourceCostReductionForEachCardInGraveyardEffect(SourceCostReductionForEachCardInGraveyardEffect effect) {
|
||||
super(effect);
|
||||
this.filter = effect.filter.copy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common.cost;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
|
|
@ -6,7 +5,6 @@ import java.util.Set;
|
|||
import java.util.UUID;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.choices.ChoiceImpl;
|
||||
|
|
@ -70,11 +68,9 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||
if (upTo) {
|
||||
if (abilityToModify instanceof ActivatedAbility) {
|
||||
if (((ActivatedAbility) abilityToModify).isCheckPlayableMode()) {
|
||||
CardUtil.reduceCost(abilityToModify, this.amount);
|
||||
return true;
|
||||
}
|
||||
if (game.inCheckPlayableState()) {
|
||||
CardUtil.reduceCost(abilityToModify, this.amount);
|
||||
return true;
|
||||
}
|
||||
Mana mana = abilityToModify.getManaCostsToPay().getMana();
|
||||
int reduceMax = mana.getGeneric();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common.cost;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
|
|
@ -6,7 +5,6 @@ import java.util.Set;
|
|||
import mage.MageObject;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCosts;
|
||||
|
|
@ -85,7 +83,7 @@ public class SpellsCostReductionControllerEffect extends CostModificationEffectI
|
|||
return false;
|
||||
}
|
||||
int reduce = reduceMax;
|
||||
if (!(abilityToModify instanceof ActivatedAbility) || !((ActivatedAbility) abilityToModify).isCheckPlayableMode()) {
|
||||
if (!game.inCheckPlayableState()) {
|
||||
ChoiceImpl choice = new ChoiceImpl(false);
|
||||
Set<String> set = new LinkedHashSet<>();
|
||||
for (int i = 0; i <= amount; i++) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common.counter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -96,7 +95,9 @@ public class AddCountersSourceEffect extends OneShotEffect {
|
|||
if (permanent == null && source.getAbilityType() == AbilityType.STATIC) {
|
||||
permanent = game.getPermanentEntering(source.getSourceId());
|
||||
}
|
||||
if (permanent != null) {
|
||||
if (permanent != null
|
||||
&& (source.getSourceObjectZoneChangeCounter() == 0 // from static ability
|
||||
|| source.getSourceObjectZoneChangeCounter() == permanent.getZoneChangeCounter(game))) { // prevent to add counters to later source objects
|
||||
if (counter != null) {
|
||||
Counter newCounter = counter.copy();
|
||||
int countersToAdd = amount.calculate(game, source, this);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public class DiscardHandTargetEffect extends OneShotEffect {
|
|||
for (UUID playerId: getTargetPointer().getTargets(game, source)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
player.discard(player.getHand().size(), source, game);
|
||||
player.discard(player.getHand().size(), false, source, game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common.search;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -87,7 +86,9 @@ public class SearchLibraryPutInPlayEffect extends SearchEffect {
|
|||
}
|
||||
sb.append(target.getTargetName()).append(" and put them onto the battlefield");
|
||||
} else {
|
||||
sb.append("a ").append(target.getTargetName()).append(" and put it onto the battlefield");
|
||||
sb.append(target.getTargetName().startsWith("a ") || target.getTargetName().startsWith("an ") ? "" : "a ")
|
||||
.append(target.getTargetName())
|
||||
.append(" and put it onto the battlefield");
|
||||
}
|
||||
if (tapped) {
|
||||
sb.append(" tapped");
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ public class SearchLibraryPutInPlayTargetPlayerEffect extends SearchEffect {
|
|||
if (forceShuffle) {
|
||||
sb.append(". Then that player shuffles their library");
|
||||
} else {
|
||||
sb.append(". If that player does, he or she shuffles their library");
|
||||
sb.append(". If that player does, they shuffle their library");
|
||||
}
|
||||
staticText = sb.toString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ public class FatesealEffect extends OneShotEffect {
|
|||
private void setText() {
|
||||
StringBuilder sb = new StringBuilder("fateseal ").append(fatesealNumber);
|
||||
if (fatesealNumber == 1) {
|
||||
sb.append(". <i>(To fateseal 1, its controller looks at the top card of an opponent's library, then he or she may put that card on the bottom of that library.)</i>");
|
||||
sb.append(". <i>(To fateseal 1, its controller looks at the top card of an opponent's library, then they may put that card on the bottom of that library.)</i>");
|
||||
} else {
|
||||
sb.append(". <i>(To fateseal ");
|
||||
sb.append(CardUtil.numberToText(fatesealNumber));
|
||||
|
|
|
|||
|
|
@ -29,6 +29,6 @@ public class AffinityForArtifactsAbility extends SimpleStaticAbility {
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "affinity for artifacts <i>(This spell costs {1} less to cast for each artifact you control.)</i>";
|
||||
return "Affinity for artifacts <i>(This spell costs {1} less to cast for each artifact you control.)</i>";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -16,8 +15,9 @@ import mage.game.events.GameEvent.EventType;
|
|||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* If you would draw a card, instead you may put exactly X cards from the top of your library into your graveyard. If
|
||||
* you do, return this card from your graveyard to your hand. Otherwise, draw a card.
|
||||
* If you would draw a card, instead you may put exactly X cards from the top of
|
||||
* your library into your graveyard. If you do, return this card from your
|
||||
* graveyard to your hand. Otherwise, draw a card.
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
|
|
@ -42,7 +42,7 @@ class DredgeEffect extends ReplacementEffectImpl {
|
|||
private final int amount;
|
||||
|
||||
public DredgeEffect(int value) {
|
||||
super(Duration.WhileInGraveyard, Outcome.ReturnToHand);
|
||||
super(Duration.WhileInGraveyard, Outcome.AIDontUseIt);
|
||||
this.amount = value;
|
||||
this.staticText = new StringBuilder("Dredge ").append(Integer.toString(value)).append(" <i>(If you would draw a card, instead you may put exactly ").append(value).append(" card(s) from the top of your library into your graveyard. If you do, return this card from your graveyard to your hand. Otherwise, draw a card.)</i>").toString();
|
||||
}
|
||||
|
|
@ -68,17 +68,18 @@ class DredgeEffect extends ReplacementEffectImpl {
|
|||
if (sourceCard == null) {
|
||||
return false;
|
||||
}
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null && player.getLibrary().size() >= amount
|
||||
&& player.chooseUse(outcome, new StringBuilder("Dredge ").append(sourceCard.getLogName()).
|
||||
append("? (").append(amount).append(" cards go from top of library to graveyard)").toString(), source, game)) {
|
||||
Player owner = game.getPlayer(game.getCard(source.getSourceId()).getOwnerId());
|
||||
if (owner != null
|
||||
&& owner.getLibrary().size() >= amount
|
||||
&& owner.chooseUse(outcome, new StringBuilder("Dredge ").append(sourceCard.getLogName()).
|
||||
append("? (").append(amount).append(" cards go from top of library to graveyard)").toString(), source, game)) {
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(new StringBuilder(player.getLogName()).append(" dredges ").append(sourceCard.getLogName()).toString());
|
||||
game.informPlayers(new StringBuilder(owner.getLogName()).append(" dredges ").append(sourceCard.getLogName()).toString());
|
||||
}
|
||||
Cards cardsToGrave = new CardsImpl();
|
||||
cardsToGrave.addAll(player.getLibrary().getTopCards(game, amount));
|
||||
player.moveCards(cardsToGrave, Zone.GRAVEYARD, source, game);
|
||||
player.moveCards(sourceCard, Zone.HAND, source, game);
|
||||
cardsToGrave.addAll(owner.getLibrary().getTopCards(game, amount));
|
||||
owner.moveCards(cardsToGrave, Zone.GRAVEYARD, source, game);
|
||||
owner.moveCards(sourceCard, Zone.HAND, source, game);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -89,13 +90,11 @@ class DredgeEffect extends ReplacementEffectImpl {
|
|||
return event.getType() == EventType.DRAW_CARD;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (event.getPlayerId().equals(source.getControllerId())) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
return controller != null && controller.getLibrary().size() >= amount;
|
||||
}
|
||||
return false;
|
||||
Player owner = game.getPlayer(game.getCard(source.getSourceId()).getOwnerId());
|
||||
return (owner != null
|
||||
&& event.getPlayerId().equals(owner.getId())
|
||||
&& owner.getLibrary().size() >= amount);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ public class EmergeAbility extends SpellAbility {
|
|||
if (super.canActivate(playerId, game).canActivate()) {
|
||||
Player controller = game.getPlayer(this.getControllerId());
|
||||
if (controller != null) {
|
||||
for (Permanent creature : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), this.getControllerId(), this.getSourceId(), game)) {
|
||||
for (Permanent creature : game.getBattlefield().getActivePermanents(
|
||||
new FilterControlledCreaturePermanent(), this.getControllerId(), this.getSourceId(), game)) {
|
||||
ManaCost costToPay = CardUtil.reduceCost(emergeCost.copy(), creature.getConvertedManaCost());
|
||||
if (costToPay.canPay(this, this.getSourceId(), this.getControllerId(), game)) {
|
||||
return ActivationStatus.getTrue();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.abilities.ActivatedAbilityImpl;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TimingRule;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class EquipFilterAbility extends ActivatedAbilityImpl {
|
||||
|
||||
private final FilterControlledCreaturePermanent filter;
|
||||
|
||||
public EquipFilterAbility(FilterControlledCreaturePermanent filter, Cost cost) {
|
||||
super(Zone.BATTLEFIELD, new AttachEffect(Outcome.AddAbility, "Equip"), cost);
|
||||
this.addTarget(new TargetControlledCreaturePermanent(filter));
|
||||
this.filter = filter;
|
||||
this.timing = TimingRule.SORCERY;
|
||||
}
|
||||
|
||||
private EquipFilterAbility(final EquipFilterAbility ability) {
|
||||
super(ability);
|
||||
this.filter = ability.filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EquipFilterAbility copy() {
|
||||
return new EquipFilterAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Equip " + filter.getMessage() + costs.getText() + manaCosts.getText();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.ActivatedAbilityImpl;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.TimingRule;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.SupertypePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.Target;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
|
||||
/**
|
||||
* @author Rystan
|
||||
*/
|
||||
public class EquipLegendaryAbility extends ActivatedAbilityImpl {
|
||||
|
||||
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("legendary creature you control");
|
||||
|
||||
static {
|
||||
filter.add(new SupertypePredicate(SuperType.LEGENDARY));
|
||||
}
|
||||
|
||||
public EquipLegendaryAbility(Outcome outcome, Cost cost) {
|
||||
this(outcome, cost, new TargetControlledCreaturePermanent(filter));
|
||||
}
|
||||
|
||||
public EquipLegendaryAbility(Outcome outcome, Cost cost, Target target) {
|
||||
super(Zone.BATTLEFIELD, new AttachEffect(outcome, "Equip"), cost);
|
||||
this.addTarget(target);
|
||||
this.timing = TimingRule.SORCERY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
ActivationStatus activationStatus = super.canActivate(playerId, game);
|
||||
if (activationStatus.canActivate()) {
|
||||
Permanent permanent = game.getPermanent(sourceId);
|
||||
if (permanent != null && permanent.hasSubtype(SubType.EQUIPMENT, game)) {
|
||||
return activationStatus;
|
||||
}
|
||||
}
|
||||
return activationStatus;
|
||||
}
|
||||
|
||||
public EquipLegendaryAbility(final EquipLegendaryAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EquipLegendaryAbility copy() {
|
||||
return new EquipLegendaryAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Equip legendary creature " + costs.getText()
|
||||
+ manaCosts.getText() + " (" + manaCosts.getText()
|
||||
+ ": <i>Attach to target legendary creature you control. Equip only as a sorcery.)</i>";
|
||||
}
|
||||
|
||||
}
|
||||
63
Mage/src/main/java/mage/abilities/keyword/EscapeAbility.java
Normal file
63
Mage/src/main/java/mage/abilities/keyword/EscapeAbility.java
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.common.ExileFromGraveCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.permanent.AnotherPredicate;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class EscapeAbility extends SpellAbility {
|
||||
|
||||
private static final FilterCard filter = new FilterCard();
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
}
|
||||
|
||||
private final String manaCost;
|
||||
private final int exileCount;
|
||||
|
||||
public EscapeAbility(Card card, String manaCost, int exileCount) {
|
||||
super(new ManaCostsImpl(manaCost), card.getName() + " with escape");
|
||||
this.newId();
|
||||
this.zone = Zone.GRAVEYARD;
|
||||
this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE;
|
||||
this.manaCost = manaCost;
|
||||
this.exileCount = exileCount;
|
||||
|
||||
Cost cost = new ExileFromGraveCost(new TargetCardInYourGraveyard(exileCount, filter));
|
||||
cost.setText("");
|
||||
this.addCost(cost);
|
||||
}
|
||||
|
||||
private EscapeAbility(final EscapeAbility ability) {
|
||||
super(ability);
|
||||
this.manaCost = ability.manaCost;
|
||||
this.exileCount = ability.exileCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EscapeAbility copy() {
|
||||
return new EscapeAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule(boolean all) {
|
||||
return getRule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Escape — " + this.manaCost + ", Exile " + CardUtil.numberToText(this.exileCount) +
|
||||
" other cards from your graveyard. <i>(You may cast this card from your graveyard for its escape cost.)</i>";
|
||||
}
|
||||
}
|
||||
|
|
@ -57,10 +57,13 @@ public class FlashbackAbility extends SpellAbility {
|
|||
|
||||
@Override
|
||||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
ActivationStatus activationStatus = super.canActivate(playerId, game);
|
||||
if (activationStatus.canActivate()) {
|
||||
if (super.canActivate(playerId, game).canActivate()) {
|
||||
Card card = game.getCard(getSourceId());
|
||||
if (card != null) {
|
||||
// Card must be in the graveyard zone
|
||||
if (game.getState().getZone(card.getId()) != Zone.GRAVEYARD) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
// Cards with no Mana Costs cant't be flashbacked (e.g. Ancestral Vision)
|
||||
if (card.getManaCost().isEmpty()) {
|
||||
return ActivationStatus.getFalse();
|
||||
|
|
@ -76,7 +79,7 @@ public class FlashbackAbility extends SpellAbility {
|
|||
return card.getSpellAbility().canActivate(playerId, game);
|
||||
}
|
||||
}
|
||||
return activationStatus;
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
|
|
@ -24,8 +24,6 @@ import mage.players.Player;
|
|||
import mage.target.TargetCard;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
* <p>
|
||||
|
|
@ -101,11 +99,13 @@ class HideawayExileEffect extends OneShotEffect {
|
|||
cards.addAll(controller.getLibrary().getTopCards(game, 4));
|
||||
if (!cards.isEmpty()) {
|
||||
TargetCard target1 = new TargetCard(Zone.LIBRARY, filter1);
|
||||
target1.setNotTarget(true);
|
||||
if (controller.choose(Outcome.Detriment, cards, target1, game)) {
|
||||
Card card = cards.get(target1.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
cards.remove(card);
|
||||
controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source),
|
||||
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
|
||||
controller.moveCardToExileWithInfo(card, exileId,
|
||||
"Hideaway (" + hideawaySource.getIdName() + ')', source.getSourceId(), game, Zone.LIBRARY, false);
|
||||
card.setFaceDown(true, game);
|
||||
}
|
||||
|
|
@ -121,7 +121,7 @@ class HideawayLookAtFaceDownCardEffect extends AsThoughEffectImpl {
|
|||
|
||||
public HideawayLookAtFaceDownCardEffect() {
|
||||
super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit);
|
||||
staticText = "You may look at cards exiled with {this}";
|
||||
staticText = "You may look at the cards exiled with {this}";
|
||||
}
|
||||
|
||||
private HideawayLookAtFaceDownCardEffect(final HideawayLookAtFaceDownCardEffect effect) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import java.io.ObjectStreamException;
|
|||
* 112.7a. Once activated or triggered, an ability exists on the stack independently of its source. Destruction or removal of the source after that time won't affect the ability. Note that some abilities cause a source to do something (for example, "Prodigal Sorcerer deals 1 damage...
|
||||
* 608.2b. If the spell or ability specifies targets, it checks whether the targets are still legal. A target that's no longer in the zone it was in when it was targeted is illegal. Other changes to the game state may cause a target to no longer be legal; for example, its...
|
||||
* 608.2g. If an effect requires information from the game (such as the number of creatures on the battlefield), the answer is determined only once, when the effect is applied. If the effect requires information from a specific object, including the source of the ability itself or a...
|
||||
* 800.4f. If an effect requires information about a specific player, the effect uses the current information about that player if he or she is still in the game; otherwise, the effect uses the last known information about that player before he or she left the game.
|
||||
* 800.4f. If an effect requires information about a specific player, the effect uses the current information about that player if they are still in the game; otherwise, the effect uses the last known information about that player before they left the game.
|
||||
* is used to determine whether it had infect.
|
||||
*
|
||||
* 702.87e. The infect rules function no matter what zone an object with infect deals damage from.
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import mage.players.Player;
|
|||
* "Madness [cost]" means "If a player would discard this card, that player
|
||||
* discards it, but may exile it instead of putting it into their graveyard" and
|
||||
* "When this card is exiled this way, its owner may cast it by paying [cost]
|
||||
* rather than paying its mana cost. If that player doesn't, he or she puts this
|
||||
* rather than paying its mana cost. If that player doesn't, they put this
|
||||
* card into their graveyard.
|
||||
*
|
||||
* 702.33b. Casting a spell using its madness ability follows the rules for
|
||||
|
|
@ -133,7 +133,7 @@ class MadnessReplacementEffect extends ReplacementEffectImpl {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks for the MADNESS_CARD_EXILED event to ask the player if he wants to
|
||||
* Checks for the MADNESS_CARD_EXILED event to ask the player if they want to
|
||||
* cast the card by it's Madness costs. If not, the card goes to the graveyard.
|
||||
*/
|
||||
class MadnessTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ import mage.watchers.common.MiracleWatcher;
|
|||
* this card this way, you may cast it by paying [cost] rather than its mana
|
||||
* cost."
|
||||
*
|
||||
* 702.92b If a player chooses to reveal a card using its miracle ability, he or
|
||||
* she plays with that card revealed until that card leaves their hand, that
|
||||
* ability resolves, or that ability otherwise leaves the stack.
|
||||
* 702.92b If a player chooses to reveal a card using its miracle ability, they
|
||||
* play with that card revealed until that card leaves their hand, that ability
|
||||
* resolves, or that ability otherwise leaves the stack.
|
||||
*
|
||||
* You can cast a card for its miracle cost only as the miracle triggered
|
||||
* ability resolves. If you don't want to cast it at that time (or you can't
|
||||
|
|
|
|||
|
|
@ -186,6 +186,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
|
|||
spell.setFaceDown(true, game); // so only the back is visible
|
||||
if (alternateCosts.canPay(ability, sourceId, controllerId, game)) {
|
||||
if (player.chooseUse(Outcome.Benefit, "Cast this card as a 2/2 face-down creature for " + getCosts().getText() + " ?", ability, game)) {
|
||||
game.getState().setValue("MorphAbility" + ability.getSourceId(), "activated"); // Gift of Doom
|
||||
activateMorph(game);
|
||||
// change mana costs
|
||||
ability.getManaCostsToPay().clear();
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public class MyriadAbility extends AttacksTriggeredAbility {
|
|||
super(new MyriadEffect(), false,
|
||||
"Myriad <i>(Whenever this creature attacks, for each opponent other than the defending player, "
|
||||
+ "put a token that's a copy of this creature onto the battlefield tapped and attacking "
|
||||
+ "that player or a planeswalker he or she controls. Exile those tokens at the end of combat.)</i>",
|
||||
+ "that player or a planeswalker they control. Exile those tokens at the end of combat.)</i>",
|
||||
SetTargetPointer.PLAYER
|
||||
);
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ class MyriadEffect extends OneShotEffect {
|
|||
super(Outcome.Benefit);
|
||||
this.staticText = "for each opponent other than the defending player, you may put a token "
|
||||
+ "that's a copy of this creature onto the battlefield tapped and attacking that "
|
||||
+ "player or a planeswalker he or she controls. "
|
||||
+ "player or a planeswalker they control. "
|
||||
+ "Exile the tokens at the end of combat";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
|
|
@ -17,8 +18,6 @@ import mage.target.Target;
|
|||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 702.46. Offering # 702.46a Offering is a static ability of a card that
|
||||
* functions in any zone from which the card can be cast. "[Subtype] offering"
|
||||
|
|
@ -121,7 +120,7 @@ class OfferingAsThoughEffect extends AsThoughEffectImpl {
|
|||
|
||||
if (game.getBattlefield().count(((OfferingAbility) source).getFilter(), source.getSourceId(), source.getControllerId(), game) > 0) {
|
||||
|
||||
if (CardUtil.isCheckPlayableMode(affectedAbility)) {
|
||||
if (game.inCheckPlayableState()) {
|
||||
return true;
|
||||
}
|
||||
FilterControlledCreaturePermanent filter = ((OfferingAbility) source).getFilter();
|
||||
|
|
@ -130,7 +129,7 @@ class OfferingAsThoughEffect extends AsThoughEffectImpl {
|
|||
return false;
|
||||
}
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null && !CardUtil.isCheckPlayableMode(affectedAbility)
|
||||
if (player != null && !game.inCheckPlayableState()
|
||||
&& player.chooseUse(Outcome.Benefit, "Offer a " + filter.getMessage() + " to cast " + spellToCast.getName() + '?', source, game)) {
|
||||
Target target = new TargetControlledCreaturePermanent(1, 1, filter, true);
|
||||
player.chooseTarget(Outcome.Sacrifice, target, source, game);
|
||||
|
|
@ -193,7 +192,7 @@ class OfferingCostReductionEffect extends CostModificationEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
if (CardUtil.isCheckPlayableMode(abilityToModify)) { // Cost modifaction does not work correctly for checking available spells
|
||||
if (game.inCheckPlayableState()) { // Cost modifaction does not work correctly for checking available spells
|
||||
return false;
|
||||
}
|
||||
if (abilityToModify.getId().equals(spellAbilityId) && abilityToModify instanceof SpellAbility) {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,10 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.Iterator;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.OptionalAdditionalCost;
|
||||
import mage.abilities.costs.OptionalAdditionalCostImpl;
|
||||
import mage.abilities.costs.OptionalAdditionalSourceCosts;
|
||||
import mage.abilities.costs.*;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
|
@ -23,8 +17,9 @@ import mage.game.stack.Spell;
|
|||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class ReplicateAbility extends StaticAbility implements OptionalAdditionalSourceCosts {
|
||||
|
|
@ -91,12 +86,12 @@ public class ReplicateAbility extends StaticAbility implements OptionalAdditiona
|
|||
String times = "";
|
||||
if (additionalCost.isRepeatable()) {
|
||||
int numActivations = additionalCost.getActivateCount();
|
||||
times = Integer.toString(numActivations + 1) + (numActivations == 0 ? " time " : " times ");
|
||||
times = (numActivations + 1) + (numActivations == 0 ? " time " : " times ");
|
||||
}
|
||||
if (additionalCost.canPay(ability, sourceId, controllerId, game)
|
||||
&& player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(times).append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) {
|
||||
additionalCost.activate();
|
||||
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) {
|
||||
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext(); ) {
|
||||
Cost cost = (Cost) it.next();
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
|
|
@ -170,7 +165,7 @@ class ReplicateTriggeredAbility extends TriggeredAbilityImpl {
|
|||
if (card != null) {
|
||||
for (Ability ability : card.getAbilities(game)) {
|
||||
if (ability instanceof ReplicateAbility) {
|
||||
if (((ReplicateAbility) ability).isActivated()) {
|
||||
if (ability.isActivated()) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
effect.setValue("ReplicateSpell", spell);
|
||||
effect.setValue("ReplicateCount", ((ReplicateAbility) ability).getActivateCount());
|
||||
|
|
@ -213,7 +208,7 @@ class ReplicateCopyEffect extends OneShotEffect {
|
|||
if (card != null) {
|
||||
for (Ability ability : card.getAbilities(game)) {
|
||||
if (ability instanceof ReplicateAbility) {
|
||||
if (((ReplicateAbility) ability).isActivated()) {
|
||||
if (ability.isActivated()) {
|
||||
((ReplicateAbility) ability).resetReplicate();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,8 +45,9 @@ public class SpectacleAbility extends SpellAbility {
|
|||
|
||||
@Override
|
||||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
if (OpponentsLostLifeCount.instance.calculate(game, playerId) > 0) {
|
||||
return super.canActivate(playerId, game);
|
||||
if (OpponentsLostLifeCount.instance.calculate(game, playerId) > 0
|
||||
&& super.canActivate(playerId, game).canActivate()) {
|
||||
return ActivationStatus.getTrue();
|
||||
}
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,8 +51,9 @@ public class SurgeAbility extends SpellAbility {
|
|||
if (player != null) {
|
||||
for (UUID playerToCheckId : game.getState().getPlayersInRange(playerId, game)) {
|
||||
if (!player.hasOpponent(playerToCheckId, game)) {
|
||||
if (watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(playerToCheckId) > 0) {
|
||||
return super.canActivate(playerId, game);
|
||||
if (watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(playerToCheckId) > 0
|
||||
&& super.canActivate(playerId, game).canActivate()) {
|
||||
return ActivationStatus.getTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,12 +92,12 @@ import mage.target.targetpointer.FixedTarget;
|
|||
*
|
||||
* As the second triggered ability of suspend resolves, if playing the suspended
|
||||
* card involves an additional cost, the card's owner must pay that cost if
|
||||
* able. If he or she can't, the card remains removed from the game. If the
|
||||
* able. If they can't, the card remains removed from the game. If the
|
||||
* additional cost includes mana, the situation is more complex. If the player
|
||||
* has enough mana in their mana pool to pay the cost, that player must do so.
|
||||
* If the player can't possibly pay the cost, the card remains removed from the
|
||||
* game. However, if the player has the means to produce enough mana to pay the
|
||||
* cost, then he or she has a choice: The player may play the spell, produce
|
||||
* cost, then they have a choice: The player may play the spell, produce
|
||||
* mana, and pay the cost. Or the player may choose to play no mana abilities,
|
||||
* thus making the card impossible to play because the additional mana can't be
|
||||
* paid.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -66,8 +65,8 @@ public class TransformAbility extends SimpleStaticAbility {
|
|||
for (Ability ability : sourceCard.getAbilities()) {
|
||||
permanent.addAbility(ability, game);
|
||||
}
|
||||
permanent.getPower().setValue(sourceCard.getPower().getValue());
|
||||
permanent.getToughness().setValue(sourceCard.getToughness().getValue());
|
||||
permanent.getPower().modifyBaseValue(sourceCard.getPower().getValue());
|
||||
permanent.getToughness().modifyBaseValue(sourceCard.getToughness().getValue());
|
||||
permanent.setTransformable(sourceCard.isTransformable());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl
|
|||
&& null == game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_AS_INSTANT, this, controllerId, game)) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
// check if player is in the process of playing spell costs and he is no longer allowed to use activated mana abilities (e.g. because he started to use improvise)
|
||||
// check if player is in the process of playing spell costs and they are no longer allowed to use activated mana abilities (e.g. because they started to use improvise)
|
||||
//20091005 - 605.3a
|
||||
return new ActivationStatus(costs.canPay(this, sourceId, controllerId, game), null);
|
||||
|
||||
|
|
|
|||
107
Mage/src/main/java/mage/cards/AdventureCard.java
Normal file
107
Mage/src/main/java/mage/cards/AdventureCard.java
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package mage.cards;
|
||||
|
||||
import mage.abilities.Abilities;
|
||||
import mage.abilities.AbilitiesImpl;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public abstract class AdventureCard extends CardImpl {
|
||||
|
||||
/* The adventure spell card, i.e. Swift End. */
|
||||
protected Card spellCard;
|
||||
|
||||
public AdventureCard(UUID ownerId, CardSetInfo setInfo, CardType[] types, CardType[] typesSpell, String costs, String adventureName, String costsSpell) {
|
||||
super(ownerId, setInfo, types, costs);
|
||||
this.spellCard = new AdventureCardSpellImpl(ownerId, setInfo, adventureName, typesSpell, costsSpell, this);
|
||||
}
|
||||
|
||||
public AdventureCard(AdventureCard card) {
|
||||
super(card);
|
||||
this.spellCard = card.getSpellCard().copy();
|
||||
((AdventureCardSpell) this.spellCard).setParentCard(this);
|
||||
}
|
||||
|
||||
public Card getSpellCard() {
|
||||
return spellCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assignNewId() {
|
||||
super.assignNewId();
|
||||
spellCard.assignNewId();
|
||||
}
|
||||
|
||||
@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(getSpellCard().getId(), toZone);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZone(Zone zone, Game game) {
|
||||
super.setZone(zone, game);
|
||||
game.setZone(getSpellCard().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(getSpellCard().getId(), currentZone);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) {
|
||||
switch (ability.getSpellAbilityType()) {
|
||||
case ADVENTURE_SPELL:
|
||||
return this.getSpellCard().cast(game, fromZone, ability, controllerId);
|
||||
default:
|
||||
this.getSpellCard().getSpellAbility().setControllerId(controllerId);
|
||||
return super.cast(game, fromZone, ability, controllerId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Abilities<Ability> getAbilities() {
|
||||
Abilities<Ability> allAbilities = new AbilitiesImpl<>();
|
||||
allAbilities.addAll(spellCard.getAbilities());
|
||||
allAbilities.addAll(super.getAbilities());
|
||||
return allAbilities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Abilities<Ability> getAbilities(Game game) {
|
||||
Abilities<Ability> allAbilities = new AbilitiesImpl<>();
|
||||
allAbilities.addAll(spellCard.getAbilities(game));
|
||||
allAbilities.addAll(super.getAbilities(game));
|
||||
return allAbilities;
|
||||
}
|
||||
|
||||
public Abilities<Ability> getSharedAbilities() {
|
||||
// abilities without spellcard
|
||||
return super.getAbilities();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwnerId(UUID ownerId) {
|
||||
super.setOwnerId(ownerId);
|
||||
abilities.setControllerId(ownerId);
|
||||
spellCard.getAbilities().setControllerId(ownerId);
|
||||
spellCard.setOwnerId(ownerId);
|
||||
}
|
||||
}
|
||||
20
Mage/src/main/java/mage/cards/AdventureCardSpell.java
Normal file
20
Mage/src/main/java/mage/cards/AdventureCardSpell.java
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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 phulin
|
||||
*/
|
||||
public interface AdventureCardSpell extends Card {
|
||||
|
||||
@Override
|
||||
AdventureCardSpell copy();
|
||||
|
||||
void setParentCard(AdventureCard card);
|
||||
|
||||
AdventureCard getParentCard();
|
||||
}
|
||||
154
Mage/src/main/java/mage/cards/AdventureCardSpellImpl.java
Normal file
154
Mage/src/main/java/mage/cards/AdventureCardSpellImpl.java
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.effects.common.ExileAdventureSpellEffect;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author phulin
|
||||
*/
|
||||
public class AdventureCardSpellImpl extends CardImpl implements AdventureCardSpell {
|
||||
|
||||
private AdventureCard adventureCardParent;
|
||||
|
||||
public AdventureCardSpellImpl(UUID ownerId, CardSetInfo setInfo, String adventureName, CardType[] cardTypes, String costs, AdventureCard adventureCardParent) {
|
||||
super(ownerId, setInfo, cardTypes, costs, SpellAbilityType.ADVENTURE_SPELL);
|
||||
this.subtype.add(SubType.ADVENTURE);
|
||||
|
||||
AdventureCardSpellAbility newSpellAbility = new AdventureCardSpellAbility(getSpellAbility());
|
||||
newSpellAbility.setName(adventureName, costs);
|
||||
newSpellAbility.addEffect(ExileAdventureSpellEffect.getInstance());
|
||||
newSpellAbility.setCardName(adventureName);
|
||||
this.replaceSpellAbility(newSpellAbility);
|
||||
spellAbility = newSpellAbility;
|
||||
|
||||
this.setName(adventureName);
|
||||
this.adventureCardParent = adventureCardParent;
|
||||
}
|
||||
|
||||
public AdventureCardSpellImpl(final AdventureCardSpellImpl card) {
|
||||
super(card);
|
||||
this.adventureCardParent = card.adventureCardParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getOwnerId() {
|
||||
return adventureCardParent.getOwnerId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImageName() {
|
||||
return adventureCardParent.getImageName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExpansionSetCode() {
|
||||
return adventureCardParent.getExpansionSetCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCardNumber() {
|
||||
return adventureCardParent.getCardNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, List<UUID> appliedEffects) {
|
||||
return adventureCardParent.moveToZone(toZone, sourceId, game, flag, appliedEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) {
|
||||
return adventureCardParent.moveToExile(exileId, name, sourceId, game, appliedEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdventureCard getMainCard() {
|
||||
return adventureCardParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZone(Zone zone, Game game) {
|
||||
game.setZone(adventureCardParent.getId(), zone);
|
||||
game.setZone(adventureCardParent.getSpellCard().getId(), zone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdventureCardSpell copy() {
|
||||
return new AdventureCardSpellImpl(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParentCard(AdventureCard card) {
|
||||
this.adventureCardParent = card;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdventureCard getParentCard() {
|
||||
return this.adventureCardParent;
|
||||
}
|
||||
}
|
||||
|
||||
class AdventureCardSpellAbility extends SpellAbility {
|
||||
public AdventureCardSpellAbility(final SpellAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
ExileZone adventureExileZone = game.getExile().getExileZone(ExileAdventureSpellEffect.adventureExileId(playerId, game));
|
||||
Card spellCard = game.getCard(this.getSourceId());
|
||||
if (spellCard instanceof AdventureCardSpell) {
|
||||
Card card = ((AdventureCardSpell) spellCard).getParentCard();
|
||||
if (adventureExileZone != null && adventureExileZone.contains(card.getId())) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
}
|
||||
return super.canActivate(playerId, game);
|
||||
}
|
||||
|
||||
public void setName(String name, String costs) {
|
||||
this.name = "Adventure — " + name + " " + costs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule(boolean all) {
|
||||
return this.getRule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
StringBuilder sbRule = new StringBuilder();
|
||||
sbRule.append("Adventure — ");
|
||||
sbRule.append(this.getCardName());
|
||||
sbRule.append(" ");
|
||||
sbRule.append(manaCosts.getText());
|
||||
sbRule.append(" — ");
|
||||
Modes modes = this.getModes();
|
||||
if (modes.size() <= 1) {
|
||||
sbRule.append(modes.getMode().getEffects().getTextStartingUpperCase(modes.getMode()));
|
||||
} else {
|
||||
sbRule.append(getModes().getText());
|
||||
}
|
||||
sbRule.append(" <i>(Then exile this card. You may cast the creature later from exile.)</i>");
|
||||
return sbRule.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellAbility copy() {
|
||||
return new AdventureCardSpellAbility(this);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue