mirror of
https://github.com/magefree/mage.git
synced 2025-12-26 05:22:02 -08:00
fix conflicts
This commit is contained in:
commit
ce23f6900d
2451 changed files with 84128 additions and 14873 deletions
|
|
@ -1,10 +1,5 @@
|
|||
|
||||
package mage;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.costs.Cost;
|
||||
|
|
@ -14,6 +9,11 @@ import mage.filter.Filter;
|
|||
import mage.filter.FilterMana;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author nantuko
|
||||
*/
|
||||
|
|
@ -192,6 +192,7 @@ public class ConditionalMana extends Mana implements Serializable {
|
|||
break;
|
||||
case COLORLESS:
|
||||
colorless += amount;
|
||||
break;
|
||||
case GENERIC:
|
||||
generic += amount;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import mage.util.SubTypeList;
|
|||
import java.io.Serializable;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface MageObject extends MageItem, Serializable {
|
||||
|
|
@ -32,13 +33,13 @@ public interface MageObject extends MageItem, Serializable {
|
|||
|
||||
void setName(String name);
|
||||
|
||||
EnumSet<CardType> getCardType();
|
||||
Set<CardType> getCardType();
|
||||
|
||||
SubTypeList getSubtype(Game game);
|
||||
|
||||
boolean hasSubtype(SubType subtype, Game game);
|
||||
|
||||
EnumSet<SuperType> getSuperType();
|
||||
Set<SuperType> getSuperType();
|
||||
|
||||
Abilities<Ability> getAbilities();
|
||||
|
||||
|
|
@ -199,7 +200,7 @@ public interface MageObject extends MageItem, Serializable {
|
|||
|
||||
void setIsAllCreatureTypes(boolean value);
|
||||
|
||||
default void addCardTypes(EnumSet<CardType> cardType) {
|
||||
default void addCardTypes(Set<CardType> cardType) {
|
||||
getCardType().addAll(cardType);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ public abstract class MageObjectImpl implements MageObject {
|
|||
protected ObjectColor color;
|
||||
protected ObjectColor frameColor;
|
||||
protected FrameStyle frameStyle;
|
||||
protected EnumSet<CardType> cardType = EnumSet.noneOf(CardType.class);
|
||||
protected Set<CardType> cardType = EnumSet.noneOf(CardType.class);
|
||||
protected SubTypeList subtype = new SubTypeList();
|
||||
protected boolean isAllCreatureTypes;
|
||||
protected EnumSet<SuperType> supertype = EnumSet.noneOf(SuperType.class);
|
||||
protected Set<SuperType> supertype = EnumSet.noneOf(SuperType.class);
|
||||
protected Abilities<Ability> abilities;
|
||||
protected String text;
|
||||
protected MageInt power;
|
||||
|
|
@ -111,7 +111,7 @@ public abstract class MageObjectImpl implements MageObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<CardType> getCardType() {
|
||||
public Set<CardType> getCardType() {
|
||||
return cardType;
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ public abstract class MageObjectImpl implements MageObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<SuperType> getSuperType() {
|
||||
public Set<SuperType> getSuperType() {
|
||||
return supertype;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -254,14 +251,17 @@ public abstract class AbilityImpl implements Ability {
|
|||
int xValue = this.getManaCostsToPay().getX();
|
||||
this.getManaCostsToPay().clear();
|
||||
VariableManaCost xCosts = new VariableManaCost();
|
||||
xCosts.setAmount(xValue);
|
||||
// no x events - rules from Unbound Flourishing:
|
||||
// - Spells with additional costs that include X won't be affected by Unbound Flourishing. X must be in the spell's mana cost.
|
||||
xCosts.setAmount(xValue, xValue, false);
|
||||
this.getManaCostsToPay().add(xCosts);
|
||||
} else {
|
||||
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
|
||||
|
|
@ -288,11 +288,13 @@ public abstract class AbilityImpl implements Ability {
|
|||
if (getAbilityType() == AbilityType.SPELL && (getManaCostsToPay().isEmpty() && getCosts().isEmpty()) && !noMana) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 20121001 - 601.2b
|
||||
// If the spell has a variable cost that will be paid as it's being cast (such as an {X} in
|
||||
// its mana cost; see rule 107.3), the player announces the value of that variable.
|
||||
VariableManaCost variableManaCost = handleManaXCosts(game, noMana, controller);
|
||||
String announceString = handleOtherXCosts(game, controller);
|
||||
|
||||
// For effects from cards like Void Winnower x costs have to be set
|
||||
if (this.getAbilityType() == AbilityType.SPELL
|
||||
&& game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL_LATE, getId(), getSourceId(), getControllerId()), this)) {
|
||||
|
|
@ -308,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,
|
||||
|
|
@ -342,23 +344,6 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
}
|
||||
}
|
||||
//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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is a hack to prevent mana abilities with mana costs from causing endless loops - pay other costs first
|
||||
if (this instanceof ActivatedManaAbilityImpl && !costs.pay(this, game, sourceId, controllerId, noMana, null)) {
|
||||
|
|
@ -367,10 +352,9 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
|
||||
//20101001 - 601.2e
|
||||
if (costModificationActive) {
|
||||
if (sourceObject != null) {
|
||||
sourceObject.adjustCosts(this, game); // still needed
|
||||
game.getContinuousEffects().costModification(this, game);
|
||||
} else {
|
||||
costModificationActive = true;
|
||||
}
|
||||
|
||||
UUID activatorId = controllerId;
|
||||
|
|
@ -428,13 +412,31 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
@Override
|
||||
public boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game) {
|
||||
boolean canUseAlternativeCost = true;
|
||||
boolean canUseAdditionalCost = true;
|
||||
|
||||
if (this instanceof SpellAbility) {
|
||||
if (((SpellAbility) this).getSpellAbilityCastMode() != SpellAbilityCastMode.NORMAL) {
|
||||
// A player can't apply two alternative methods of casting or two alternative costs to a single spell.
|
||||
// So can only use alternate costs if the spell is cast in normal mode
|
||||
return false;
|
||||
// A player can't apply two alternative methods of casting or two alternative costs to a single spell.
|
||||
switch (((SpellAbility) this).getSpellAbilityCastMode()) {
|
||||
|
||||
case FLASHBACK:
|
||||
case MADNESS:
|
||||
// from Snapcaster Mage:
|
||||
// If you cast a spell from a graveyard using its flashback ability, you can’t pay other alternative costs
|
||||
// (such as that of Foil). (2018-12-07)
|
||||
canUseAlternativeCost = false;
|
||||
// You may pay any optional additional costs the spell has, such as kicker costs. You must pay any
|
||||
// mandatory additional costs the spell has, such as that of Tormenting Voice. (2018-12-07)
|
||||
canUseAdditionalCost = true;
|
||||
break;
|
||||
case NORMAL:
|
||||
default:
|
||||
canUseAlternativeCost = true;
|
||||
canUseAdditionalCost = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
boolean alternativeCostisUsed = false;
|
||||
if (sourceObject != null && !(sourceObject instanceof Permanent)) {
|
||||
Abilities<Ability> abilities = null;
|
||||
|
|
@ -443,10 +445,11 @@ public abstract class AbilityImpl implements Ability {
|
|||
} else {
|
||||
sourceObject.getAbilities();
|
||||
}
|
||||
|
||||
if (abilities != null) {
|
||||
for (Ability ability : abilities) {
|
||||
// if cast for noMana no Alternative costs are allowed
|
||||
if (!noMana && ability instanceof AlternativeSourceCosts) {
|
||||
if (canUseAlternativeCost && !noMana && ability instanceof AlternativeSourceCosts) {
|
||||
AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability;
|
||||
if (alternativeSpellCosts.isAvailable(this, game)) {
|
||||
if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) {
|
||||
|
|
@ -456,13 +459,14 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (ability instanceof OptionalAdditionalSourceCosts) {
|
||||
if (canUseAdditionalCost && ability instanceof OptionalAdditionalSourceCosts) {
|
||||
((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// controller specific alternate spell costs
|
||||
if (!noMana && !alternativeCostisUsed) {
|
||||
if (canUseAlternativeCost && !noMana && !alternativeCostisUsed) {
|
||||
if (this.getAbilityType() == AbilityType.SPELL
|
||||
// 117.9a Only one alternative cost can be applied to any one spell as it's being cast.
|
||||
// So an alternate spell ability can't be paid with Omniscience
|
||||
|
|
@ -479,6 +483,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return alternativeCostisUsed;
|
||||
}
|
||||
|
||||
|
|
@ -499,7 +504,9 @@ public abstract class AbilityImpl implements Ability {
|
|||
costs.add(fixedCost);
|
||||
}
|
||||
// set the xcosts to paid
|
||||
variableCost.setAmount(xValue);
|
||||
// no x events - rules from Unbound Flourishing:
|
||||
// - Spells with additional costs that include X won't be affected by Unbound Flourishing. X must be in the spell's mana cost.
|
||||
variableCost.setAmount(xValue, xValue, false);
|
||||
((Cost) variableCost).setPaid();
|
||||
String message = controller.getLogName() + " announces a value of " + xValue + " (" + variableCost.getActionText() + ')';
|
||||
announceString.append(message);
|
||||
|
|
@ -510,14 +517,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);
|
||||
|
|
@ -530,6 +537,13 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
}
|
||||
|
||||
public int handleManaXMultiplier(Game game, int value) {
|
||||
// some spells can change X value without new pays (Unbound Flourishing doubles X)
|
||||
GameEvent xEvent = GameEvent.getEvent(GameEvent.EventType.X_MANA_ANNOUNCE, getId(), getSourceId(), getControllerId(), value);
|
||||
game.replaceEvent(xEvent, this);
|
||||
return xEvent.getAmount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles X mana costs and sets manaCostsToPay.
|
||||
*
|
||||
|
|
@ -546,16 +560,22 @@ public abstract class AbilityImpl implements Ability {
|
|||
VariableManaCost variableManaCost = null;
|
||||
for (ManaCost cost : manaCostsToPay) {
|
||||
if (cost instanceof VariableManaCost) {
|
||||
variableManaCost = (VariableManaCost) cost;
|
||||
break; // only one VariableManCost per spell (or is it possible to have more?)
|
||||
if (variableManaCost == null) {
|
||||
variableManaCost = (VariableManaCost) cost;
|
||||
} else {
|
||||
// only one VariableManCost per spell (or is it possible to have more?)
|
||||
logger.error("Variable mana cost allowes only in one instance per ability: " + this);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (variableManaCost != null) {
|
||||
int xValue;
|
||||
if (!variableManaCost.isPaid()) { // should only happen for human players
|
||||
int xValue;
|
||||
int xValueMultiplier = handleManaXMultiplier(game, 1);
|
||||
if (!noMana) {
|
||||
xValue = controller.announceXMana(variableManaCost.getMinX(), variableManaCost.getMaxX(), "Announce the value for " + variableManaCost.getText(), game, this);
|
||||
int amountMana = xValue * variableManaCost.getMultiplier();
|
||||
xValue = controller.announceXMana(variableManaCost.getMinX(), variableManaCost.getMaxX(), xValueMultiplier,
|
||||
"Announce the value for " + variableManaCost.getText(), game, this);
|
||||
int amountMana = xValue * variableManaCost.getXInstancesCount();
|
||||
StringBuilder manaString = threadLocalBuilder.get();
|
||||
if (variableManaCost.getFilter() == null || variableManaCost.getFilter().isGeneric()) {
|
||||
manaString.append('{').append(amountMana).append('}');
|
||||
|
|
@ -584,7 +604,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
}
|
||||
manaCostsToPay.add(new ManaCostsImpl(manaString.toString()));
|
||||
manaCostsToPay.setX(amountMana);
|
||||
manaCostsToPay.setX(xValue * xValueMultiplier, amountMana);
|
||||
}
|
||||
variableManaCost.setPaid();
|
||||
}
|
||||
|
|
@ -611,7 +631,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);
|
||||
}
|
||||
|
||||
|
|
@ -639,7 +659,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
subAbility.setSourceId(sourceId);
|
||||
}
|
||||
}
|
||||
for (Watcher watcher : watchers) {
|
||||
for (Watcher watcher : getWatchers()) {
|
||||
watcher.setSourceId(sourceId);
|
||||
}
|
||||
|
||||
|
|
@ -712,7 +732,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
watcher.setSourceId(this.sourceId);
|
||||
watcher.setControllerId(this.controllerId);
|
||||
watchers.add(watcher);
|
||||
getWatchers().add(watcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -844,7 +864,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
if (getModes().getMode() != null) {
|
||||
return getModes().getMode().getTargets();
|
||||
}
|
||||
return null;
|
||||
return new Targets();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1145,11 +1165,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;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities;
|
||||
|
||||
import mage.abilities.costs.OptionalAdditionalModeSourceCosts;
|
||||
|
|
@ -10,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.*;
|
||||
|
||||
|
|
@ -19,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();
|
||||
|
|
@ -45,22 +49,27 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
this.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
for (Map.Entry<UUID, Mode> entry : modes.duplicateModes.entrySet()) {
|
||||
this.put(entry.getKey(), entry.getValue().copy());
|
||||
duplicateModes.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
duplicateToOriginalModeRefs.putAll(modes.duplicateToOriginalModeRefs);
|
||||
|
||||
this.minModes = modes.minModes;
|
||||
this.maxModes = modes.maxModes;
|
||||
this.selectedModes.addAll(modes.getSelectedModes());
|
||||
|
||||
if (modes.getSelectedModes().isEmpty()) {
|
||||
this.currentMode = values().iterator().next();
|
||||
} else {
|
||||
this.currentMode = get(modes.getMode().getId());
|
||||
}
|
||||
this.modeChooser = modes.modeChooser;
|
||||
this.eachModeOnlyOnce = modes.eachModeOnlyOnce;
|
||||
this.eachModeMoreThanOnce = modes.eachModeMoreThanOnce;
|
||||
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 {
|
||||
this.currentMode = get(modes.getMode().getId());
|
||||
}
|
||||
}
|
||||
|
||||
public Modes copy() {
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
package mage.abilities;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -24,7 +25,7 @@ public class SpecialActions extends AbilitiesImpl<SpecialAction> {
|
|||
* false = only non mana actions get returned
|
||||
* @return
|
||||
*/
|
||||
public LinkedHashMap<UUID, SpecialAction> getControlledBy(UUID controllerId, boolean manaAction) {
|
||||
public Map<UUID, SpecialAction> getControlledBy(UUID controllerId, boolean manaAction) {
|
||||
LinkedHashMap<UUID, SpecialAction> controlledBy = new LinkedHashMap<>();
|
||||
for (SpecialAction action: this) {
|
||||
if (action.isControlledBy(controllerId) && action.isManaAction() == manaAction) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
@ -55,18 +54,31 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
this.cardName = ability.cardName;
|
||||
}
|
||||
|
||||
/*
|
||||
* 7/5/19 - jgray1206 - Moved null != game.getContinuesEffects()... into this method instead of having it in
|
||||
* canActivate. There are abilities that directly use this method that should know when spells
|
||||
* can be casted that are affected by the CastAsInstant effect.
|
||||
* (i.e. Vizier of the Menagerie and issue #5816)
|
||||
*/
|
||||
public boolean spellCanBeActivatedRegularlyNow(UUID playerId, Game game) {
|
||||
MageObject object = game.getObject(sourceId);
|
||||
return timing == TimingRule.INSTANT
|
||||
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)
|
||||
|| game.canPlaySorcery(playerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
if (null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game) // check this first to allow Offering in main phase
|
||||
|| this.spellCanBeActivatedRegularlyNow(playerId, game)) {
|
||||
if (spellAbilityType == SpellAbilityType.SPLIT || spellAbilityType == SpellAbilityType.SPLIT_AFTERMATH) {
|
||||
if (this.spellCanBeActivatedRegularlyNow(playerId, game)) {
|
||||
if (spellAbilityType == SpellAbilityType.SPLIT
|
||||
|| spellAbilityType == SpellAbilityType.SPLIT_AFTERMATH) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
// fix for Gitaxian Probe and casting opponent's spells
|
||||
|
|
@ -78,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();
|
||||
|
|
@ -87,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();
|
||||
}
|
||||
}
|
||||
|
|
@ -162,13 +175,15 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// mana cost instances
|
||||
for (ManaCost manaCost : card.getManaCost()) {
|
||||
if (manaCost instanceof VariableManaCost) {
|
||||
xMultiplier = ((VariableManaCost) manaCost).getMultiplier();
|
||||
xMultiplier = ((VariableManaCost) manaCost).getXInstancesCount();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// mana cost final X value
|
||||
boolean hasNonManaXCost = false;
|
||||
for (Cost cost : getCosts()) {
|
||||
if (cost instanceof VariableCost) {
|
||||
|
|
@ -184,6 +199,11 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
return amount * xMultiplier;
|
||||
}
|
||||
|
||||
public void setCardName(String cardName) {
|
||||
this.cardName = cardName;
|
||||
setSpellName();
|
||||
}
|
||||
|
||||
private void setSpellName() {
|
||||
switch (spellAbilityType) {
|
||||
case SPLIT_FUSED:
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
|
||||
package mage.abilities;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public abstract class StateTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
|
@ -23,11 +22,7 @@ public abstract class StateTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
public boolean canTrigger(Game game) {
|
||||
//20100716 - 603.8
|
||||
Boolean triggered = (Boolean) game.getState().getValue(getSourceId().toString() + "triggered");
|
||||
if (triggered == null) {
|
||||
triggered = Boolean.FALSE;
|
||||
}
|
||||
return !triggered;
|
||||
return !Boolean.TRUE.equals(game.getState().getValue(getSourceId().toString() + "triggered"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package mage.abilities;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.AbilityType;
|
||||
|
|
@ -13,8 +11,10 @@ import mage.game.events.GameEvent.EventType;
|
|||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public abstract class TriggeredAbilityImpl extends AbilityImpl implements TriggeredAbility {
|
||||
|
|
@ -111,7 +111,8 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
|||
|| ruleLow.startsWith("untap")
|
||||
|| ruleLow.startsWith("put")
|
||||
|| ruleLow.startsWith("remove")
|
||||
|| ruleLow.startsWith("counter")) {
|
||||
|| ruleLow.startsWith("counter")
|
||||
|| ruleLow.startsWith("goad")) {
|
||||
sb.append("you may ");
|
||||
} else if (!ruleLow.startsWith("its controller may")) {
|
||||
sb.append("you may have ");
|
||||
|
|
@ -164,7 +165,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
|||
} else if (((ZoneChangeEvent) event).getTarget() != null) {
|
||||
source = ((ZoneChangeEvent) event).getTarget();
|
||||
} else {
|
||||
source = game.getLastKnownInformation(getSourceId(), ((ZoneChangeEvent) event).getZone());
|
||||
source = game.getLastKnownInformation(getSourceId(), event.getZone());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,52 +1,28 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.constants.TimingRule;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* @author Plopman
|
||||
* @author Plopman, JayDi85
|
||||
*/
|
||||
public class CastCommanderAbility extends SpellAbility {
|
||||
|
||||
public CastCommanderAbility(Card card) {
|
||||
super(card.getManaCost(), card.getName(), Zone.COMMAND, SpellAbilityType.BASE);
|
||||
if (card.getSpellAbility() != null) {
|
||||
this.getCosts().addAll(card.getSpellAbility().getCosts().copy());
|
||||
this.getEffects().addAll(card.getSpellAbility().getEffects().copy());
|
||||
this.getTargets().addAll(card.getSpellAbility().getTargets().copy());
|
||||
this.timing = card.getSpellAbility().getTiming();
|
||||
} else {
|
||||
this.costs = new CostsImpl<>();
|
||||
this.timing = TimingRule.SORCERY;
|
||||
}
|
||||
this.usesStack = true;
|
||||
this.controllerId = card.getOwnerId();
|
||||
this.sourceId = card.getId();
|
||||
private String ruleText;
|
||||
|
||||
public CastCommanderAbility(Card card, SpellAbility spellTemplate) {
|
||||
super(spellTemplate);
|
||||
this.newId();
|
||||
this.setCardName(spellTemplate.getCardName());
|
||||
this.zone = Zone.COMMAND;
|
||||
this.spellAbilityType = spellTemplate.getSpellAbilityType();
|
||||
this.ruleText = spellTemplate.getRule(); // need to support custom rule texts like OverloadAbility
|
||||
}
|
||||
|
||||
public CastCommanderAbility(final CastCommanderAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activate(Game game, boolean noMana) {
|
||||
if (super.activate(game, noMana)) {
|
||||
// save amount of times commander was cast
|
||||
Integer castCount = (Integer) game.getState().getValue(sourceId + "_castCount");
|
||||
if (castCount == null) {
|
||||
castCount = 1;
|
||||
} else {
|
||||
castCount++;
|
||||
}
|
||||
game.getState().setValue(sourceId + "_castCount", castCount);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
this.ruleText = ability.ruleText;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -54,4 +30,9 @@ public class CastCommanderAbility extends SpellAbility {
|
|||
return new CastCommanderAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return ruleText;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedCreatureEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class DealtDamageToSourceTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
|
@ -18,16 +17,16 @@ public class DealtDamageToSourceTriggeredAbility extends TriggeredAbilityImpl {
|
|||
private final boolean useValue;
|
||||
private boolean usedForCombatDamageStep;
|
||||
|
||||
public DealtDamageToSourceTriggeredAbility(Zone zone, Effect effect, boolean optional) {
|
||||
this(zone, effect, optional, false);
|
||||
public DealtDamageToSourceTriggeredAbility(Effect effect, boolean optional) {
|
||||
this(effect, optional, false);
|
||||
}
|
||||
|
||||
public DealtDamageToSourceTriggeredAbility(Zone zone, Effect effect, boolean optional, boolean enrage) {
|
||||
this(zone, effect, optional, enrage, false);
|
||||
public DealtDamageToSourceTriggeredAbility(Effect effect, boolean optional, boolean enrage) {
|
||||
this(effect, optional, enrage, false);
|
||||
}
|
||||
|
||||
public DealtDamageToSourceTriggeredAbility(Zone zone, Effect effect, boolean optional, boolean enrage, boolean useValue) {
|
||||
super(zone, effect, optional);
|
||||
public DealtDamageToSourceTriggeredAbility(Effect effect, boolean optional, boolean enrage, boolean useValue) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
this.enrage = enrage;
|
||||
this.useValue = useValue;
|
||||
this.usedForCombatDamageStep = false;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.PlayLandAbility;
|
||||
import mage.constants.Zone;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class PlayLandAsCommanderAbility extends PlayLandAbility {
|
||||
|
||||
public PlayLandAsCommanderAbility(PlayLandAbility originalAbility) {
|
||||
super(originalAbility);
|
||||
zone = Zone.COMMAND;
|
||||
}
|
||||
|
||||
private PlayLandAsCommanderAbility(PlayLandAsCommanderAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayLandAsCommanderAbility copy() {
|
||||
return new PlayLandAsCommanderAbility(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* For oathbreaker game mode
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class SignatureSpellCastOnlyWithOathbreakerEffect extends ContinuousRuleModifyingEffectImpl {
|
||||
|
||||
private final Condition condition;
|
||||
private final UUID signatureSpell;
|
||||
|
||||
public SignatureSpellCastOnlyWithOathbreakerEffect(Condition condition, UUID signatureSpell) {
|
||||
super(Duration.EndOfGame, Outcome.Detriment);
|
||||
this.condition = condition;
|
||||
this.signatureSpell = signatureSpell;
|
||||
staticText = setText();
|
||||
}
|
||||
|
||||
private SignatureSpellCastOnlyWithOathbreakerEffect(final SignatureSpellCastOnlyWithOathbreakerEffect effect) {
|
||||
super(effect);
|
||||
this.condition = effect.condition;
|
||||
this.signatureSpell = effect.signatureSpell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.CAST_SPELL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (event.getSourceId().equals(signatureSpell)) {
|
||||
return condition != null && !condition.apply(game, source);
|
||||
}
|
||||
return false; // cast not prevented by this effect
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignatureSpellCastOnlyWithOathbreakerEffect copy() {
|
||||
return new SignatureSpellCastOnlyWithOathbreakerEffect(this);
|
||||
}
|
||||
|
||||
private String setText() {
|
||||
StringBuilder sb = new StringBuilder("cast this spell only ");
|
||||
if (condition != null) {
|
||||
sb.append(' ').append(condition.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -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,4 +1,3 @@
|
|||
|
||||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -20,7 +19,7 @@ public enum BuybackCondition implements Condition {
|
|||
if (card != null) {
|
||||
return card.getAbilities().stream()
|
||||
.filter(a -> a instanceof BuybackAbility)
|
||||
.anyMatch(Ability::isActivated);
|
||||
.anyMatch(a -> ((BuybackAbility) a).isBuybackActivated(game));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.constants.CommanderCardType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
|
@ -22,7 +22,7 @@ public enum CommanderInPlayCondition implements Condition {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
for (UUID commanderId : controller.getCommandersIds()) {
|
||||
for (UUID commanderId : game.getCommandersIds(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER)) {
|
||||
Permanent commander = game.getPermanent(commanderId);
|
||||
if (commander != null && commander.isControlledBy(source.getControllerId())) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.cards.Card;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.permanent.PermanentIdPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.util.ManaUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* For Oathbreaker game mode
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class OathbreakerOnBattlefieldCondition implements Condition {
|
||||
|
||||
private UUID playerId;
|
||||
private FilterControlledPermanent filter;
|
||||
private String compatibleNames;
|
||||
|
||||
public OathbreakerOnBattlefieldCondition(Game game, UUID playerId, UUID signatureSpellId, Set<UUID> oathbreakersToSearch) {
|
||||
this.playerId = playerId;
|
||||
this.filter = new FilterControlledPermanent("oathbreaker on battlefield");
|
||||
|
||||
Card spell = game.getCard(signatureSpellId);
|
||||
FilterMana spellColors = spell != null ? spell.getColorIdentity() : null;
|
||||
|
||||
// spell can be casted by any compatible oathbreakers
|
||||
List<PermanentIdPredicate> compatibleList = new ArrayList<>();
|
||||
List<String> compatibleNames = new ArrayList<>();
|
||||
if (oathbreakersToSearch != null && !oathbreakersToSearch.isEmpty()) {
|
||||
for (UUID id : oathbreakersToSearch) {
|
||||
Card commander = game.getCard(id);
|
||||
if (commander != null && ManaUtil.isColorIdentityCompatible(commander.getColorIdentity(), spellColors)) {
|
||||
compatibleList.add(new PermanentIdPredicate(id));
|
||||
compatibleNames.add(commander.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
this.compatibleNames = String.join("; ", compatibleNames);
|
||||
|
||||
if (compatibleList.isEmpty()) {
|
||||
// random id to disable condition
|
||||
this.filter.add(new PermanentIdPredicate(UUID.randomUUID()));
|
||||
} else {
|
||||
// oathbreaker on battlefield
|
||||
this.filter.add(Predicates.or(compatibleList));
|
||||
}
|
||||
}
|
||||
|
||||
public String getCompatibleNames() {
|
||||
return !this.compatibleNames.isEmpty() ? this.compatibleNames : "you haven't compatible oathbreaker";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// source.getSourceId() is null for commander's effects
|
||||
int permanentsOnBattlefield = game.getBattlefield().count(this.filter, source.getSourceId(), playerId, game);
|
||||
return permanentsOnBattlefield > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filter.getMessage();
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public enum RaidCondition implements Condition {
|
|||
return watcher != null && watcher.getNumberOfAttackersCurrentTurn(source.getControllerId()) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "if you attacked with a creature this turn";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +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}.
|
||||
*
|
||||
* Example of such source costs adjusting:
|
||||
* {@link mage.abilities.keyword.AffinityForArtifactsAbility}
|
||||
*
|
||||
* @author nantuko
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface AdjustingSourceCosts {
|
||||
|
||||
void adjustCosts(Ability ability, Game game);
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ public class AlternativeCost2Impl<T extends AlternativeCost2Impl<T>> extends Cos
|
|||
this.name = name;
|
||||
this.delimiter = delimiter;
|
||||
if (reminderText != null) {
|
||||
this.reminderText = new StringBuilder("<i>").append(reminderText).append("</i>").toString();
|
||||
this.reminderText = "<i>" + reminderText + "</i>";
|
||||
}
|
||||
this.add(cost);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.abilities.costs;
|
||||
|
||||
import java.util.Iterator;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
|
|
@ -16,13 +15,14 @@ import mage.game.Game;
|
|||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class AlternativeCostSourceAbility extends StaticAbility implements AlternativeSourceCosts {
|
||||
|
||||
Costs<AlternativeCost2> alternateCosts = new CostsImpl<>();
|
||||
private Costs<AlternativeCost2> alternateCosts = new CostsImpl<>();
|
||||
protected Condition condition;
|
||||
protected String rule;
|
||||
protected FilterCard filter;
|
||||
|
|
@ -46,14 +46,13 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param cost alternate cost to pay
|
||||
* @param cost alternate cost to pay
|
||||
* @param condition only if the condition is true it's possible to use the
|
||||
* alternate costs
|
||||
* @param rule if != null used as rule text
|
||||
* @param filter filters the cards this alternate cost can be applied to
|
||||
* @param onlyMana if true only the mana costs are replaced by this costs,
|
||||
* other costs stay untouched
|
||||
* alternate costs
|
||||
* @param rule if != null used as rule text
|
||||
* @param filter filters the cards this alternate cost can be applied to
|
||||
* @param onlyMana if true only the mana costs are replaced by this costs,
|
||||
* other costs stay untouched
|
||||
*/
|
||||
public AlternativeCostSourceAbility(Cost cost, Condition condition, String rule, FilterCard filter, boolean onlyMana) {
|
||||
super(Zone.ALL, null);
|
||||
|
|
@ -149,10 +148,9 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
|
|||
if (!onlyMana) {
|
||||
ability.getCosts().clear();
|
||||
}
|
||||
for (Cost cost : alternativeCostsToCheck) {
|
||||
AlternativeCost2 alternateCost = (AlternativeCost2) cost;
|
||||
for (AlternativeCost2 alternateCost : alternativeCostsToCheck) {
|
||||
alternateCost.activate();
|
||||
for (Iterator it = ((Costs) alternateCost).iterator(); it.hasNext();) {
|
||||
for (Iterator it = ((Costs) alternateCost).iterator(); it.hasNext(); ) {
|
||||
Cost costDeailed = (Cost) it.next();
|
||||
if (costDeailed instanceof ManaCost) {
|
||||
ability.getManaCostsToPay().add((ManaCost) costDeailed.copy());
|
||||
|
|
@ -223,7 +221,8 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
|
|||
if (alternativeCost.getCost() instanceof ManaCost) {
|
||||
sb.append("pay ");
|
||||
}
|
||||
sb.append(alternativeCost.getText(true));
|
||||
String text = alternativeCost.getText(true);
|
||||
sb.append(Character.toLowerCase(text.charAt(0)) + text.substring(1));
|
||||
}
|
||||
++numberCosts;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
|
||||
package mage.abilities.costs;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class OptionalAdditionalCostImpl extends CostsImpl<Cost> implements OptionalAdditionalCost {
|
||||
|
|
@ -32,9 +30,10 @@ public class OptionalAdditionalCostImpl extends CostsImpl<Cost> implements Optio
|
|||
super(cost);
|
||||
this.name = cost.name;
|
||||
this.reminderText = cost.reminderText;
|
||||
this.delimiter = cost.delimiter;
|
||||
this.activated = cost.activated;
|
||||
this.activatedCounter = cost.activatedCounter;
|
||||
this.delimiter = cost.delimiter;
|
||||
this.repeatable = cost.repeatable;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -77,7 +76,7 @@ public class OptionalAdditionalCostImpl extends CostsImpl<Cost> implements Optio
|
|||
* message.
|
||||
*
|
||||
* @param position - if there are multiple costs, it's the postion the cost
|
||||
* is set (starting with 0)
|
||||
* is set (starting with 0)
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
|
|
@ -95,7 +94,6 @@ public class OptionalAdditionalCostImpl extends CostsImpl<Cost> implements Optio
|
|||
|
||||
/**
|
||||
* If the player intends to pay the cost, the cost will be activated
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void activate() {
|
||||
|
|
@ -105,7 +103,6 @@ public class OptionalAdditionalCostImpl extends CostsImpl<Cost> implements Optio
|
|||
|
||||
/**
|
||||
* Reset the activate and count information
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
|
|
@ -145,6 +142,7 @@ public class OptionalAdditionalCostImpl extends CostsImpl<Cost> implements Optio
|
|||
|
||||
/**
|
||||
* Returns the number of times the cost was activated
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
|
||||
|
||||
package mage.abilities.costs;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public interface VariableCost {
|
||||
/**
|
||||
* Returns the variable amount if already set
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int getAmount();
|
||||
|
||||
/**
|
||||
* Sets the variable amount
|
||||
*
|
||||
* @param amount
|
||||
* @param xValue - value of X
|
||||
* @param xPay - total value of pays for X (X * xMultiplier * xInstancesCount)
|
||||
* @param isPayed - is that was real payed or just value setup
|
||||
*/
|
||||
void setAmount(int amount);
|
||||
void setAmount(int xValue, int xPay, boolean isPayed);
|
||||
|
||||
/**
|
||||
* returns the action text (e.g. "creature cards to exile from your hand", "life to pay")
|
||||
|
|
@ -29,6 +29,7 @@ public interface VariableCost {
|
|||
* @return
|
||||
*/
|
||||
String getActionText();
|
||||
|
||||
/**
|
||||
* Return a min value to announce
|
||||
*
|
||||
|
|
@ -37,6 +38,7 @@ public interface VariableCost {
|
|||
* @return
|
||||
*/
|
||||
int getMinValue(Ability source, Game game);
|
||||
|
||||
/**
|
||||
* Returns a max value to announce
|
||||
*
|
||||
|
|
@ -45,13 +47,16 @@ public interface VariableCost {
|
|||
* @return
|
||||
*/
|
||||
int getMaxValue(Ability source, Game game);
|
||||
|
||||
/**
|
||||
* Asks the controller to announce the variable value
|
||||
*
|
||||
* @param source
|
||||
* @param game
|
||||
* @return
|
||||
*/
|
||||
int announceXValue(Ability source, Game game);
|
||||
|
||||
/**
|
||||
* Returns a fixed cost with the announced variable value
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.abilities.costs;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.mana.ManaAbility;
|
||||
import mage.game.Game;
|
||||
|
|
@ -10,8 +8,9 @@ import mage.players.Player;
|
|||
import mage.target.Target;
|
||||
import mage.target.Targets;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public abstract class VariableCostImpl implements Cost, VariableCost {
|
||||
|
|
@ -29,10 +28,9 @@ public abstract class VariableCostImpl implements Cost, VariableCost {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param xText string for the defined value
|
||||
* @param xText string for the defined value
|
||||
* @param actionText what happens with the value (e.g. "to tap", "to exile
|
||||
* from your graveyard")
|
||||
* from your graveyard")
|
||||
*/
|
||||
public VariableCostImpl(String xText, String actionText) {
|
||||
id = UUID.randomUUID();
|
||||
|
|
@ -125,8 +123,8 @@ public abstract class VariableCostImpl implements Cost, VariableCost {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setAmount(int amount) {
|
||||
amountPaid = amount;
|
||||
public void setAmount(int xValue, int xPay, boolean isPayed) {
|
||||
amountPaid = xPay;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -40,9 +40,8 @@ public class DiscardSourceCost extends CostImpl {
|
|||
Player player = game.getPlayer(controllerId);
|
||||
if (player != null) {
|
||||
Card card = player.getHand().get(sourceId, game);
|
||||
if (card != null) {
|
||||
paid = player.discard(card, null, game);
|
||||
}
|
||||
paid = player.discard(card, null, game);
|
||||
|
||||
}
|
||||
return paid;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
package mage.abilities.costs.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.CostImpl;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.game.Game;
|
||||
import mage.util.ManaUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class DynamicValueGenericManaCost extends CostImpl {
|
||||
|
||||
DynamicValue amount;
|
||||
|
||||
public DynamicValueGenericManaCost(DynamicValue amount, String text) {
|
||||
this.amount = amount;
|
||||
setText(text);
|
||||
}
|
||||
|
||||
public DynamicValueGenericManaCost(DynamicValueGenericManaCost cost) {
|
||||
super(cost);
|
||||
this.amount = cost.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
|
||||
Cost cost = ManaUtil.createManaCost(amount, game, ability, null);
|
||||
return cost.canPay(ability, sourceId, controllerId, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
|
||||
Cost cost = ManaUtil.createManaCost(amount, game, ability, null);
|
||||
paid = cost.pay(ability, game, sourceId, controllerId, noMana);
|
||||
return paid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicValueGenericManaCost copy() {
|
||||
return new DynamicValueGenericManaCost(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +1,5 @@
|
|||
package mage.abilities.costs.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.CostImpl;
|
||||
|
|
@ -16,8 +13,11 @@ import mage.game.Game;
|
|||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class ExileFromHandCost extends CostImpl {
|
||||
|
|
@ -30,10 +30,9 @@ public class ExileFromHandCost extends CostImpl {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param target
|
||||
* @param setXFromCMC the spells X value on the stack is set to the
|
||||
* converted mana costs of the exiled card
|
||||
* converted mana costs of the exiled card
|
||||
*/
|
||||
public ExileFromHandCost(TargetCardInHand target, boolean setXFromCMC) {
|
||||
this.addTarget(target);
|
||||
|
|
@ -68,7 +67,9 @@ public class ExileFromHandCost extends CostImpl {
|
|||
paid = true;
|
||||
if (setXFromCMC) {
|
||||
VariableManaCost vmc = new VariableManaCost();
|
||||
vmc.setAmount(cmc);
|
||||
// no x events - rules from Unbound Flourishing:
|
||||
// - Spells with additional costs that include X won't be affected by Unbound Flourishing. X must be in the spell's mana cost.
|
||||
vmc.setAmount(cmc, cmc, false);
|
||||
vmc.setPaid();
|
||||
ability.getManaCostsToPay().add(vmc);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.costs.mana;
|
||||
|
||||
import mage.Mana;
|
||||
|
|
@ -12,6 +11,9 @@ public class GenericManaCost extends ManaCostImpl {
|
|||
|
||||
protected int mana;
|
||||
|
||||
/**
|
||||
* warning, use ManaUtil.createManaCost to create generic cost
|
||||
*/
|
||||
public GenericManaCost(int mana) {
|
||||
this.mana = mana;
|
||||
this.cost = Mana.GenericMana(mana);
|
||||
|
|
@ -42,7 +44,7 @@ public class GenericManaCost extends ManaCostImpl {
|
|||
|
||||
@Override
|
||||
public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costsToPay) {
|
||||
this.assignGeneric(ability, game, pool, mana, costsToPay);
|
||||
this.assignGeneric(ability, game, pool, mana, null, costsToPay);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
|
||||
package mage.abilities.costs.mana;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
|
|
@ -12,11 +8,16 @@ import mage.abilities.mana.ManaOptions;
|
|||
import mage.constants.ColoredManaSymbol;
|
||||
import mage.constants.ManaType;
|
||||
import mage.filter.Filter;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.game.Game;
|
||||
import mage.players.ManaPool;
|
||||
import mage.players.Player;
|
||||
import mage.util.ManaUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class ManaCostImpl extends CostImpl implements ManaCost {
|
||||
|
||||
protected Mana payment;
|
||||
|
|
@ -143,33 +144,49 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean assignGeneric(Ability ability, Game game, ManaPool pool, int mana, Cost costToPay) {
|
||||
int conditionalCount = pool.getConditionalCount(ability, game, null, costToPay);
|
||||
protected boolean assignGeneric(Ability ability, Game game, ManaPool pool, int mana, FilterMana filterMana, Cost costToPay) {
|
||||
int conditionalCount = pool.getConditionalCount(ability, game, filterMana, costToPay);
|
||||
while (mana > payment.count() && (pool.count() > 0 || conditionalCount > 0)) {
|
||||
if (pool.pay(ManaType.COLORLESS, ability, sourceFilter, game, costToPay, usedManaToPay)) {
|
||||
// try to use different mana to pay (conditional mana will used in pool.pay)
|
||||
// filterMana can be null, uses for spells like "spend only black mana on X"
|
||||
|
||||
// {C}
|
||||
if ((filterMana == null || filterMana.isColorless()) && pool.pay(ManaType.COLORLESS, ability, sourceFilter, game, costToPay, usedManaToPay)) {
|
||||
this.payment.increaseColorless();
|
||||
continue;
|
||||
}
|
||||
if (pool.pay(ManaType.BLACK, ability, sourceFilter, game, costToPay, usedManaToPay)) {
|
||||
|
||||
// {B}
|
||||
if ((filterMana == null || filterMana.isBlack()) && pool.pay(ManaType.BLACK, ability, sourceFilter, game, costToPay, usedManaToPay)) {
|
||||
this.payment.increaseBlack();
|
||||
continue;
|
||||
}
|
||||
if (pool.pay(ManaType.BLUE, ability, sourceFilter, game, costToPay, usedManaToPay)) {
|
||||
|
||||
// {U}
|
||||
if ((filterMana == null || filterMana.isBlue()) && pool.pay(ManaType.BLUE, ability, sourceFilter, game, costToPay, usedManaToPay)) {
|
||||
this.payment.increaseBlue();
|
||||
continue;
|
||||
}
|
||||
if (pool.pay(ManaType.WHITE, ability, sourceFilter, game, costToPay, usedManaToPay)) {
|
||||
|
||||
// {W}
|
||||
if ((filterMana == null || filterMana.isWhite()) && pool.pay(ManaType.WHITE, ability, sourceFilter, game, costToPay, usedManaToPay)) {
|
||||
this.payment.increaseWhite();
|
||||
continue;
|
||||
}
|
||||
if (pool.pay(ManaType.GREEN, ability, sourceFilter, game, costToPay, usedManaToPay)) {
|
||||
|
||||
// {G}
|
||||
if ((filterMana == null || filterMana.isGreen()) && pool.pay(ManaType.GREEN, ability, sourceFilter, game, costToPay, usedManaToPay)) {
|
||||
this.payment.increaseGreen();
|
||||
continue;
|
||||
}
|
||||
if (pool.pay(ManaType.RED, ability, sourceFilter, game, costToPay, usedManaToPay)) {
|
||||
|
||||
// {R}
|
||||
if ((filterMana == null || filterMana.isRed()) && pool.pay(ManaType.RED, ability, sourceFilter, game, costToPay, usedManaToPay)) {
|
||||
this.payment.increaseRed();
|
||||
continue;
|
||||
}
|
||||
|
||||
// nothing to pay
|
||||
break;
|
||||
}
|
||||
return mana > payment.count();
|
||||
|
|
@ -208,14 +225,14 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
|
|||
}
|
||||
Player player = game.getPlayer(controllerId);
|
||||
if (!player.getManaPool().isForcedToPay()) {
|
||||
assignPayment(game, ability, player.getManaPool(), costToPay);
|
||||
assignPayment(game, ability, player.getManaPool(), costToPay != null ? costToPay : this);
|
||||
}
|
||||
game.getState().getSpecialActions().removeManaActions();
|
||||
while (!isPaid()) {
|
||||
ManaCost unpaid = this.getUnpaid();
|
||||
String promptText = ManaUtil.addSpecialManaPayAbilities(ability, game, unpaid);
|
||||
if (player.playMana(ability, unpaid, promptText, game)) {
|
||||
assignPayment(game, ability, player.getManaPool(), costToPay);
|
||||
assignPayment(game, ability, player.getManaPool(), costToPay != null ? costToPay : this);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.costs.mana;
|
||||
|
||||
import mage.Mana;
|
||||
|
|
@ -11,9 +10,8 @@ import java.util.UUID;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
* @param <T>
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public interface ManaCosts<T extends ManaCost> extends List<T>, ManaCost {
|
||||
|
||||
|
|
@ -21,9 +19,15 @@ public interface ManaCosts<T extends ManaCost> extends List<T>, ManaCost {
|
|||
|
||||
List<VariableCost> getVariableCosts();
|
||||
|
||||
boolean containsX();
|
||||
|
||||
int getX();
|
||||
|
||||
void setX(int x);
|
||||
/**
|
||||
* @param xValue final X value -- announced X * xMultiplier, where xMultiplier can be changed by replace events like Unbound Flourishing)
|
||||
* @param xPay real number of pay amount (x * xMultiplier * xInstances, where xInstances is number of {X} in pay like 1, 2, 3)
|
||||
*/
|
||||
void setX(int xValue, int xPay);
|
||||
|
||||
void load(String mana);
|
||||
|
||||
|
|
@ -41,7 +45,7 @@ public interface ManaCosts<T extends ManaCost> extends List<T>, ManaCost {
|
|||
static ManaCosts<ManaCost> removeVariableManaCost(ManaCosts<ManaCost> m) {
|
||||
return m.stream()
|
||||
.filter(mc -> !(mc instanceof VariableManaCost))
|
||||
.collect(Collectors.toCollection(ManaCostsImpl<ManaCost>::new));
|
||||
.collect(Collectors.toCollection(ManaCostsImpl::new));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.abilities.costs.mana;
|
||||
|
||||
import java.util.*;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
|
|
@ -14,15 +12,18 @@ 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;
|
||||
import mage.target.Targets;
|
||||
import mage.util.ManaUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
* @param <T>
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements ManaCosts<T> {
|
||||
|
||||
|
|
@ -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() {
|
||||
|
|
@ -213,6 +270,11 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
return variableCosts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsX() {
|
||||
return !getVariableCosts().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getX() {
|
||||
int amount = 0;
|
||||
|
|
@ -224,10 +286,10 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setX(int x) {
|
||||
public void setX(int xValue, int xPay) {
|
||||
List<VariableCost> variableCosts = getVariableCosts();
|
||||
if (!variableCosts.isEmpty()) {
|
||||
variableCosts.get(0).setAmount(x);
|
||||
variableCosts.get(0).setAmount(xValue, xPay, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -339,7 +401,12 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
if (player != null) {
|
||||
game.undo(playerId);
|
||||
this.clearPaid();
|
||||
this.setX(referenceCosts.getX());
|
||||
|
||||
// TODO: checks Word of Command with Unbound Flourishing's X multiplier
|
||||
// TODO: checks Word of Command with {X}{X} cards
|
||||
int xValue = referenceCosts.getX();
|
||||
this.setX(xValue, xValue);
|
||||
|
||||
player.getManaPool().restoreMana(pool.getPoolBookmark());
|
||||
game.bookmarkState();
|
||||
}
|
||||
|
|
@ -378,15 +445,15 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
} else if (!symbol.equals("X")) {
|
||||
this.add(new ColoredManaCost(ColoredManaSymbol.lookup(symbol.charAt(0))));
|
||||
} else // check X wasn't added before
|
||||
if (modifierForX == 0) {
|
||||
// count X occurence
|
||||
for (String s : symbols) {
|
||||
if (s.equals("X")) {
|
||||
modifierForX++;
|
||||
if (modifierForX == 0) {
|
||||
// count X occurence
|
||||
for (String s : symbols) {
|
||||
if (s.equals("X")) {
|
||||
modifierForX++;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.add(new VariableManaCost(modifierForX));
|
||||
} //TODO: handle multiple {X} and/or {Y} symbols
|
||||
this.add(new VariableManaCost(modifierForX));
|
||||
} //TODO: handle multiple {X} and/or {Y} symbols
|
||||
} else if (Character.isDigit(symbol.charAt(0))) {
|
||||
this.add(new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2))));
|
||||
} else if (symbol.contains("P")) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
|
||||
package mage.abilities.costs.mana;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
|
|
@ -10,6 +7,9 @@ import mage.constants.ColoredManaSymbol;
|
|||
import mage.game.Game;
|
||||
import mage.players.ManaPool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MonoHybridManaCost extends ManaCostImpl {
|
||||
|
||||
private final ColoredManaSymbol mana;
|
||||
|
|
@ -45,7 +45,7 @@ public class MonoHybridManaCost extends ManaCostImpl {
|
|||
@Override
|
||||
public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) {
|
||||
if (!assignColored(ability, game, pool, mana, costToPay)) {
|
||||
assignGeneric(ability, game, pool, mana2, costToPay);
|
||||
assignGeneric(ability, game, pool, mana2, null, costToPay);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.costs.mana;
|
||||
|
||||
import mage.Mana;
|
||||
|
|
@ -36,7 +35,7 @@ public class SnowManaCost extends ManaCostImpl {
|
|||
|
||||
@Override
|
||||
public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) {
|
||||
this.assignGeneric(ability, game, pool, 1, costToPay);
|
||||
this.assignGeneric(ability, game, pool, 1, null, costToPay);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.costs.mana;
|
||||
|
||||
import mage.Mana;
|
||||
|
|
@ -11,13 +10,20 @@ import mage.game.Game;
|
|||
import mage.players.ManaPool;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
* @author BetaSteward_at_googlemail.com, JayDi85
|
||||
*/
|
||||
public class VariableManaCost extends ManaCostImpl implements VariableCost {
|
||||
public final class VariableManaCost extends ManaCostImpl implements VariableCost {
|
||||
|
||||
protected int multiplier;
|
||||
protected FilterMana filter;
|
||||
// variable mana cost usage on 2019-06-20:
|
||||
// 1. as X value in spell/ability cast (announce X, set VariableManaCost as paid and add generic mana to pay instead)
|
||||
// 2. as X value in direct pay (X already announced, cost is unpaid, need direct pay)
|
||||
|
||||
protected int xInstancesCount; // number of {X} instances in cost like {X} or {X}{X}
|
||||
protected int xValue = 0; // final X value after announce and replace events
|
||||
protected int xPay = 0; // final/total need pay after announce and replace events (example: {X}{X}, X=3, xPay = 6)
|
||||
protected boolean wasAnnounced = false;
|
||||
|
||||
protected FilterMana filter; // mana filter that can be used for that cost
|
||||
protected int minX = 0;
|
||||
protected int maxX = Integer.MAX_VALUE;
|
||||
|
||||
|
|
@ -25,15 +31,18 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost {
|
|||
this(1);
|
||||
}
|
||||
|
||||
public VariableManaCost(int multiplier) {
|
||||
this.multiplier = multiplier;
|
||||
public VariableManaCost(int xInstancesCount) {
|
||||
this.xInstancesCount = xInstancesCount;
|
||||
this.cost = new Mana();
|
||||
options.add(new Mana());
|
||||
}
|
||||
|
||||
public VariableManaCost(final VariableManaCost manaCost) {
|
||||
super(manaCost);
|
||||
this.multiplier = manaCost.multiplier;
|
||||
this.xInstancesCount = manaCost.xInstancesCount;
|
||||
this.xValue = manaCost.xValue;
|
||||
this.xPay = manaCost.xPay;
|
||||
this.wasAnnounced = manaCost.wasAnnounced;
|
||||
if (manaCost.filter != null) {
|
||||
this.filter = manaCost.filter.copy();
|
||||
}
|
||||
|
|
@ -48,16 +57,15 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost {
|
|||
|
||||
@Override
|
||||
public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) {
|
||||
payment.add(pool.getMana(filter));
|
||||
payment.add(pool.getAllConditionalMana(ability, game, filter));
|
||||
pool.payX(ability, game, filter);
|
||||
// X mana cost always pays as generic mana
|
||||
this.assignGeneric(ability, game, pool, xPay, filter, costToPay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
if (multiplier > 1) {
|
||||
StringBuilder symbol = new StringBuilder(multiplier);
|
||||
for (int i = 0; i < multiplier; i++) {
|
||||
if (xInstancesCount > 1) {
|
||||
StringBuilder symbol = new StringBuilder(xInstancesCount);
|
||||
for (int i = 0; i < xInstancesCount; i++) {
|
||||
symbol.append("{X}");
|
||||
}
|
||||
return symbol.toString();
|
||||
|
|
@ -66,6 +74,14 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPaid() {
|
||||
if (!wasAnnounced) return false;
|
||||
if (paid) return true;
|
||||
|
||||
return this.isColorlessPaid(xPay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VariableManaCost getUnpaid() {
|
||||
return this;
|
||||
|
|
@ -73,17 +89,24 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost {
|
|||
|
||||
@Override
|
||||
public int getAmount() {
|
||||
return payment.count() / multiplier;
|
||||
// must return X value
|
||||
return this.xValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAmount(int amount) {
|
||||
payment.setGeneric(amount);
|
||||
public void setAmount(int xValue, int xPay, boolean isPayed) {
|
||||
// xPay is total pay value (X * instances)
|
||||
this.xValue = xValue;
|
||||
this.xPay = xPay;
|
||||
if (isPayed) {
|
||||
payment.setGeneric(xPay);
|
||||
}
|
||||
this.wasAnnounced = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testPay(Mana testMana) {
|
||||
return true;
|
||||
return true; // TODO: need rework to generic mana style?
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -91,8 +114,8 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost {
|
|||
return new VariableManaCost(this);
|
||||
}
|
||||
|
||||
public int getMultiplier() {
|
||||
return multiplier;
|
||||
public int getXInstancesCount() {
|
||||
return this.xInstancesCount;
|
||||
}
|
||||
|
||||
public int getMinX() {
|
||||
|
|
@ -118,27 +141,27 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost {
|
|||
|
||||
@Override
|
||||
public int announceXValue(Ability source, Game game) {
|
||||
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cost getFixedCostsFromAnnouncedValue(int xValue) {
|
||||
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActionText() {
|
||||
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinValue(Ability source, Game game) {
|
||||
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxValue(Ability source, Game game) {
|
||||
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
public FilterMana getFilter() {
|
||||
|
|
|
|||
|
|
@ -50,13 +50,13 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
|
|||
|
||||
// checks for compatibility
|
||||
EffectType needType = EffectType.CONTINUOUS;
|
||||
if (effect != null && !effect.getEffectType().equals(needType)) {
|
||||
if (effect.getEffectType() != needType) {
|
||||
Assert.fail("ConditionalContinuousEffect supports only " + needType.toString() + " but found " + effect.getEffectType().toString());
|
||||
}
|
||||
if (otherwiseEffect != null && !otherwiseEffect.getEffectType().equals(needType)) {
|
||||
if (otherwiseEffect != null && otherwiseEffect.getEffectType() != needType) {
|
||||
Assert.fail("ConditionalContinuousEffect supports only " + needType.toString() + " but found " + effect.getEffectType().toString());
|
||||
}
|
||||
if (effect != null && otherwiseEffect != null && !effect.getEffectType().equals(otherwiseEffect.getEffectType())) {
|
||||
if (otherwiseEffect != null && effect.getEffectType() != otherwiseEffect.getEffectType()) {
|
||||
Assert.fail("ConditionalContinuousEffect must be same but found " + effect.getEffectType().toString() + " and " + otherwiseEffect.getEffectType().toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -119,7 +119,7 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
|
|||
if (condition == null && baseCondition != null) {
|
||||
condition = baseCondition;
|
||||
}
|
||||
boolean conditionState = condition.apply(game, source);
|
||||
boolean conditionState = condition != null && condition.apply(game, source);
|
||||
if (conditionState) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
return effect.apply(game, source);
|
||||
|
|
@ -127,10 +127,10 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
|
|||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
return otherwiseEffect.apply(game, source);
|
||||
}
|
||||
if (!conditionState && effect.getDuration() == Duration.OneUse) {
|
||||
if (effect.getDuration() == Duration.OneUse) {
|
||||
used = true;
|
||||
}
|
||||
if (!conditionState && effect.getDuration() == Duration.Custom) {
|
||||
if (effect.getDuration() == Duration.Custom) {
|
||||
this.discard();
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum ArtifactsYouControlCount implements DynamicValue {
|
||||
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
return game.getBattlefield().count(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArtifactsYouControlCount copy() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "X";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "artifacts you control";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.game.Game;
|
||||
import mage.watchers.common.CardsDrawnThisTurnWatcher;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public enum CardsDrawnThisTurnDynamicValue implements DynamicValue {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
CardsDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsDrawnThisTurnWatcher.class);
|
||||
if (watcher != null) {
|
||||
return watcher.getCardsDrawnThisTurn(sourceAbility.getControllerId());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardsDrawnThisTurnDynamicValue copy() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "card you've drawn this turn";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.game.Game;
|
||||
import mage.watchers.common.CommanderPlaysCountWatcher;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class CommanderPlaysCount implements DynamicValue {
|
||||
|
||||
private Integer multiplier;
|
||||
|
||||
public CommanderPlaysCount() {
|
||||
this(1);
|
||||
}
|
||||
|
||||
public CommanderPlaysCount(Integer multiplier) {
|
||||
this.multiplier = multiplier;
|
||||
}
|
||||
|
||||
public CommanderPlaysCount(final CommanderPlaysCount dynamicValue) {
|
||||
this.multiplier = dynamicValue.multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
||||
int value = 0;
|
||||
if (watcher != null) {
|
||||
value = watcher.getPlaysCount(sourceAbility.getSourceId());
|
||||
}
|
||||
return value * multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommanderPlaysCount copy() {
|
||||
return new CommanderPlaysCount(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "X";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.OptionalAdditionalCost;
|
||||
import mage.abilities.costs.OptionalAdditionalCostImpl;
|
||||
import mage.abilities.costs.VariableCost;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.Spell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum GetKickerXValue implements DynamicValue {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability source, Effect effect) {
|
||||
// calcs only kicker with X values
|
||||
|
||||
// kicker adds additional costs to spell ability
|
||||
// only one X value per card possible
|
||||
// kicker can be calls multiple times (use getKickedCounter)
|
||||
|
||||
int finalValue = 0;
|
||||
Spell spell = game.getSpellOrLKIStack(source.getSourceId());
|
||||
if (spell != null && spell.getSpellAbility() != null) {
|
||||
int xValue = spell.getSpellAbility().getManaCostsToPay().getX();
|
||||
for (Ability ability : spell.getAbilities()) {
|
||||
if (ability instanceof KickerAbility) {
|
||||
|
||||
// search that kicker used X value
|
||||
KickerAbility kickerAbility = (KickerAbility) ability;
|
||||
boolean haveVarCost = kickerAbility.getKickerCosts()
|
||||
.stream()
|
||||
.anyMatch(varCost -> !((OptionalAdditionalCostImpl) varCost).getVariableCosts().isEmpty());
|
||||
|
||||
|
||||
if (haveVarCost) {
|
||||
int kickedCount = ((KickerAbility) ability).getKickedCounter(game, source);
|
||||
if (kickedCount > 0) {
|
||||
finalValue += kickedCount * xValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return finalValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GetKickerXValue copy() {
|
||||
return GetKickerXValue.instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "X";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
@ -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,15 +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.abilities.keyword.SpliceOntoArcaneAbility;
|
||||
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;
|
||||
|
|
@ -27,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
|
||||
*/
|
||||
|
|
@ -337,7 +332,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;
|
||||
|
|
@ -370,7 +365,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;
|
||||
|
|
@ -509,9 +504,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;
|
||||
}
|
||||
|
|
@ -663,12 +669,12 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
List<SpliceCardEffect> spliceEffects = getApplicableSpliceCardEffects(game, abilityToModify.getControllerId());
|
||||
// get the applyable splice abilities
|
||||
List<SpliceOntoArcaneAbility> spliceAbilities = new ArrayList<>();
|
||||
List<Ability> spliceAbilities = new ArrayList<>();
|
||||
for (SpliceCardEffect effect : spliceEffects) {
|
||||
Set<Ability> abilities = spliceCardEffects.getAbility(effect.getId());
|
||||
for (Ability ability : abilities) {
|
||||
if (effect.applies(abilityToModify, ability, game)) {
|
||||
spliceAbilities.add((SpliceOntoArcaneAbility) ability);
|
||||
spliceAbilities.add(ability);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -681,7 +687,7 @@ public class ContinuousEffects implements Serializable {
|
|||
do {
|
||||
FilterCard filter = new FilterCard("a card to splice");
|
||||
ArrayList<Predicate<MageObject>> idPredicates = new ArrayList<>();
|
||||
for (SpliceOntoArcaneAbility ability : spliceAbilities) {
|
||||
for (Ability ability : spliceAbilities) {
|
||||
idPredicates.add(new CardIdPredicate((ability.getSourceId())));
|
||||
}
|
||||
filter.add(Predicates.or(idPredicates));
|
||||
|
|
@ -689,8 +695,8 @@ public class ContinuousEffects implements Serializable {
|
|||
controller.chooseTarget(Outcome.Benefit, target, abilityToModify, game);
|
||||
UUID cardId = target.getFirstTarget();
|
||||
if (cardId != null) {
|
||||
SpliceOntoArcaneAbility selectedAbility = null;
|
||||
for (SpliceOntoArcaneAbility ability : spliceAbilities) {
|
||||
Ability selectedAbility = null;
|
||||
for (Ability ability : spliceAbilities) {
|
||||
if (ability.getSourceId().equals(cardId)) {
|
||||
selectedAbility = ability;
|
||||
break;
|
||||
|
|
@ -713,10 +719,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) {
|
||||
|
|
@ -730,10 +736,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()) {
|
||||
|
|
@ -763,7 +766,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());
|
||||
|
|
@ -954,7 +957,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());
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
|
@ -12,9 +10,9 @@ import mage.constants.Outcome;
|
|||
import mage.game.Game;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
import mage.util.ManaUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class CounterUnlessPaysEffect extends OneShotEffect {
|
||||
|
|
@ -54,23 +52,27 @@ public class CounterUnlessPaysEffect extends OneShotEffect {
|
|||
Player player = game.getPlayer(spell.getControllerId());
|
||||
if (player != null) {
|
||||
Cost costToPay;
|
||||
String costValueMessage;
|
||||
if (cost != null) {
|
||||
costToPay = cost.copy();
|
||||
costValueMessage = costToPay.getText();
|
||||
} else {
|
||||
costToPay = new GenericManaCost(genericMana.calculate(game, source, this));
|
||||
costToPay = ManaUtil.createManaCost(genericMana, game, source, this);
|
||||
costValueMessage = "{" + genericMana.calculate(game, source, this) + "}";
|
||||
}
|
||||
String message;
|
||||
if (costToPay instanceof ManaCost) {
|
||||
message = "Would you like to pay " + costToPay.getText() + " to prevent counter effect?";
|
||||
message = "Would you like to pay " + costValueMessage + " to prevent counter effect?";
|
||||
} else {
|
||||
message = costToPay.getText() + " to prevent counter effect?";
|
||||
message = costValueMessage + " to prevent counter effect?";
|
||||
}
|
||||
|
||||
costToPay.clearPaid();
|
||||
if (!(player.chooseUse(Outcome.Benefit, message, source, game) && costToPay.pay(source, game, spell.getSourceId(), spell.getControllerId(), false, null))) {
|
||||
game.informPlayers(player.getLogName() + " chooses not to pay " + costToPay.getText() + " to prevent the counter effect");
|
||||
game.informPlayers(player.getLogName() + " chooses not to pay " + costValueMessage + " to prevent the counter effect");
|
||||
return game.getStack().counter(spell.getId(), source.getSourceId(), game);
|
||||
}
|
||||
game.informPlayers(player.getLogName() + " chooses to pay " + costToPay.getText() + " to prevent the counter effect");
|
||||
game.informPlayers(player.getLogName() + " chooses to pay " + costValueMessage + " to prevent the counter effect");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.Mode;
|
||||
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
|
||||
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
|
|
@ -135,12 +135,14 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
} else {
|
||||
permanent = game.getPermanentOrLKIBattlefield(targetId);
|
||||
}
|
||||
|
||||
// can target card or permanent
|
||||
Card copyFrom;
|
||||
ApplyToPermanent applier = new EmptyApplyToPermanent();
|
||||
if (permanent != null) {
|
||||
// handle copies of copies
|
||||
Permanent copyFromPermanent = permanent;
|
||||
for (Effect effect : game.getState().getContinuousEffects().getLayeredEffects(game)) {
|
||||
for (ContinuousEffect effect : game.getState().getContinuousEffects().getLayeredEffects(game)) {
|
||||
if (effect instanceof CopyEffect) {
|
||||
CopyEffect copyEffect = (CopyEffect) effect;
|
||||
// there is another copy effect that our targetPermanent copies stats from
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
|
||||
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
|
||||
|
|
@ -17,8 +15,11 @@ import mage.game.permanent.token.Token;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class CreateTokenEffect extends OneShotEffect {
|
||||
|
|
@ -84,7 +85,7 @@ public class CreateTokenEffect extends OneShotEffect {
|
|||
return lastAddedTokenId;
|
||||
}
|
||||
|
||||
public ArrayList<UUID> getLastAddedTokenIds() {
|
||||
public List<UUID> getLastAddedTokenIds() {
|
||||
return lastAddedTokenIds;
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +134,11 @@ public class CreateTokenEffect extends OneShotEffect {
|
|||
}
|
||||
}
|
||||
if (attacking) {
|
||||
sb.append(" that are");
|
||||
if (amount.toString().equals("1")) {
|
||||
sb.append(" that's");
|
||||
} else {
|
||||
sb.append(" that are");
|
||||
}
|
||||
if (tapped) {
|
||||
sb.append(" tapped and");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
|
|
@ -12,14 +12,18 @@ import mage.game.permanent.Permanent;
|
|||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class DamageMultiEffect extends OneShotEffect {
|
||||
|
||||
protected DynamicValue amount;
|
||||
private String sourceName = "{source}";
|
||||
private final Set<MageObjectReference> damagedSet = new HashSet<>();
|
||||
|
||||
public DamageMultiEffect(int amount) {
|
||||
this(new StaticValue(amount));
|
||||
|
|
@ -37,6 +41,7 @@ public class DamageMultiEffect extends OneShotEffect {
|
|||
|
||||
public DamageMultiEffect(final DamageMultiEffect effect) {
|
||||
super(effect);
|
||||
this.damagedSet.addAll(effect.damagedSet);
|
||||
this.amount = effect.amount;
|
||||
this.sourceName = effect.sourceName;
|
||||
}
|
||||
|
|
@ -48,17 +53,21 @@ public class DamageMultiEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (!source.getTargets().isEmpty()) {
|
||||
Target multiTarget = source.getTargets().get(0);
|
||||
for (UUID target : multiTarget.getTargets()) {
|
||||
Permanent permanent = game.getPermanent(target);
|
||||
if (permanent != null) {
|
||||
permanent.damage(multiTarget.getTargetAmount(target), source.getSourceId(), game, false, true);
|
||||
} else {
|
||||
Player player = game.getPlayer(target);
|
||||
if (player != null) {
|
||||
player.damage(multiTarget.getTargetAmount(target), source.getSourceId(), game, false, true);
|
||||
}
|
||||
this.damagedSet.clear();
|
||||
if (source.getTargets().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
Target multiTarget = source.getTargets().get(0);
|
||||
for (UUID target : multiTarget.getTargets()) {
|
||||
Permanent permanent = game.getPermanent(target);
|
||||
if (permanent != null) {
|
||||
if (permanent.damage(multiTarget.getTargetAmount(target), source.getSourceId(), game, false, true) > 0) {
|
||||
damagedSet.add(new MageObjectReference(permanent, game));
|
||||
} ;
|
||||
} else {
|
||||
Player player = game.getPlayer(target);
|
||||
if (player != null) {
|
||||
player.damage(multiTarget.getTargetAmount(target), source.getSourceId(), game, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -83,4 +92,8 @@ public class DamageMultiEffect extends OneShotEffect {
|
|||
public void setSourceName(String sourceName) {
|
||||
this.sourceName = sourceName;
|
||||
}
|
||||
|
||||
public Set<MageObjectReference> getDamagedSet() {
|
||||
return damagedSet;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -25,7 +24,7 @@ import java.util.UUID;
|
|||
|
||||
/**
|
||||
* Effect for the DevourAbility
|
||||
*
|
||||
* <p>
|
||||
* 702.81. Devour 702.81a Devour is a static ability. "Devour N" means "As this
|
||||
* object enters the battlefield, you may sacrifice any number of creatures.
|
||||
* This permanent enters the battlefield with N +1/+1 counters on it for each
|
||||
|
|
@ -33,7 +32,6 @@ import java.util.UUID;
|
|||
* to the number of creatures the permanent devoured. "It devoured" means
|
||||
* "sacrificed as a result of its devour ability as it entered the battlefield."
|
||||
*
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class DevourEffect extends ReplacementEffectImpl {
|
||||
|
|
@ -43,6 +41,7 @@ public class DevourEffect extends ReplacementEffectImpl {
|
|||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
}
|
||||
|
||||
private final DevourFactor devourFactor;
|
||||
|
||||
public DevourEffect(DevourFactor devourFactor) {
|
||||
|
|
@ -125,10 +124,10 @@ public class DevourEffect extends ReplacementEffectImpl {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
public List<ArrayList<String>> getSubtypes(Game game, UUID permanentId) {
|
||||
public List<SubTypeList> getSubtypes(Game game, UUID permanentId) {
|
||||
Object object = game.getState().getValue(permanentId.toString() + "devoured");
|
||||
if (object != null) {
|
||||
return (List<ArrayList<String>>) object;
|
||||
return (List<SubTypeList>) object;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
|
@ -136,7 +135,7 @@ public class DevourEffect extends ReplacementEffectImpl {
|
|||
public int getDevouredCreaturesAmount(Game game, UUID permanentId) {
|
||||
Object object = game.getState().getValue(permanentId.toString() + "devoured");
|
||||
if (object != null) {
|
||||
return ((List<ArrayList<String>>) object).size();
|
||||
return ((List<SubTypeList>) object).size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
|
||||
public class DiscardCardControllerTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private final FilterCard filter;
|
||||
|
||||
public DiscardCardControllerTriggeredAbility(Effect effect, boolean isOptional) {
|
||||
this(effect, isOptional, StaticFilters.FILTER_CARD);
|
||||
}
|
||||
|
||||
public DiscardCardControllerTriggeredAbility(Effect effect, boolean isOptional, FilterCard filter) {
|
||||
super(Zone.BATTLEFIELD, effect, isOptional);
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
private DiscardCardControllerTriggeredAbility(final DiscardCardControllerTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.filter = ability.filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiscardCardControllerTriggeredAbility copy() {
|
||||
return new DiscardCardControllerTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DISCARDED_CARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return event.getPlayerId().equals(getControllerId())
|
||||
&& filter.match(game.getCard(event.getTargetId()), getId(), getControllerId(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever you discard " + filter.getMessage() + ", " + super.getRule();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,3 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
|
|
@ -13,31 +8,30 @@ import mage.game.Game;
|
|||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
|
||||
public class DiscardsACardPlayerTriggeredAbility extends TriggeredAbilityImpl {
|
||||
public class DiscardCardPlayerTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private SetTargetPointer setTargetPointer;
|
||||
|
||||
public DiscardsACardPlayerTriggeredAbility(Effect effect, boolean isOptional) {
|
||||
public DiscardCardPlayerTriggeredAbility(Effect effect, boolean isOptional) {
|
||||
this(effect, isOptional, SetTargetPointer.NONE);
|
||||
}
|
||||
|
||||
public DiscardsACardPlayerTriggeredAbility(Effect effect, boolean isOptional, SetTargetPointer setTargetPointer) {
|
||||
public DiscardCardPlayerTriggeredAbility(Effect effect, boolean isOptional, SetTargetPointer setTargetPointer) {
|
||||
super(Zone.BATTLEFIELD, effect, isOptional);
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
}
|
||||
|
||||
public DiscardsACardPlayerTriggeredAbility(final DiscardsACardPlayerTriggeredAbility ability) {
|
||||
private DiscardCardPlayerTriggeredAbility(final DiscardCardPlayerTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.setTargetPointer = ability.setTargetPointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiscardsACardPlayerTriggeredAbility copy() {
|
||||
return new DiscardsACardPlayerTriggeredAbility(this);
|
||||
public DiscardCardPlayerTriggeredAbility copy() {
|
||||
return new DiscardCardPlayerTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -52,6 +46,6 @@ import mage.game.events.GameEvent;
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever player discards a card, " + super.getRule();
|
||||
return "Whenever a player discards a card, " + super.getRule();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.Effect;
|
||||
|
|
@ -15,9 +13,11 @@ import mage.constants.Outcome;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.ManaUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect {
|
||||
|
|
@ -65,34 +65,39 @@ public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect {
|
|||
Player controller = game.getPlayer(source.getControllerId());
|
||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||
Cost costToPay;
|
||||
if (controller != null
|
||||
&& sourceObject != null) {
|
||||
String costValueMessage;
|
||||
if (controller != null && sourceObject != null) {
|
||||
if (cost != null) {
|
||||
costToPay = cost.copy();
|
||||
costValueMessage = costToPay.getText();
|
||||
} else {
|
||||
costToPay = new GenericManaCost(genericMana.calculate(game, source, this));
|
||||
costToPay = ManaUtil.createManaCost(genericMana, game, source, this);
|
||||
costValueMessage = "{" + genericMana.calculate(game, source, this) + "}";
|
||||
}
|
||||
String message;
|
||||
if (chooseUseText == null) {
|
||||
String effectText = executingEffects.getText(source.getModes().getMode());
|
||||
message = "Pay " + costToPay.getText() + " to prevent (" + effectText.substring(0, effectText.length() - 1) + ")?";
|
||||
message = "Pay " + costValueMessage + " to prevent (" + effectText.substring(0, effectText.length() - 1) + ")?";
|
||||
} else {
|
||||
message = chooseUseText;
|
||||
}
|
||||
message = CardUtil.replaceSourceName(message, sourceObject.getName());
|
||||
|
||||
boolean result = true;
|
||||
boolean doEffect = true;
|
||||
// check if any player is willing to pay
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null
|
||||
&& costToPay.canPay(source, source.getSourceId(), player.getId(), game) && player.chooseUse(Outcome.Detriment, message, source, game)) {
|
||||
if (player != null && player.canRespond()
|
||||
&& costToPay.canPay(source, source.getSourceId(), player.getId(), game)
|
||||
&& player.chooseUse(Outcome.Detriment, message, source, game)) {
|
||||
costToPay.clearPaid();
|
||||
if (costToPay.pay(source, game, source.getSourceId(), player.getId(), false, null)) {
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(player.getLogName() + " pays the cost to prevent the effect");
|
||||
}
|
||||
doEffect = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.Effect;
|
||||
|
|
@ -16,9 +14,9 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.ManaUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author MarcoMarin & L_J
|
||||
*/
|
||||
public class DoUnlessTargetPlayerOrTargetsControllerPaysEffect extends OneShotEffect {
|
||||
|
|
@ -70,62 +68,65 @@ public class DoUnlessTargetPlayerOrTargetsControllerPaysEffect extends OneShotEf
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player targetController = game.getPlayer(this.getTargetPointer().getFirst(game, source));
|
||||
Player player = game.getPlayer(this.getTargetPointer().getFirst(game, source));
|
||||
Permanent targetPermanent = game.getPermanentOrLKIBattlefield(this.getTargetPointer().getFirst(game, source));
|
||||
if (targetPermanent != null) {
|
||||
targetController = game.getPlayer(targetPermanent.getControllerId());
|
||||
player = game.getPlayer(targetPermanent.getControllerId());
|
||||
}
|
||||
if (targetController != null) {
|
||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||
if (sourceObject != null) {
|
||||
Cost costToPay;
|
||||
if (cost != null) {
|
||||
costToPay = cost.copy();
|
||||
} else {
|
||||
costToPay = new GenericManaCost(genericMana.calculate(game, source, this));
|
||||
}
|
||||
String message;
|
||||
if (chooseUseText == null) {
|
||||
String effectText = executingEffects.getText(source.getModes().getMode());
|
||||
message = "Pay " + costToPay.getText() + " to prevent (" + effectText.substring(0, effectText.length() - 1) + ")?";
|
||||
} else {
|
||||
message = chooseUseText;
|
||||
}
|
||||
message = CardUtil.replaceSourceName(message, sourceObject.getName());
|
||||
boolean result = true;
|
||||
boolean doEffect = true;
|
||||
|
||||
// check if targetController is willing to pay
|
||||
if (costToPay.canPay(source, source.getSourceId(), targetController.getId(), game) && targetController.chooseUse(Outcome.Detriment, message, source, game)) {
|
||||
costToPay.clearPaid();
|
||||
if (costToPay.pay(source, game, source.getSourceId(), targetController.getId(), false, null)) {
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(targetController.getLogName() + " pays the cost to prevent the effect");
|
||||
}
|
||||
doEffect = false;
|
||||
}
|
||||
}
|
||||
|
||||
// do the effects if not paid
|
||||
if (doEffect) {
|
||||
for (Effect effect : executingEffects) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
if (effect instanceof OneShotEffect) {
|
||||
result &= effect.apply(game, source);
|
||||
} else {
|
||||
game.addEffect((ContinuousEffect) effect, source);
|
||||
}
|
||||
}
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
if (otherwiseEffect instanceof OneShotEffect) {
|
||||
result &= otherwiseEffect.apply(game, source);
|
||||
} else {
|
||||
game.addEffect((ContinuousEffect) otherwiseEffect, source);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||
if (player != null && sourceObject != null) {
|
||||
Cost costToPay;
|
||||
String costValueMessage;
|
||||
if (cost != null) {
|
||||
costToPay = cost.copy();
|
||||
costValueMessage = costToPay.getText();
|
||||
} else {
|
||||
costToPay = ManaUtil.createManaCost(genericMana, game, source, this);
|
||||
costValueMessage = "{" + genericMana.calculate(game, source, this) + "}";
|
||||
}
|
||||
String message;
|
||||
if (chooseUseText == null) {
|
||||
String effectText = executingEffects.getText(source.getModes().getMode());
|
||||
message = "Pay " + costValueMessage + " to prevent (" + effectText.substring(0, effectText.length() - 1) + ")?";
|
||||
} else {
|
||||
message = chooseUseText;
|
||||
}
|
||||
message = CardUtil.replaceSourceName(message, sourceObject.getName());
|
||||
boolean result = true;
|
||||
boolean doEffect = true;
|
||||
|
||||
// check if targetController is willing to pay
|
||||
if (costToPay.canPay(source, source.getSourceId(), player.getId(), game)
|
||||
&& player.chooseUse(Outcome.Detriment, message, source, game)) {
|
||||
costToPay.clearPaid();
|
||||
if (costToPay.pay(source, game, source.getSourceId(), player.getId(), false, null)) {
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(player.getLogName() + " pays the cost to prevent the effect");
|
||||
}
|
||||
doEffect = false;
|
||||
}
|
||||
}
|
||||
|
||||
// do the effects if not paid
|
||||
if (doEffect) {
|
||||
for (Effect effect : executingEffects) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
if (effect instanceof OneShotEffect) {
|
||||
result &= effect.apply(game, source);
|
||||
} else {
|
||||
game.addEffect((ContinuousEffect) effect, source);
|
||||
}
|
||||
}
|
||||
} else if (otherwiseEffect != null) {
|
||||
otherwiseEffect.setTargetPointer(this.targetPointer);
|
||||
if (otherwiseEffect instanceof OneShotEffect) {
|
||||
result &= otherwiseEffect.apply(game, source);
|
||||
} else {
|
||||
game.addEffect((ContinuousEffect) otherwiseEffect, source);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
|
@ -9,8 +8,9 @@ import mage.constants.Outcome;
|
|||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class FightTargetsEffect extends OneShotEffect {
|
||||
|
|
@ -57,27 +57,23 @@ public class FightTargetsEffect extends OneShotEffect {
|
|||
return creature1.fight(creature2, source, game);
|
||||
}
|
||||
}
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(card.getName() + " has been fizzled.");
|
||||
}
|
||||
}
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(card.getName() + " has been fizzled.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FightTargetsEffect
|
||||
copy() {
|
||||
public FightTargetsEffect copy() {
|
||||
return new FightTargetsEffect(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String
|
||||
getText(Mode mode
|
||||
) {
|
||||
if (staticText
|
||||
!= null && !staticText
|
||||
.isEmpty()) {
|
||||
public String getText(Mode mode) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
|
||||
}
|
||||
|
|
@ -86,4 +82,4 @@ public class FightTargetsEffect extends OneShotEffect {
|
|||
.getTargets().get(1).getTargetName();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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,14 +1,17 @@
|
|||
|
||||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.constants.Outcome;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class InfoEffect extends OneShotEffect {
|
||||
|
|
@ -32,4 +35,11 @@ public class InfoEffect extends OneShotEffect {
|
|||
return new InfoEffect(this);
|
||||
}
|
||||
|
||||
public static void addInfoToPermanent(Game game, Ability source, Permanent permanent, String info) {
|
||||
// add simple static info to permanent's rules
|
||||
SimpleStaticAbility ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect(info));
|
||||
GainAbilityTargetEffect gainAbilityEffect = new GainAbilityTargetEffect(ability, Duration.WhileOnBattlefield);
|
||||
gainAbilityEffect.setTargetPointer(new FixedTarget(permanent.getId()));
|
||||
game.addEffect(gainAbilityEffect, source);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@
|
|||
*/
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import static java.lang.Integer.min;
|
||||
import java.util.Locale;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
|
|
@ -45,8 +43,11 @@ import mage.players.Player;
|
|||
import mage.target.TargetCard;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.lang.Integer.min;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX
|
||||
*/
|
||||
public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEffect {
|
||||
|
|
@ -63,36 +64,35 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
|
|||
|
||||
//TODO: These constructors are a mess
|
||||
public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards,
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, boolean putOnTop) {
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, boolean putOnTop) {
|
||||
this(numberOfCards, mayShuffleAfter, numberToPick, pickFilter,
|
||||
putOnTop, true);
|
||||
}
|
||||
|
||||
public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards,
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, boolean putOnTop, boolean reveal) {
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, boolean putOnTop, boolean reveal) {
|
||||
this(numberOfCards, mayShuffleAfter, numberToPick, pickFilter,
|
||||
Zone.LIBRARY, putOnTop, reveal);
|
||||
}
|
||||
|
||||
public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards,
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, Zone targetZoneLookedCards,
|
||||
boolean putOnTop, boolean reveal) {
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, Zone targetZoneLookedCards,
|
||||
boolean putOnTop, boolean reveal) {
|
||||
this(numberOfCards, mayShuffleAfter, numberToPick, pickFilter,
|
||||
targetZoneLookedCards, putOnTop, reveal, reveal);
|
||||
}
|
||||
|
||||
public LookLibraryAndPickControllerEffect(int numberOfCards,
|
||||
int numberToPick, FilterCard pickFilter, boolean upTo) {
|
||||
int numberToPick, FilterCard pickFilter, boolean upTo) {
|
||||
this(new StaticValue(numberOfCards), false,
|
||||
new StaticValue(numberToPick), pickFilter, Zone.LIBRARY, false,
|
||||
true, upTo);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param numberOfCards
|
||||
* @param numberToPick
|
||||
* @param pickFilter
|
||||
|
|
@ -102,8 +102,8 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
|
|||
* @param optional
|
||||
*/
|
||||
public LookLibraryAndPickControllerEffect(int numberOfCards,
|
||||
int numberToPick, FilterCard pickFilter, boolean reveal,
|
||||
boolean upTo, Zone targetZonePickedCards, boolean optional) {
|
||||
int numberToPick, FilterCard pickFilter, boolean reveal,
|
||||
boolean upTo, Zone targetZonePickedCards, boolean optional) {
|
||||
this(new StaticValue(numberOfCards), false,
|
||||
new StaticValue(numberToPick), pickFilter, Zone.LIBRARY, false,
|
||||
reveal, upTo, targetZonePickedCards, optional, true, true);
|
||||
|
|
@ -111,45 +111,43 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param numberOfCards
|
||||
* @param mayShuffleAfter
|
||||
* @param numberToPick
|
||||
* @param pickFilter
|
||||
* @param targetZoneLookedCards
|
||||
* @param putOnTop if zone for the rest is library decide if cards go to top
|
||||
* or bottom
|
||||
* @param putOnTop if zone for the rest is library decide if cards go to top
|
||||
* or bottom
|
||||
* @param reveal
|
||||
* @param upTo
|
||||
*/
|
||||
public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards,
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, Zone targetZoneLookedCards,
|
||||
boolean putOnTop, boolean reveal, boolean upTo) {
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, Zone targetZoneLookedCards,
|
||||
boolean putOnTop, boolean reveal, boolean upTo) {
|
||||
this(numberOfCards, mayShuffleAfter, numberToPick, pickFilter,
|
||||
targetZoneLookedCards, putOnTop, reveal, upTo, Zone.HAND,
|
||||
false, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param numberOfCards
|
||||
* @param mayShuffleAfter
|
||||
* @param numberToPick
|
||||
* @param pickFilter
|
||||
* @param targetZoneLookedCards
|
||||
* @param putOnTop if zone for the rest is library decide if cards go to top
|
||||
* or bottom
|
||||
* @param putOnTop if zone for the rest is library decide if cards go to top
|
||||
* or bottom
|
||||
* @param reveal
|
||||
* @param upTo
|
||||
* @param targetZonePickedCards
|
||||
* @param optional
|
||||
*/
|
||||
public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards,
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, Zone targetZoneLookedCards, boolean putOnTop,
|
||||
boolean reveal, boolean upTo, Zone targetZonePickedCards,
|
||||
boolean optional) {
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, Zone targetZoneLookedCards, boolean putOnTop,
|
||||
boolean reveal, boolean upTo, Zone targetZonePickedCards,
|
||||
boolean optional) {
|
||||
|
||||
this(numberOfCards, mayShuffleAfter, numberToPick, pickFilter,
|
||||
targetZoneLookedCards, putOnTop, reveal, upTo,
|
||||
|
|
@ -157,14 +155,13 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param numberOfCards
|
||||
* @param mayShuffleAfter
|
||||
* @param numberToPick
|
||||
* @param pickFilter
|
||||
* @param targetZoneLookedCards
|
||||
* @param putOnTop if zone for the rest is library decide if cards go to top
|
||||
* or bottom
|
||||
* @param putOnTop if zone for the rest is library decide if cards go to top
|
||||
* or bottom
|
||||
* @param reveal
|
||||
* @param upTo
|
||||
* @param targetZonePickedCards
|
||||
|
|
@ -173,10 +170,10 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
|
|||
* @param anyOrder
|
||||
*/
|
||||
public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards,
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, Zone targetZoneLookedCards, boolean putOnTop,
|
||||
boolean reveal, boolean upTo, Zone targetZonePickedCards,
|
||||
boolean optional, boolean putOnTopSelected, boolean anyOrder) {
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, Zone targetZoneLookedCards, boolean putOnTop,
|
||||
boolean reveal, boolean upTo, Zone targetZonePickedCards,
|
||||
boolean optional, boolean putOnTopSelected, boolean anyOrder) {
|
||||
super(Outcome.DrawCard, numberOfCards, mayShuffleAfter,
|
||||
targetZoneLookedCards, putOnTop);
|
||||
this.numberToPick = numberToPick;
|
||||
|
|
@ -334,7 +331,7 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
|
|||
sb.append("on the bottom");
|
||||
}
|
||||
sb.append(" of your library in ");
|
||||
if (anyOrder) {
|
||||
if (anyOrder && !backInRandomOrder) {
|
||||
sb.append("any");
|
||||
} else {
|
||||
sb.append("a random");
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.predicate.permanent.ControllerPredicate;
|
||||
import mage.filter.predicate.permanent.TokenPredicate;
|
||||
|
|
@ -17,7 +17,6 @@ import mage.target.TargetPermanent;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
//
|
||||
|
|
@ -31,6 +30,7 @@ import mage.target.targetpointer.FixedTarget;
|
|||
//
|
||||
public class PopulateEffect extends OneShotEffect {
|
||||
|
||||
private final boolean tappedAndAttacking;
|
||||
private static final FilterPermanent filter = new FilterPermanent("token for populate");
|
||||
|
||||
static {
|
||||
|
|
@ -43,35 +43,44 @@ public class PopulateEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
public PopulateEffect(String prefixText) {
|
||||
super(Outcome.Copy);
|
||||
this(false);
|
||||
this.staticText = (!prefixText.isEmpty() ? prefixText + " p" : "P") + "opulate <i>(Create a token that's a copy of a creature token you control.)</i>";
|
||||
}
|
||||
|
||||
public PopulateEffect(boolean tappedAndAttacking) {
|
||||
super(Outcome.Copy);
|
||||
this.tappedAndAttacking = tappedAndAttacking;
|
||||
this.staticText = "populate. The token enters the battlefield tapped and attacking. " +
|
||||
"<i>(To populate, create a token that's a copy of a creature token you control.)</i>";
|
||||
}
|
||||
|
||||
public PopulateEffect(final PopulateEffect effect) {
|
||||
super(effect);
|
||||
this.tappedAndAttacking = effect.tappedAndAttacking;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
Target target = new TargetPermanent(filter);
|
||||
target.setNotTarget(true);
|
||||
if (target.canChoose(source.getControllerId(), game)) {
|
||||
player.choose(Outcome.Copy, target, source.getSourceId(), game);
|
||||
Permanent tokenToCopy = game.getPermanent(target.getFirstTarget());
|
||||
if (tokenToCopy != null) {
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers("Token selected for populate: " + tokenToCopy.getLogName());
|
||||
}
|
||||
Effect effect = new CreateTokenCopyTargetEffect();
|
||||
effect.setTargetPointer(new FixedTarget(target.getFirstTarget()));
|
||||
return effect.apply(game, source);
|
||||
}
|
||||
}
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
Target target = new TargetPermanent(filter);
|
||||
target.setNotTarget(true);
|
||||
if (!target.canChoose(source.getControllerId(), game)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
player.choose(Outcome.Copy, target, source.getSourceId(), game);
|
||||
Permanent tokenToCopy = game.getPermanent(target.getFirstTarget());
|
||||
if (tokenToCopy == null) {
|
||||
return true;
|
||||
}
|
||||
game.informPlayers("Token selected for populate: " + tokenToCopy.getLogName());
|
||||
Effect effect = new CreateTokenCopyTargetEffect(
|
||||
null, null, false, 1, tappedAndAttacking, tappedAndAttacking
|
||||
);
|
||||
effect.setTargetPointer(new FixedTarget(target.getFirstTarget()));
|
||||
return effect.apply(game, source);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -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,10 +1,6 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
|
@ -12,13 +8,17 @@ import mage.cards.Card;
|
|||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
|
|
@ -62,9 +62,15 @@ public class PutOnLibraryTargetEffect extends OneShotEffect {
|
|||
}
|
||||
break;
|
||||
case GRAVEYARD:
|
||||
Card card = game.getCard(targetId);
|
||||
if (card != null && game.getState().getZone(targetId) == Zone.GRAVEYARD) {
|
||||
cards.add(card);
|
||||
Card graveyardCard = game.getCard(targetId);
|
||||
if (graveyardCard != null) {
|
||||
cards.add(graveyardCard);
|
||||
}
|
||||
break;
|
||||
case STACK:
|
||||
Card stackSpellCard = game.getSpell(targetId).getCard();
|
||||
if (stackSpellCard != null) {
|
||||
cards.add(stackSpellCard);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -76,7 +82,7 @@ public class PutOnLibraryTargetEffect extends OneShotEffect {
|
|||
if (card != null) {
|
||||
Player owner = game.getPlayer(card.getOwnerId());
|
||||
Cards cardsPlayer = new CardsImpl();
|
||||
for (Iterator<Card> iterator = cards.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<Card> iterator = cards.iterator(); iterator.hasNext(); ) {
|
||||
Card next = iterator.next();
|
||||
if (next.isOwnedBy(owner.getId())) {
|
||||
cardsPlayer.add(next);
|
||||
|
|
@ -95,7 +101,7 @@ public class PutOnLibraryTargetEffect extends OneShotEffect {
|
|||
if (permanent != null) {
|
||||
Player owner = game.getPlayer(permanent.getOwnerId());
|
||||
Cards cardsPlayer = new CardsImpl();
|
||||
for (Iterator<Permanent> iterator = permanents.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<Permanent> iterator = permanents.iterator(); iterator.hasNext(); ) {
|
||||
Permanent next = iterator.next();
|
||||
if (next.isOwnedBy(owner.getId())) {
|
||||
cardsPlayer.add(next);
|
||||
|
|
|
|||
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