forked from External/mage
Merge pull request 'master' (#48) from External/mage:master into master
All checks were successful
/ build_release (push) Successful in 18m43s
All checks were successful
/ build_release (push) Successful in 18m43s
Reviewed-on: #48
This commit is contained in:
commit
4f714203ae
443 changed files with 15065 additions and 2504 deletions
|
|
@ -61,6 +61,8 @@ public interface TriggeredAbility extends Ability {
|
|||
*/
|
||||
int getRemainingTriggersLimitEachGame(Game game);
|
||||
|
||||
TriggeredAbility setOptional(boolean optional);
|
||||
|
||||
TriggeredAbility setDoOnlyOnceEachTurn(boolean doOnlyOnce);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -209,6 +209,12 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TriggeredAbility setOptional(boolean optional) {
|
||||
this.optional = optional;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TriggeredAbility withRuleTextReplacement(boolean replaceRuleText) {
|
||||
this.replaceRuleText = replaceRuleText;
|
||||
|
|
|
|||
|
|
@ -29,8 +29,7 @@ public class DealsDamageAttachedTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT
|
||||
|| event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_BY_SOURCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import mage.util.CardUtil;
|
|||
public class ReflexiveTriggeredAbility extends DelayedTriggeredAbility {
|
||||
|
||||
private final String text;
|
||||
private final Condition condition;
|
||||
|
||||
public ReflexiveTriggeredAbility(Effect effect, boolean optional) {
|
||||
this(effect, optional, null);
|
||||
|
|
@ -27,13 +26,14 @@ public class ReflexiveTriggeredAbility extends DelayedTriggeredAbility {
|
|||
public ReflexiveTriggeredAbility(Effect effect, boolean optional, String text, Condition condition) {
|
||||
super(effect, Duration.EndOfTurn, true, optional);
|
||||
this.text = text;
|
||||
this.condition = condition;
|
||||
if (condition != null) {
|
||||
this.withInterveningIf(condition);
|
||||
}
|
||||
}
|
||||
|
||||
protected ReflexiveTriggeredAbility(final ReflexiveTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.text = ability.text;
|
||||
this.condition = ability.condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -55,11 +55,6 @@ public class ReflexiveTriggeredAbility extends DelayedTriggeredAbility {
|
|||
return CardUtil.getTextWithFirstCharUpperCase(text) + '.';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkInterveningIfClause(Game game) {
|
||||
return condition == null || condition.apply(game, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReflexiveTriggeredAbility setTriggerPhrase(String triggerPhrase) {
|
||||
super.setTriggerPhrase(triggerPhrase);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public enum LessonsInGraveCondition implements Condition {
|
||||
ONE(1),
|
||||
THREE(3);
|
||||
private final int amount;
|
||||
private static final FilterCard filter = new FilterCard(SubType.LESSON);
|
||||
private static final Hint hint = new ValueHint("Lesson cards in your graveyard", new CardsInControllerGraveyardCount(filter));
|
||||
|
||||
public static Hint getHint() {
|
||||
return hint;
|
||||
}
|
||||
|
||||
LessonsInGraveCondition(int amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
return player != null && player.getGraveyard().count(filter, game) >= this.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (amount == 1) {
|
||||
return "there's a Lesson card in your graveyard";
|
||||
}
|
||||
return "there are " + CardUtil.numberToText(this.amount) + " or more Lesson cards in your graveyard";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.hint.ConditionHint;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledLandPermanent;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public enum YouControlABasicLandCondition implements Condition {
|
||||
instance;
|
||||
private static final Hint hint = new ConditionHint(instance);
|
||||
|
||||
public static Hint getHint() {
|
||||
return hint;
|
||||
}
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledLandPermanent();
|
||||
|
||||
static {
|
||||
filter.add(SuperType.BASIC.getPredicate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return game.getBattlefield().contains(filter, source.getControllerId(), source, game, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "you control a basic land";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package mage.abilities.decorator;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.Effects;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.TargetPointer;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* Adds condition to {@link OneShotEffect}. Acts as decorator.
|
||||
*
|
||||
* @author Grath
|
||||
*/
|
||||
public class OptionalOneShotEffect extends OneShotEffect {
|
||||
|
||||
private final Effects effects = new Effects();
|
||||
|
||||
public OptionalOneShotEffect(OneShotEffect effect) {
|
||||
super(effect != null ? effect.getOutcome() : Outcome.Benefit); // must be first line, can't error for null effect here.
|
||||
if (effect == null) {
|
||||
throw new IllegalArgumentException("Wrong code usage: OptionalOneShotEffect should start with an effect to generate Outcome.");
|
||||
}
|
||||
this.effects.add(effect);
|
||||
}
|
||||
|
||||
protected OptionalOneShotEffect(final OptionalOneShotEffect effect) {
|
||||
super(effect);
|
||||
this.effects.addAll(effect.effects.copy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// nothing to do - no problem
|
||||
if (effects.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
String chooseText = staticText;
|
||||
if (chooseText == null || chooseText.isEmpty()) {
|
||||
chooseText = getText(source.getModes().getMode());
|
||||
chooseText = Character.toUpperCase(chooseText.charAt(0)) + chooseText.substring(1);
|
||||
}
|
||||
if (player != null && player.chooseUse(outcome, chooseText, source, game)) {
|
||||
effects.setTargetPointer(this.getTargetPointer().copy());
|
||||
effects.forEach(effect -> effect.apply(game, source));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public OptionalOneShotEffect addEffect(OneShotEffect effect) {
|
||||
this.effects.add(effect);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String key, Object value) {
|
||||
super.setValue(key, value);
|
||||
this.effects.setValue(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalOneShotEffect copy() {
|
||||
return new OptionalOneShotEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
return "you may " + CardUtil.getTextWithFirstCharLowerCase(effects.getText(mode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalOneShotEffect setTargetPointer(TargetPointer targetPointer) {
|
||||
effects.setTargetPointer(targetPointer);
|
||||
super.setTargetPointer(targetPointer);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalOneShotEffect withTargetDescription(String target) {
|
||||
effects.forEach(effect -> effect.withTargetDescription(target));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -36,14 +36,12 @@ public class IntPlusDynamicValue implements DynamicValue {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(baseValue).append(" plus ");
|
||||
return sb.append(value.toString()).toString();
|
||||
return baseValue + " plus " + value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return value.getMessage();
|
||||
return baseValue + " plus " + value.getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -48,6 +48,6 @@ public class CountersControllerCount implements DynamicValue {
|
|||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return (counterType != null ? counterType.toString() + ' ' : "") + "counter on {this}'s controller";
|
||||
return "the number of " + counterType.getName() + " counters you have";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public enum ShrinesYouControlCount implements DynamicValue {
|
||||
WHERE_X("X", "the number of Shrines you control"),
|
||||
FOR_EACH("1", "Shrine you control");
|
||||
|
||||
private static final Hint hint = new ValueHint("Shrines you control", WHERE_X);
|
||||
|
||||
public static Hint getHint() {
|
||||
return hint;
|
||||
}
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SHRINE);
|
||||
|
||||
private final String number;
|
||||
private final String message;
|
||||
|
||||
ShrinesYouControlCount(String number, String message) {
|
||||
this.number = number;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
return game.getBattlefield().count(filter, sourceAbility.getControllerId(), sourceAbility, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShrinesYouControlCount copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return number;
|
||||
}
|
||||
}
|
||||
|
|
@ -248,6 +248,10 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
|
|||
return startingControllerId;
|
||||
}
|
||||
|
||||
protected int getEffectStartingOnTurn() {
|
||||
return effectStartingOnTurn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStartingControllerAndTurnNum(Game game, UUID startingController, UUID activePlayerId) {
|
||||
this.startingControllerId = startingController;
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
private final boolean tapped;
|
||||
private Permanent savedPermanent = null;
|
||||
private int startingLoyalty = -1;
|
||||
private final int tokenPower;
|
||||
private final int tokenToughness;
|
||||
private int tokenPower;
|
||||
private int tokenToughness;
|
||||
private boolean useLKI = false;
|
||||
private PermanentModifier permanentModifier = null;
|
||||
|
||||
|
|
@ -387,6 +387,16 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
return this;
|
||||
}
|
||||
|
||||
public CreateTokenCopyTargetEffect setPower(int tokenPower) {
|
||||
this.tokenPower = tokenPower;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CreateTokenCopyTargetEffect setToughness(int tokenToughness) {
|
||||
this.tokenToughness = tokenToughness;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CreateTokenCopyTargetEffect addAbilityClassesToRemoveFromTokens(Class<? extends Ability> clazz) {
|
||||
this.abilityClazzesToRemove.add(clazz);
|
||||
return this;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public class DamageTargetAndAllControlledEffect extends OneShotEffect {
|
||||
|
||||
private final int firstAmount;
|
||||
private final int secondAmount;
|
||||
private final FilterPermanent filter;
|
||||
|
||||
/**
|
||||
* Deals simultaneous damage to the target and to each creature the target controls
|
||||
*/
|
||||
public DamageTargetAndAllControlledEffect(int amount) {
|
||||
this(amount, amount, StaticFilters.FILTER_PERMANENT_CREATURE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals simultaneous damage to the target and to each creature the target controls
|
||||
*/
|
||||
public DamageTargetAndAllControlledEffect(int firstAmount, int secondAmount, FilterPermanent filter) {
|
||||
super(Outcome.Damage);
|
||||
this.firstAmount = firstAmount;
|
||||
this.secondAmount = secondAmount;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
protected DamageTargetAndAllControlledEffect(final DamageTargetAndAllControlledEffect effect) {
|
||||
super(effect);
|
||||
this.firstAmount = effect.firstAmount;
|
||||
this.secondAmount = effect.secondAmount;
|
||||
this.filter = effect.filter.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DamageTargetAndAllControlledEffect copy() {
|
||||
return new DamageTargetAndAllControlledEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (permanent != null) {
|
||||
permanent.damage(firstAmount, source.getSourceId(), source, game);
|
||||
} else {
|
||||
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
if (player != null) {
|
||||
player.damage(firstAmount, source.getSourceId(), source, game);
|
||||
}
|
||||
}
|
||||
Player controller = game.getPlayerOrPlaneswalkerController(getTargetPointer().getFirst(game, source));
|
||||
if (controller != null) {
|
||||
for (Permanent perm : game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game)) {
|
||||
perm.damage(secondAmount, source.getSourceId(), source, game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
String description = getTargetPointer().describeTargets(mode.getTargets(), "that player");
|
||||
return "{this} deals " + firstAmount + "damage to " + description +
|
||||
" and " + secondAmount + " damage to each " + filter.getMessage() +
|
||||
" that player" +
|
||||
(description.contains("planeswalker") ? " or that planeswalker's controller" : "") +
|
||||
" controls";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public class DamageTargetAndSelfEffect extends OneShotEffect {
|
||||
|
||||
private final int firstAmount;
|
||||
private final int secondAmount;
|
||||
|
||||
/**
|
||||
* Deals simultaneous damage to the target and to itself
|
||||
*/
|
||||
public DamageTargetAndSelfEffect(int amount) {
|
||||
this(amount, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals simultaneous damage to the target and to itself
|
||||
*/
|
||||
public DamageTargetAndSelfEffect(int firstAmount, int secondAmount) {
|
||||
super(Outcome.Damage);
|
||||
this.firstAmount = firstAmount;
|
||||
this.secondAmount = secondAmount;
|
||||
}
|
||||
|
||||
protected DamageTargetAndSelfEffect(final DamageTargetAndSelfEffect effect) {
|
||||
super(effect);
|
||||
this.firstAmount = effect.firstAmount;
|
||||
this.secondAmount = effect.secondAmount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DamageTargetAndSelfEffect copy() {
|
||||
return new DamageTargetAndSelfEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (UUID targetId : this.getTargetPointer().getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(targetId);
|
||||
if (permanent != null) {
|
||||
permanent.damage(firstAmount, source.getSourceId(), source, game);
|
||||
} else {
|
||||
Player player = game.getPlayer(targetId);
|
||||
if (player != null) {
|
||||
player.damage(firstAmount, source.getSourceId(), source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
Permanent itself = source.getSourcePermanentIfItStillExists(game);
|
||||
if (itself != null) {
|
||||
itself.damage(secondAmount, source.getSourceId(), source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
return "{this} deals " + firstAmount + "damage to " +
|
||||
getTargetPointer().describeTargets(mode.getTargets(), "that creature") +
|
||||
" and " + secondAmount + "damage to itself";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public class DamageTargetAndTargetControllerEffect extends OneShotEffect {
|
||||
|
||||
private final int firstAmount;
|
||||
private final int secondAmount;
|
||||
|
||||
/**
|
||||
* Deals simultaneous damage to the target and to the controller of the target
|
||||
*/
|
||||
public DamageTargetAndTargetControllerEffect(int firstAmount, int secondAmount) {
|
||||
super(Outcome.Damage);
|
||||
this.firstAmount = firstAmount;
|
||||
this.secondAmount = secondAmount;
|
||||
}
|
||||
|
||||
protected DamageTargetAndTargetControllerEffect(final DamageTargetAndTargetControllerEffect effect) {
|
||||
super(effect);
|
||||
this.firstAmount = effect.firstAmount;
|
||||
this.secondAmount = effect.secondAmount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DamageTargetAndTargetControllerEffect copy() {
|
||||
return new DamageTargetAndTargetControllerEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (UUID targetId : this.getTargetPointer().getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(targetId);
|
||||
if (permanent != null) {
|
||||
permanent.damage(firstAmount, source.getSourceId(), source, game);
|
||||
}
|
||||
Permanent lki = game.getPermanentOrLKIBattlefield(targetId);
|
||||
if (lki != null) {
|
||||
Player player = game.getPlayer(lki.getControllerId());
|
||||
if (player != null) {
|
||||
player.damage(secondAmount, source.getSourceId(), source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
String description = getTargetPointer().describeTargets(mode.getTargets(), "that creature");
|
||||
return "{this} deals " + firstAmount + "damage to " + description +
|
||||
" and " + secondAmount + "damage to that " +
|
||||
(description.contains(" or ") ? "permanent's" : "creature's") + " controller";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public class DamageTargetAndTargetEffect extends OneShotEffect {
|
||||
|
||||
private final int firstAmount;
|
||||
private final int secondAmount;
|
||||
|
||||
/**
|
||||
* Deals simultaneous damage to two targets. Must set target tag 1 and 2
|
||||
*/
|
||||
public DamageTargetAndTargetEffect(int firstAmount, int secondAmount) {
|
||||
super(Outcome.Damage);
|
||||
this.firstAmount = firstAmount;
|
||||
this.secondAmount = secondAmount;
|
||||
}
|
||||
|
||||
protected DamageTargetAndTargetEffect(final DamageTargetAndTargetEffect effect) {
|
||||
super(effect);
|
||||
this.firstAmount = effect.firstAmount;
|
||||
this.secondAmount = effect.secondAmount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DamageTargetAndTargetEffect copy() {
|
||||
return new DamageTargetAndTargetEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
source.getTargets().getTargetsByTag(1).forEach(uuid -> damageTarget(uuid, firstAmount, source, game));
|
||||
source.getTargets().getTargetsByTag(2).forEach(uuid -> damageTarget(uuid, secondAmount, source, game));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void damageTarget(UUID targetId, int amount, Ability source, Game game) {
|
||||
Permanent permanent = game.getPermanent(targetId);
|
||||
if (permanent != null) {
|
||||
permanent.damage(amount, source.getSourceId(), source, game) ;
|
||||
} else {
|
||||
Player player = game.getPlayer(targetId);
|
||||
if (player != null) {
|
||||
player.damage(amount, source.getSourceId(), source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
// verify check that target tags are properly setup
|
||||
if (mode.getTargets().getByTag(1) == null || mode.getTargets().getByTag(2) == null) {
|
||||
throw new IllegalArgumentException("Wrong code usage: need to add tags to targets");
|
||||
}
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
return "{this} deals " + firstAmount + "damage to " + mode.getTargets().getByTag(1).getDescription() +
|
||||
" and " + secondAmount + "damage to " + mode.getTargets().getByTag(2).getDescription();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public class DamageTargetAndYouEffect extends OneShotEffect {
|
||||
|
||||
private final int firstAmount;
|
||||
private final int secondAmount;
|
||||
|
||||
/**
|
||||
* Deals simultaneous damage to the target and the controller of the source
|
||||
*/
|
||||
public DamageTargetAndYouEffect(int amount) {
|
||||
this(amount, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals simultaneous damage to the target and the controller of the source
|
||||
*/
|
||||
public DamageTargetAndYouEffect(int firstAmount, int secondAmount) {
|
||||
super(Outcome.Damage);
|
||||
this.firstAmount = firstAmount;
|
||||
this.secondAmount = secondAmount;
|
||||
}
|
||||
|
||||
protected DamageTargetAndYouEffect(final DamageTargetAndYouEffect effect) {
|
||||
super(effect);
|
||||
this.firstAmount = effect.firstAmount;
|
||||
this.secondAmount = effect.secondAmount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DamageTargetAndYouEffect copy() {
|
||||
return new DamageTargetAndYouEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (UUID targetId : this.getTargetPointer().getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(targetId);
|
||||
if (permanent != null) {
|
||||
permanent.damage(firstAmount, source.getSourceId(), source, game);
|
||||
} else {
|
||||
Player player = game.getPlayer(targetId);
|
||||
if (player != null) {
|
||||
player.damage(firstAmount, source.getSourceId(), source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
controller.damage(secondAmount, source.getSourceId(), source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
return "{this} deals " + firstAmount + "damage to " +
|
||||
getTargetPointer().describeTargets(mode.getTargets(), "that creature") +
|
||||
" and " + secondAmount + "damage to you";
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.Mode;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
|
|
@ -24,7 +23,6 @@ public class DamageTargetEffect extends OneShotEffect {
|
|||
protected DynamicValue amount;
|
||||
protected boolean preventable;
|
||||
protected String targetDescription;
|
||||
protected boolean useOnlyTargetPointer; // TODO: investigate why do we ignore targetPointer by default??
|
||||
protected String sourceName = "{this}";
|
||||
|
||||
public DamageTargetEffect(int amount) {
|
||||
|
|
@ -44,10 +42,6 @@ public class DamageTargetEffect extends OneShotEffect {
|
|||
this(StaticValue.get(amount), preventable, targetDescription);
|
||||
}
|
||||
|
||||
public DamageTargetEffect(int amount, boolean preventable, String targetDescription, boolean useOnlyTargetPointer) {
|
||||
this(StaticValue.get(amount), preventable, targetDescription, useOnlyTargetPointer);
|
||||
}
|
||||
|
||||
public DamageTargetEffect(int amount, boolean preventable, String targetDescription, String whoDealDamageName) {
|
||||
this(StaticValue.get(amount), preventable, targetDescription);
|
||||
this.sourceName = whoDealDamageName;
|
||||
|
|
@ -67,15 +61,10 @@ public class DamageTargetEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
public DamageTargetEffect(DynamicValue amount, boolean preventable, String targetDescription) {
|
||||
this(amount, preventable, targetDescription, false);
|
||||
}
|
||||
|
||||
public DamageTargetEffect(DynamicValue amount, boolean preventable, String targetDescription, boolean useOnlyTargetPointer) {
|
||||
super(Outcome.Damage);
|
||||
this.amount = amount;
|
||||
this.preventable = preventable;
|
||||
this.targetDescription = targetDescription;
|
||||
this.useOnlyTargetPointer = useOnlyTargetPointer;
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
|
|
@ -95,21 +84,15 @@ public class DamageTargetEffect extends OneShotEffect {
|
|||
this.amount = effect.amount.copy();
|
||||
this.preventable = effect.preventable;
|
||||
this.targetDescription = effect.targetDescription;
|
||||
this.useOnlyTargetPointer = effect.useOnlyTargetPointer;
|
||||
this.sourceName = effect.sourceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DamageTargetEffect withTargetDescription(String targetDescription) {
|
||||
this.targetDescription = targetDescription;
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO: this should most likely be refactored to not be needed and always use target pointer.
|
||||
public Effect setUseOnlyTargetPointer(boolean useOnlyTargetPointer) {
|
||||
this.useOnlyTargetPointer = useOnlyTargetPointer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DamageTargetEffect copy() {
|
||||
return new DamageTargetEffect(this);
|
||||
|
|
@ -117,21 +100,6 @@ public class DamageTargetEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (!useOnlyTargetPointer && source.getTargets().size() > 1) {
|
||||
for (Target target : source.getTargets()) {
|
||||
for (UUID targetId : target.getTargets()) {
|
||||
Permanent permanent = game.getPermanent(targetId);
|
||||
if (permanent != null) {
|
||||
permanent.damage(amount.calculate(game, source, this), source.getSourceId(), source, game, false, preventable);
|
||||
}
|
||||
Player player = game.getPlayer(targetId);
|
||||
if (player != null) {
|
||||
player.damage(amount.calculate(game, source, this), source.getSourceId(), source, game, false, preventable);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
for (UUID targetId : this.getTargetPointer().getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(targetId);
|
||||
if (permanent != null) {
|
||||
|
|
@ -154,7 +122,7 @@ public class DamageTargetEffect extends OneShotEffect {
|
|||
StringBuilder sb = new StringBuilder();
|
||||
String message = amount.getMessage();
|
||||
sb.append(this.sourceName).append(" deals ");
|
||||
if (message.isEmpty() || !message.equals("1")) {
|
||||
if (!message.equals("1")) {
|
||||
sb.append(amount);
|
||||
}
|
||||
if (!sb.toString().endsWith(" ")) {
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ public class GainLifeEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
// TODO: this text generation probably needs reworking
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import mage.abilities.effects.OneShotEffect;
|
|||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.Target;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
@ -42,37 +41,26 @@ public class TargetsDamageTargetsEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (source.getTargets().size() < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Target damageTarget = source.getTargets().getByTag(1);
|
||||
Target additionalDamageTarget = source.getTargets().getByTag(2);
|
||||
Target destTarget = source.getTargets().getByTag(3);
|
||||
|
||||
List<Permanent> damagingPermanents = new ArrayList<>();
|
||||
List<Permanent> receivingPermanents = new ArrayList<>();
|
||||
for (UUID id : damageTarget.getTargets()) {
|
||||
for (UUID id : source.getTargets().getTargetsByTag(1)) { // dealing damage
|
||||
Permanent permanent = game.getPermanent(id);
|
||||
if (permanent != null) {
|
||||
damagingPermanents.add(permanent);
|
||||
}
|
||||
}
|
||||
if (additionalDamageTarget != null) {
|
||||
for (UUID id : additionalDamageTarget.getTargets()) {
|
||||
Permanent permanent = game.getPermanent(id);
|
||||
if (permanent != null) {
|
||||
damagingPermanents.add(permanent);
|
||||
}
|
||||
for (UUID id : source.getTargets().getTargetsByTag(2)) { // additional dealing damage, if applicable
|
||||
Permanent permanent = game.getPermanent(id);
|
||||
if (permanent != null) {
|
||||
damagingPermanents.add(permanent);
|
||||
}
|
||||
}
|
||||
for (UUID id : destTarget.getTargets()) {
|
||||
for (UUID id : source.getTargets().getTargetsByTag(3)) { // receiving damage
|
||||
Permanent permanent = game.getPermanent(id);
|
||||
if (permanent != null) {
|
||||
receivingPermanents.add(permanent);
|
||||
}
|
||||
}
|
||||
|
||||
if (receivingPermanents.isEmpty() || damagingPermanents.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -86,6 +74,10 @@ public class TargetsDamageTargetsEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
// verify check that target tags are properly setup
|
||||
if (mode.getTargets().getByTag(1) == null || mode.getTargets().getByTag(3) == null) {
|
||||
throw new IllegalArgumentException("Wrong code usage: need to add tags to targets");
|
||||
}
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
|
|
@ -102,4 +94,4 @@ public class TargetsDamageTargetsEffect extends OneShotEffect {
|
|||
sb.append(mode.getTargets().getByTag(3).getDescription());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
package mage.abilities.effects.common.combat;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.RestrictionEffect;
|
||||
import mage.constants.Duration;
|
||||
import mage.filter.common.FilterBlockingCreature;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class CantBlockAloneSourceEffect extends RestrictionEffect {
|
||||
|
||||
private static final FilterBlockingCreature filter = new FilterBlockingCreature("Blocking creatures");
|
||||
public CantBlockAloneSourceEffect() {
|
||||
super(Duration.WhileOnBattlefield);
|
||||
staticText = "{this} can't block alone";
|
||||
}
|
||||
|
||||
protected CantBlockAloneSourceEffect(final CantBlockAloneSourceEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CantBlockAloneSourceEffect copy() {
|
||||
return new CantBlockAloneSourceEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBlockCheckAfter(Ability source, Game game, boolean canUseChooseDialogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Permanent permanent, Ability source, Game game) {
|
||||
if (permanent.getId().equals(source.getSourceId())) {
|
||||
return game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game).size() <= 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.mana.*;
|
||||
|
|
@ -8,6 +9,8 @@ import mage.filter.StaticFilters;
|
|||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
|
|
@ -21,9 +24,9 @@ public class BecomesAllBasicsControlledEffect extends ContinuousEffectImpl {
|
|||
new GreenManaAbility()
|
||||
};
|
||||
|
||||
public BecomesAllBasicsControlledEffect() {
|
||||
super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment);
|
||||
this.staticText = "Lands you control are every basic land type in addition to their other types";
|
||||
public BecomesAllBasicsControlledEffect(Duration duration) {
|
||||
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment);
|
||||
this.staticText = "lands you control are every basic land type in addition to their other types";
|
||||
dependendToTypes.add(DependencyType.BecomeNonbasicLand);
|
||||
dependencyTypes.add(DependencyType.BecomeMountain);
|
||||
dependencyTypes.add(DependencyType.BecomeForest);
|
||||
|
|
@ -42,29 +45,55 @@ public class BecomesAllBasicsControlledEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(
|
||||
StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, source.getControllerId(), game)) {
|
||||
permanent.addSubType(game,
|
||||
SubType.PLAINS,
|
||||
SubType.ISLAND,
|
||||
SubType.SWAMP,
|
||||
SubType.MOUNTAIN,
|
||||
SubType.FOREST);
|
||||
// Optimization: Remove basic mana abilities since they are redundant with AnyColorManaAbility
|
||||
// and keeping them will only produce too many combinations inside ManaOptions
|
||||
for (Ability basicManaAbility : basicManaAbilities) {
|
||||
if (permanent.getAbilities(game).containsRule(basicManaAbility)) {
|
||||
permanent.removeAbility(basicManaAbility, source.getSourceId(), game);
|
||||
}
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
if (getAffectedObjectsSet()) {
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, source.getControllerId(), game)) {
|
||||
affectedObjectList.add(new MageObjectReference(permanent, game));
|
||||
}
|
||||
// Add the {T}: Add one mana of any color ability
|
||||
// This is functionally equivalent to having five "{T}: Add {COLOR}" for each COLOR in {W}{U}{B}{R}{G}
|
||||
AnyColorManaAbility ability = new AnyColorManaAbility();
|
||||
if (!permanent.getAbilities(game).containsRule(ability)) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (!getAffectedObjectsSet()) {
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(
|
||||
StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, source.getControllerId(), game
|
||||
)) {
|
||||
removeTypes(permanent, game, source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext(); ) {
|
||||
Permanent permanent = it.next().getPermanent(game);
|
||||
if (permanent != null) {
|
||||
removeTypes(permanent, game, source);
|
||||
} else {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void removeTypes(Permanent permanent, Game game, Ability source) {
|
||||
permanent.addSubType(game,
|
||||
SubType.PLAINS,
|
||||
SubType.ISLAND,
|
||||
SubType.SWAMP,
|
||||
SubType.MOUNTAIN,
|
||||
SubType.FOREST);
|
||||
// Optimization: Remove basic mana abilities since they are redundant with AnyColorManaAbility
|
||||
// and keeping them will only produce too many combinations inside ManaOptions
|
||||
for (Ability basicManaAbility : basicManaAbilities) {
|
||||
if (permanent.getAbilities(game).containsRule(basicManaAbility)) {
|
||||
permanent.removeAbility(basicManaAbility, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
// Add the {T}: Add one mana of any color ability
|
||||
// This is functionally equivalent to having five "{T}: Add {COLOR}" for each COLOR in {W}{U}{B}{R}{G}
|
||||
AnyColorManaAbility ability = new AnyColorManaAbility();
|
||||
if (!permanent.getAbilities(game).containsRule(ability)) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.mana.RedManaAbility;
|
||||
import mage.constants.*;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class NonbasicLandsAreMountainsEffect extends ContinuousEffectImpl {
|
||||
|
||||
public NonbasicLandsAreMountainsEffect() {
|
||||
super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment);
|
||||
this.staticText = "nonbasic lands are Mountains";
|
||||
this.dependencyTypes.add(DependencyType.BecomeMountain);
|
||||
this.dependendToTypes.add(DependencyType.BecomeNonbasicLand);
|
||||
}
|
||||
|
||||
private NonbasicLandsAreMountainsEffect(final NonbasicLandsAreMountainsEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonbasicLandsAreMountainsEffect copy() {
|
||||
return new NonbasicLandsAreMountainsEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (Permanent land : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_LANDS_NONBASIC, source.getControllerId(), game)) {
|
||||
// 305.7 Note that this doesn't remove any abilities that were granted to the land by other effects
|
||||
// So the ability removing has to be done before Layer 6
|
||||
// Lands have their mana ability intrinsically, so that is added in layer 4
|
||||
land.removeAllSubTypes(game, SubTypeSet.NonBasicLandType);
|
||||
land.addSubType(game, SubType.MOUNTAIN);
|
||||
land.removeAllAbilities(source.getSourceId(), game);
|
||||
land.addAbility(new RedManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,11 +17,11 @@ import mage.constants.Outcome;
|
|||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
|
@ -47,18 +47,21 @@ public class AirbendTargetEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Set<Permanent> permanents = this
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
Set<Card> objects = this
|
||||
.getTargetPointer()
|
||||
.getTargets(game, source)
|
||||
.stream()
|
||||
.map(game::getPermanent)
|
||||
.map(uuid -> Optional.ofNullable((Card) game.getPermanent(uuid)).orElseGet(() -> game.getSpell(uuid)))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
if (player == null || permanents.isEmpty()) {
|
||||
if (objects.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
player.moveCards(permanents, Zone.EXILED, source, game);
|
||||
Cards cards = new CardsImpl(permanents);
|
||||
player.moveCards(objects, Zone.EXILED, source, game);
|
||||
Cards cards = new CardsImpl(objects);
|
||||
cards.retainZone(Zone.EXILED, game);
|
||||
for (Card card : cards.getCards(game)) {
|
||||
game.addEffect(new AirbendingCastEffect(card, game), source);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import mage.MageObjectReference;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.DelayedTriggeredAbility;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect;
|
||||
import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect;
|
||||
|
|
@ -25,9 +27,13 @@ import mage.util.CardUtil;
|
|||
*/
|
||||
public class EarthbendTargetEffect extends OneShotEffect {
|
||||
|
||||
private final int amount;
|
||||
private final DynamicValue amount;
|
||||
|
||||
public EarthbendTargetEffect(int amount) {
|
||||
this(StaticValue.get(amount));
|
||||
}
|
||||
|
||||
public EarthbendTargetEffect(DynamicValue amount) {
|
||||
super(Outcome.Benefit);
|
||||
this.amount = amount;
|
||||
}
|
||||
|
|
@ -48,18 +54,23 @@ public class EarthbendTargetEffect extends OneShotEffect {
|
|||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
int value = amount.calculate(game, source, this);
|
||||
doEarthBend(permanent, value, game, source);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void doEarthBend(Permanent permanent, int value, Game game, Ability source) {
|
||||
game.addEffect(new BecomesCreatureTargetEffect(
|
||||
new CreatureToken(0, 0)
|
||||
.withAbility(HasteAbility.getInstance()),
|
||||
false, true, Duration.Custom
|
||||
), source);
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(amount), source, game);
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(value), source, game);
|
||||
game.addDelayedTriggeredAbility(new EarthbendingDelayedTriggeredAbility(permanent, game), source);
|
||||
game.fireEvent(GameEvent.getEvent(
|
||||
GameEvent.EventType.EARTHBENDED, permanent.getId(),
|
||||
source, source.getControllerId(), amount
|
||||
source, source.getControllerId(), value
|
||||
));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -67,10 +78,21 @@ public class EarthbendTargetEffect extends OneShotEffect {
|
|||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
return "earthbend " + amount + ". <i>(Target land you control becomes a 0/0 creature " +
|
||||
"with haste that's still a land. Put " + CardUtil.numberToText(amount, "a") +
|
||||
" +1/+1 counter" + (amount > 1 ? "s" : "") + " on it. " +
|
||||
"When it dies or is exiled, return it to the battlefield tapped.)</i>";
|
||||
StringBuilder sb = new StringBuilder("earthbend ");
|
||||
sb.append(amount);
|
||||
if (!(amount instanceof StaticValue)) {
|
||||
sb.append(", where X is ");
|
||||
sb.append(amount.getMessage());
|
||||
}
|
||||
sb.append(". <i>(Target land you control becomes a 0/0 creature with haste that's still a land. Put ");
|
||||
String value = amount instanceof StaticValue
|
||||
? CardUtil.numberToText(((StaticValue) amount).getValue(), "a")
|
||||
: amount.toString();
|
||||
sb.append(value);
|
||||
sb.append(" +1/+1 counter");
|
||||
sb.append(("a".equals(value) ? "" : "s"));
|
||||
sb.append(" on it. When it dies or is exiled, return it to the battlefield tapped.)</i>");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.combat.CantAttackAloneSourceEffect;
|
||||
import mage.constants.Zone;
|
||||
|
||||
/**
|
||||
* @author magenoxx_at_googlemail.com
|
||||
*/
|
||||
public class CantAttackAloneAbility extends SimpleStaticAbility {
|
||||
|
||||
public CantAttackAloneAbility() {
|
||||
super(Zone.BATTLEFIELD, new CantAttackAloneSourceEffect());
|
||||
}
|
||||
|
||||
private CantAttackAloneAbility(CantAttackAloneAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CantAttackAloneAbility copy() {
|
||||
return new CantAttackAloneAbility(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.combat.CantAttackAloneSourceEffect;
|
||||
import mage.abilities.effects.common.combat.CantBlockAloneSourceEffect;
|
||||
import mage.constants.Zone;
|
||||
|
||||
/**
|
||||
* @author notgreat
|
||||
*/
|
||||
public class CantAttackOrBlockAloneAbility extends SimpleStaticAbility {
|
||||
|
||||
public CantAttackOrBlockAloneAbility() {
|
||||
super(Zone.BATTLEFIELD, new CantAttackAloneSourceEffect().setText("{this} can't attack or block alone"));
|
||||
this.addEffect(new CantBlockAloneSourceEffect().setText(""));
|
||||
}
|
||||
|
||||
private CantAttackOrBlockAloneAbility(CantAttackOrBlockAloneAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CantAttackOrBlockAloneAbility copy() {
|
||||
return new CantAttackOrBlockAloneAbility(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
|
||||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.StaticAbility;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
|
||||
/**
|
||||
* @author magenoxx_at_googlemail.com
|
||||
*/
|
||||
public class CantBlockAloneAbility extends StaticAbility implements MageSingleton {
|
||||
|
||||
private static final CantBlockAloneAbility instance = new CantBlockAloneAbility();
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static CantBlockAloneAbility getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private CantBlockAloneAbility() {
|
||||
super(Zone.BATTLEFIELD, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "{this} can't block alone.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CantBlockAloneAbility copy() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,23 +1,22 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.MageIdentifier;
|
||||
import mage.abilities.PlayLandAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.effects.AsThoughEffect;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MayhemLandAbility extends PlayLandAbility {
|
||||
public class MayhemLandAbility extends StaticAbility {
|
||||
|
||||
private final String rule;
|
||||
|
||||
public MayhemLandAbility(Card card) {
|
||||
super(card.getName());
|
||||
this.zone = Zone.GRAVEYARD;
|
||||
public MayhemLandAbility() {
|
||||
super(AbilityType.STATIC, Zone.GRAVEYARD);
|
||||
this.newId();
|
||||
this.name += " with Mayhem";
|
||||
this.addEffect(new MayhemPlayEffect());
|
||||
this.addWatcher(new MayhemWatcher());
|
||||
this.setRuleAtTheTop(true);
|
||||
this.rule = "Mayhem " +
|
||||
|
|
@ -30,24 +29,6 @@ public class MayhemLandAbility extends PlayLandAbility {
|
|||
this.rule = ability.rule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
if (!Zone.GRAVEYARD.match(game.getState().getZone(getSourceId()))
|
||||
|| !MayhemWatcher.checkCard(getSourceId(), game)) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
return super.canActivate(playerId, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activate(Game game, Set<MageIdentifier> allowedIdentifiers, boolean noMana) {
|
||||
if (!super.activate(game, allowedIdentifiers, noMana)) {
|
||||
return false;
|
||||
}
|
||||
this.setCostsTag(MayhemAbility.MAYHEM_ACTIVATION_VALUE_KEY, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MayhemLandAbility copy() {
|
||||
return new MayhemLandAbility(this);
|
||||
|
|
@ -57,4 +38,31 @@ public class MayhemLandAbility extends PlayLandAbility {
|
|||
public String getRule() {
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MayhemPlayEffect extends AsThoughEffectImpl {
|
||||
|
||||
public MayhemPlayEffect() {
|
||||
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileInGraveyard, Outcome.Neutral);
|
||||
}
|
||||
|
||||
public MayhemPlayEffect(final MayhemPlayEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
|
||||
return Zone.GRAVEYARD.match(game.getState().getZone(sourceId))
|
||||
&& MayhemWatcher.checkCard(sourceId, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsThoughEffect copy() {
|
||||
return new MayhemPlayEffect(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -272,6 +272,8 @@ public enum TokenRepository {
|
|||
res.add(createXmageToken(XMAGE_IMAGE_NAME_COPY, 12, "https://api.scryfall.com/cards/tpip/1/en?format=image"));
|
||||
res.add(createXmageToken(XMAGE_IMAGE_NAME_COPY, 13, "https://api.scryfall.com/cards/teoc/1/en?format=image"));
|
||||
res.add(createXmageToken(XMAGE_IMAGE_NAME_COPY, 14, "https://api.scryfall.com/cards/tspm/1/en?format=image"));
|
||||
res.add(createXmageToken(XMAGE_IMAGE_NAME_COPY, 15, "https://api.scryfall.com/cards/ttla/1/en?format=image"));
|
||||
res.add(createXmageToken(XMAGE_IMAGE_NAME_COPY, 16, "https://api.scryfall.com/cards/ttla/2/en?format=image"));
|
||||
|
||||
// City's Blessing
|
||||
// https://scryfall.com/search?q=type%3Atoken+include%3Aextras+unique%3Aprints+City%27s+Blessing+&unique=cards&as=grid&order=name
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ public enum AffinityType {
|
|||
BIRDS(new FilterControlledPermanent(SubType.BIRD, "Birds")),
|
||||
CITIZENS(new FilterControlledPermanent(SubType.CITIZEN, "Citizens")),
|
||||
SLIVERS(new FilterControlledPermanent(SubType.SLIVER, "Slivers")),
|
||||
ALLIES(new FilterControlledPermanent(SubType.ALLY, "Allies"), "Ally"),
|
||||
TOWNS(new FilterControlledPermanent(SubType.TOWN, "Towns")),
|
||||
GATES(new FilterControlledPermanent(SubType.GATE, "Gates"), GatesYouControlHint.instance),
|
||||
SNOW_LANDS(AffinityFilters.SNOW_LANDS),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,11 @@ import mage.cards.Card;
|
|||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author awjackson
|
||||
|
|
@ -17,6 +21,7 @@ public enum PutCards {
|
|||
GRAVEYARD(Outcome.Discard, Zone.GRAVEYARD, "into your graveyard"),
|
||||
BATTLEFIELD(Outcome.PutCardInPlay, Zone.BATTLEFIELD, "onto the battlefield"),
|
||||
BATTLEFIELD_TAPPED(Outcome.PutCardInPlay, Zone.BATTLEFIELD, "onto the battlefield tapped"),
|
||||
BATTLEFIELD_TAPPED_ATTACKING(Outcome.PutCardInPlay, Zone.BATTLEFIELD, "onto the battlefield tapped and attacking"),
|
||||
BATTLEFIELD_TRANSFORMED(Outcome.PutCardInPlay, Zone.BATTLEFIELD, "onto the battlefield transformed"),
|
||||
EXILED(Outcome.Exile, Zone.EXILED, "into exile"), // may need special case code to generate correct text
|
||||
TOP_OR_BOTTOM(Outcome.Benefit, Zone.LIBRARY, "on the top or bottom of your library"),
|
||||
|
|
@ -75,6 +80,15 @@ public enum PutCards {
|
|||
return player.putCardsOnBottomOfLibrary(new CardsImpl(card), game, source, false);
|
||||
case BATTLEFIELD_TAPPED:
|
||||
return player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null);
|
||||
case BATTLEFIELD_TAPPED_ATTACKING:
|
||||
if (player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null)) {
|
||||
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (permanent != null) {
|
||||
game.getCombat().addAttackingCreature(permanent.getId(), game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case SHUFFLE:
|
||||
return player.shuffleCardsToLibrary(card, game, source);
|
||||
case BATTLEFIELD_TRANSFORMED:
|
||||
|
|
@ -101,6 +115,18 @@ public enum PutCards {
|
|||
return player.putCardsOnBottomOfLibrary(cards, game, source, false);
|
||||
case BATTLEFIELD_TAPPED:
|
||||
return player.moveCards(cards.getCards(game), Zone.BATTLEFIELD, source, game, true, false, false, null);
|
||||
case BATTLEFIELD_TAPPED_ATTACKING:
|
||||
Set<Card> cardSet = cards.getCards(game);
|
||||
if (player.moveCards(cardSet, Zone.BATTLEFIELD, source, game, true, false, false, null)) {
|
||||
for (Card card : cardSet) {
|
||||
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (permanent != null) {
|
||||
game.getCombat().addAttackingCreature(permanent.getId(), game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case SHUFFLE:
|
||||
return player.shuffleCardsToLibrary(cards, game, source);
|
||||
case BATTLEFIELD_TRANSFORMED:
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@ public enum SubType {
|
|||
PINCHER("Pincher", SubTypeSet.CreatureType),
|
||||
PIRATE("Pirate", SubTypeSet.CreatureType),
|
||||
PLANT("Plant", SubTypeSet.CreatureType),
|
||||
PLATYPUS("Platypus", SubTypeSet.CreatureType),
|
||||
PORCUPINE("Porcupine", SubTypeSet.CreatureType),
|
||||
POSSUM("Possum", SubTypeSet.CreatureType),
|
||||
PRAETOR("Praetor", SubTypeSet.CreatureType),
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ public enum CounterType {
|
|||
COLLECTION("collection"),
|
||||
COMPONENT("component"),
|
||||
CONTESTED("contested"),
|
||||
CONQUEROR("conqueror"),
|
||||
CORPSE("corpse"),
|
||||
CORRUPTION("corruption"),
|
||||
CREDIT("credit"),
|
||||
|
|
|
|||
|
|
@ -601,6 +601,13 @@ public final class StaticFilters {
|
|||
FILTER_OPPONENTS_PERMANENT_ARTIFACT_OR_CREATURE.setLockedFilter(true);
|
||||
}
|
||||
|
||||
public static final FilterPermanent FILTER_ANOTHER_PERMANENT = new FilterPermanent("another permanent");
|
||||
|
||||
static {
|
||||
FILTER_ANOTHER_PERMANENT.add(AnotherPredicate.instance);
|
||||
FILTER_ANOTHER_PERMANENT.setLockedFilter(true);
|
||||
}
|
||||
|
||||
public static final FilterCreaturePermanent FILTER_ANOTHER_CREATURE = new FilterCreaturePermanent("another creature");
|
||||
|
||||
static {
|
||||
|
|
@ -902,6 +909,13 @@ public final class StaticFilters {
|
|||
FILTER_CONTROLLED_SAMURAI_OR_WARRIOR.setLockedFilter(true);
|
||||
}
|
||||
|
||||
public static final FilterControlledPermanent FILTER_ANOTHER_CONTROLLED_SHRINE = new FilterControlledPermanent(SubType.SHRINE, "another Shrine you control");
|
||||
|
||||
static {
|
||||
FILTER_ANOTHER_CONTROLLED_SHRINE.add(AnotherPredicate.instance);
|
||||
FILTER_ANOTHER_CONTROLLED_SHRINE.setLockedFilter(true);
|
||||
}
|
||||
|
||||
public static final FilterPlaneswalkerPermanent FILTER_PERMANENT_PLANESWALKER = new FilterPlaneswalkerPermanent();
|
||||
|
||||
static {
|
||||
|
|
|
|||
|
|
@ -679,29 +679,6 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
possibleBlockers.put(attacker.getId(), goodBlockers);
|
||||
}
|
||||
|
||||
// effects: can't block alone
|
||||
// too much blockers
|
||||
if (blockersCount == 1) {
|
||||
List<UUID> toBeRemoved = new ArrayList<>();
|
||||
for (UUID blockerId : getBlockers()) {
|
||||
Permanent blocker = game.getPermanent(blockerId);
|
||||
if (blocker != null && blocker.getAbilities().containsKey(CantBlockAloneAbility.getInstance().getId())) {
|
||||
blockWasLegal = false;
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(blocker.getLogName() + " can't block alone. Removing it from combat.");
|
||||
}
|
||||
toBeRemoved.add(blockerId);
|
||||
}
|
||||
}
|
||||
|
||||
for (UUID blockerId : toBeRemoved) {
|
||||
game.getCombat().removeBlocker(blockerId, game);
|
||||
}
|
||||
if (blockers.isEmpty()) {
|
||||
this.blocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (UUID uuid : attackers) {
|
||||
Permanent attacker = game.getPermanent(uuid);
|
||||
if (attacker != null && this.blocked) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
package mage.game.permanent.token;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.keyword.CantAttackAloneAbility;
|
||||
import mage.abilities.keyword.CantBlockAloneAbility;
|
||||
import mage.abilities.keyword.CantAttackOrBlockAloneAbility;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
|
|
@ -19,8 +18,7 @@ public final class BeastieToken extends TokenImpl {
|
|||
power = new MageInt(4);
|
||||
toughness = new MageInt(4);
|
||||
|
||||
this.addAbility(new CantAttackAloneAbility());
|
||||
this.addAbility(CantBlockAloneAbility.getInstance());
|
||||
this.addAbility(new CantAttackOrBlockAloneAbility());
|
||||
}
|
||||
|
||||
private BeastieToken(final BeastieToken token) {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
package mage.game.permanent.token;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.DealsCombatDamageTriggeredAbility;
|
||||
import mage.abilities.effects.common.SacrificeSourceEffect;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
public final class VrondissRageOfAncientsToken extends TokenImpl {
|
||||
|
||||
|
|
@ -21,7 +18,7 @@ public final class VrondissRageOfAncientsToken extends TokenImpl {
|
|||
power = new MageInt(5);
|
||||
toughness = new MageInt(4);
|
||||
|
||||
this.addAbility(new VrondissRageOfAncientsTokenTriggeredAbility());
|
||||
this.addAbility(new DealsCombatDamageTriggeredAbility(new SacrificeSourceEffect(), false));
|
||||
}
|
||||
|
||||
private VrondissRageOfAncientsToken(final VrondissRageOfAncientsToken token) {
|
||||
|
|
@ -32,35 +29,3 @@ public final class VrondissRageOfAncientsToken extends TokenImpl {
|
|||
return new VrondissRageOfAncientsToken(this);
|
||||
}
|
||||
}
|
||||
|
||||
class VrondissRageOfAncientsTokenTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public VrondissRageOfAncientsTokenTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new SacrificeSourceEffect(), false);
|
||||
}
|
||||
|
||||
protected VrondissRageOfAncientsTokenTriggeredAbility(final VrondissRageOfAncientsTokenTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VrondissRageOfAncientsTokenTriggeredAbility copy() {
|
||||
return new VrondissRageOfAncientsTokenTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER
|
||||
|| event.getType() == GameEvent.EventType.DAMAGED_PERMANENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return event.getSourceId().equals(this.getSourceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "When this creature deals damage, sacrifice it.";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ public class ManaPool implements Serializable {
|
|||
// empty mana pool effects
|
||||
private final Set<ManaType> doNotEmptyManaTypes = new HashSet<>(); // keep some colors
|
||||
private boolean manaBecomesBlack = false; // replace all pool by black
|
||||
private boolean manaBecomesRed = false; // replace all pool by red
|
||||
private boolean manaBecomesColorless = false; // replace all pool by colorless
|
||||
|
||||
private static final class ConditionalManaInfo {
|
||||
|
|
@ -76,6 +77,7 @@ public class ManaPool implements Serializable {
|
|||
}
|
||||
this.doNotEmptyManaTypes.addAll(pool.doNotEmptyManaTypes);
|
||||
this.manaBecomesBlack = pool.manaBecomesBlack;
|
||||
this.manaBecomesRed = pool.manaBecomesRed;
|
||||
this.manaBecomesColorless = pool.manaBecomesColorless;
|
||||
}
|
||||
|
||||
|
|
@ -236,6 +238,7 @@ public class ManaPool implements Serializable {
|
|||
public void clearEmptyManaPoolRules() {
|
||||
doNotEmptyManaTypes.clear();
|
||||
this.manaBecomesBlack = false;
|
||||
this.manaBecomesRed = false;
|
||||
this.manaBecomesColorless = false;
|
||||
}
|
||||
|
||||
|
|
@ -247,6 +250,10 @@ public class ManaPool implements Serializable {
|
|||
this.manaBecomesBlack = manaBecomesBlack;
|
||||
}
|
||||
|
||||
public void setManaBecomesRed(boolean manaBecomesRed) {
|
||||
this.manaBecomesRed = manaBecomesRed;
|
||||
}
|
||||
|
||||
public void setManaBecomesColorless(boolean manaBecomesColorless) {
|
||||
this.manaBecomesColorless = manaBecomesColorless;
|
||||
}
|
||||
|
|
@ -267,6 +274,9 @@ public class ManaPool implements Serializable {
|
|||
if (manaBecomesBlack) {
|
||||
continue;
|
||||
}
|
||||
if (manaBecomesRed) {
|
||||
continue;
|
||||
}
|
||||
if (manaBecomesColorless) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -315,12 +325,20 @@ public class ManaPool implements Serializable {
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This should be reimplemented as replacement effects instead, so you can choose which applies.
|
||||
if (manaBecomesBlack) {
|
||||
int amount = toEmpty.get(manaType);
|
||||
toEmpty.clear(manaType);
|
||||
toEmpty.add(ManaType.BLACK, amount);
|
||||
return 0;
|
||||
}
|
||||
if (manaBecomesRed) {
|
||||
int amount = toEmpty.get(manaType);
|
||||
toEmpty.clear(manaType);
|
||||
toEmpty.add(ManaType.RED, amount);
|
||||
return 0;
|
||||
}
|
||||
if (manaBecomesColorless) {
|
||||
int amount = toEmpty.get(manaType);
|
||||
toEmpty.clear(manaType);
|
||||
|
|
|
|||
|
|
@ -1536,7 +1536,7 @@ public final class CardUtil {
|
|||
} else {
|
||||
chosenAbility = player.chooseAbilityForCast(cardToCast, game, true);
|
||||
}
|
||||
boolean result = false;
|
||||
boolean result;
|
||||
if (chosenAbility instanceof SpellAbility) {
|
||||
result = player.cast(
|
||||
(SpellAbility) chosenAbility,
|
||||
|
|
@ -1545,6 +1545,8 @@ public final class CardUtil {
|
|||
} else if (playLand && chosenAbility instanceof PlayLandAbility) {
|
||||
Card land = game.getCard(chosenAbility.getSourceId());
|
||||
result = player.playLand(land, game, true);
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
partsToCast.forEach(card -> game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null));
|
||||
if (result && spellCastTracker != null) {
|
||||
|
|
|
|||
|
|
@ -1515,10 +1515,13 @@
|
|||
|
||||
# SLD
|
||||
|Generate|TOK:SLD|Angel|||AngelToken|
|
||||
|Generate|TOK:SLD|Blood|||BloodToken|
|
||||
|Generate|TOK:SLD|Cat|1||GreenCatToken|
|
||||
|Generate|TOK:SLD|Cat|2||CatToken2|
|
||||
|Generate|TOK:SLD|Cat|3||CatToken2|
|
||||
|Generate|TOK:SLD|Clue|||ClueArtifactToken|
|
||||
|Generate|TOK:SLD|Cordyceps Infected|1||CordycepsInfectedToken|
|
||||
|Generate|TOK:SLD|Cordyceps Infected|2||CordycepsInfectedToken|
|
||||
|Generate|TOK:SLD|Dog|||WhiteDogToken|
|
||||
|Generate|TOK:SLD|Egg|||AtlaPalaniToken|
|
||||
|Generate|TOK:SLD|Faerie Rogue|1||FaerieRogueToken|
|
||||
|
|
@ -2881,6 +2884,34 @@
|
|||
|Generate|TOK:SPM|Spider|||Spider21Token|
|
||||
|Generate|TOK:SPM|Treasure|||TreasureToken|
|
||||
|
||||
#TLA
|
||||
|Generate|TOK:TLA|Ally|1||AllyToken|
|
||||
|Generate|TOK:TLA|Ally|2||AllyToken|
|
||||
|Generate|TOK:TLA|Ally|3||AllyToken|
|
||||
|Generate|TOK:TLA|Ally|4||AllyToken|
|
||||
|Generate|TOK:TLA|Ally|5||AllyToken|
|
||||
|Generate|TOK:TLA|Ballistic Boulder|||BallisticBoulder|
|
||||
|Generate|TOK:TLA|Bear|||BearsCompanionBearToken|
|
||||
|Generate|TOK:TLA|Clue|1||ClueArtifactToken|
|
||||
|Generate|TOK:TLA|Clue|2||ClueArtifactToken|
|
||||
|Generate|TOK:TLA|Clue|3||ClueArtifactToken|
|
||||
|Generate|TOK:TLA|Clue|4||ClueArtifactToken|
|
||||
|Generate|TOK:TLA|Clue|5||ClueArtifactToken|
|
||||
|Generate|TOK:TLA|Food|1||FoodToken|
|
||||
|Generate|TOK:TLA|Food|2||FoodToken|
|
||||
|Generate|TOK:TLA|Food|3||FoodToken|
|
||||
|Generate|TOK:TLA|Monk|||MonkRedToken|
|
||||
|Generate|TOK:TLA|Soldier|||SoldierFirebendingToken|
|
||||
|Generate|TOK:TLA|Spirit|||SpiritWorldToken|
|
||||
|Generate|TOK:TLA|Treasure|||TreasureToken|
|
||||
|
||||
#TLE
|
||||
|Generate|TOK:TLE|Marit Lage|||MaritLageToken|
|
||||
|Generate|TOK:TLE|Soldier|||SoldierRedToken|
|
||||
|
||||
#TMT
|
||||
|Generate|TOK:TMT|Mutagen|||MutagenToken|
|
||||
|
||||
# JVC
|
||||
|Generate|TOK:JVC|Elemental Shaman|||ElementalShamanToken|
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue