Merge branch 'copy_constructor_watchers' of https://github.com/magefree/mage into copy_constructor_watchers

This commit is contained in:
Ingmar Goudt 2019-12-16 23:03:56 +01:00
commit e8303d551d
173 changed files with 5472 additions and 2993 deletions

View file

@ -53,6 +53,10 @@ public class MageObjectReference implements Comparable<MageObjectReference>, Ser
public MageObjectReference(UUID sourceId, Game game) {
this.sourceId = sourceId;
if (sourceId == null) {
throw new IllegalArgumentException("MageObjectReference contains nullable sourceId");
}
MageObject mageObject = game.getObject(sourceId);
if (mageObject != null) {
this.zoneChangeCounter = mageObject.getZoneChangeCounter(game);

View file

@ -1,7 +1,7 @@
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;
@ -9,15 +9,21 @@ import mage.constants.Zone;
import mage.game.Game;
/**
*
* @author Plopman
*/
public class CastCommanderAbility extends SpellAbility {
public CastCommanderAbility(Card card) {
super(card.getManaCost(), card.getName(), Zone.COMMAND, SpellAbilityType.BASE);
this.costs = card.getSpellAbility().getCosts().copy();
this.timing = TimingRule.SORCERY;
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();

View file

@ -1,4 +1,3 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
@ -45,6 +44,7 @@ public class DealsCombatDamageToAPlayerTriggeredAbility extends TriggeredAbility
this.text = ability.text;
this.setTargetPointer = ability.setTargetPointer;
this.onlyOpponents = ability.onlyOpponents;
this.orPlaneswalker = ability.orPlaneswalker;
}
public DealsCombatDamageToAPlayerTriggeredAbility setOrPlaneswalker(boolean orPlaneswalker) {

View file

@ -1,9 +1,5 @@
package mage.abilities.costs.common;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.CostImpl;
@ -18,8 +14,11 @@ import mage.players.Player;
import mage.target.TargetPermanent;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
*
* @author LevelX
*/
public class RemoveCounterCost extends CostImpl {
@ -102,10 +101,6 @@ public class RemoveCounterCost extends CostImpl {
new StringBuilder("Remove how many counters from ").append(permanent.getIdName()).toString(), game);
}
permanent.removeCounters(counterName, numberOfCountersSelected, game);
if (permanent.getCounters(game).getCount(counterName) == 0) {
// this removes only the item with number = 0 from the collection
permanent.getCounters(game).removeCounter(counterName);
}
countersRemoved += numberOfCountersSelected;
if (!game.isSimulation()) {
game.informPlayers(new StringBuilder(controller.getLogName())

View file

@ -1,9 +1,5 @@
package mage.abilities.decorator;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.condition.Condition;
@ -11,11 +7,14 @@ import mage.abilities.condition.FixedCondition;
import mage.abilities.condition.LockedInCondition;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.DependencyType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.SubLayer;
import mage.constants.*;
import mage.game.Game;
import org.junit.Assert;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
/**
* Adds condition to {@link ContinuousEffect}. Acts as decorator.
@ -48,6 +47,18 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
this.otherwiseEffect = otherwiseEffect;
this.baseCondition = condition;
this.staticText = text;
// checks for compatibility
EffectType needType = EffectType.CONTINUOUS;
if (effect != null && !effect.getEffectType().equals(needType)) {
Assert.fail("ConditionalContinuousEffect supports only " + needType.toString() + " but found " + effect.getEffectType().toString());
}
if (otherwiseEffect != null && !otherwiseEffect.getEffectType().equals(needType)) {
Assert.fail("ConditionalContinuousEffect supports only " + needType.toString() + " but found " + effect.getEffectType().toString());
}
if (effect != null && otherwiseEffect != null && !effect.getEffectType().equals(otherwiseEffect.getEffectType())) {
Assert.fail("ConditionalContinuousEffect must be same but found " + effect.getEffectType().toString() + " and " + otherwiseEffect.getEffectType().toString());
}
}
public ConditionalContinuousEffect(final ConditionalContinuousEffect effect) {
@ -68,6 +79,7 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
@Override
public void init(Ability source, Game game) {
super.init(source, game);
if (baseCondition instanceof LockedInCondition) {
condition = new FixedCondition(((LockedInCondition) baseCondition).getBaseCondition().apply(game, source));
} else {

View file

@ -0,0 +1,86 @@
package mage.abilities.decorator;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.abilities.effects.CostModificationEffect;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.constants.Duration;
import mage.game.Game;
/**
* @author JayDi85
*/
public class ConditionalCostModificationEffect extends CostModificationEffectImpl {
protected CostModificationEffect effect;
protected CostModificationEffect otherwiseEffect;
protected Condition condition;
protected boolean conditionState;
public ConditionalCostModificationEffect(CostModificationEffect effect, Condition condition, String text) {
this(effect, condition, null, text);
}
public ConditionalCostModificationEffect(CostModificationEffect effect, Condition condition, CostModificationEffect otherwiseEffect,
String text) {
super(effect.getDuration(), effect.getOutcome(), effect.getModificationType());
this.effect = effect;
this.condition = condition;
this.otherwiseEffect = otherwiseEffect;
if (text != null) {
this.setText(text);
}
}
public ConditionalCostModificationEffect(final ConditionalCostModificationEffect effect) {
super(effect);
this.effect = (CostModificationEffect) effect.effect.copy();
if (effect.otherwiseEffect != null) {
this.otherwiseEffect = (CostModificationEffect) effect.otherwiseEffect.copy();
}
this.condition = effect.condition;
this.conditionState = effect.conditionState;
}
@Override
public boolean isDiscarded() {
return effect.isDiscarded() || (otherwiseEffect != null && otherwiseEffect.isDiscarded());
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
conditionState = condition.apply(game, source);
if (conditionState) {
effect.setTargetPointer(this.targetPointer);
return effect.apply(game, source, abilityToModify);
} else if (otherwiseEffect != null) {
otherwiseEffect.setTargetPointer(this.targetPointer);
return otherwiseEffect.apply(game, source, abilityToModify);
}
if (!conditionState && effect.getDuration() == Duration.OneUse) {
used = true;
}
if (!conditionState && effect.getDuration() == Duration.Custom) {
this.discard();
}
return false;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
conditionState = condition.apply(game, source);
if (conditionState) {
effect.setTargetPointer(this.targetPointer);
return effect.applies(abilityToModify, source, game);
} else if (otherwiseEffect != null) {
otherwiseEffect.setTargetPointer(this.targetPointer);
return otherwiseEffect.applies(abilityToModify, source, game);
}
return false;
}
@Override
public ConditionalCostModificationEffect copy() {
return new ConditionalCostModificationEffect(this);
}
}

View file

@ -0,0 +1,140 @@
package mage.abilities.decorator;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.condition.Condition;
import mage.abilities.condition.FixedCondition;
import mage.abilities.condition.LockedInCondition;
import mage.abilities.effects.PreventionEffect;
import mage.abilities.effects.PreventionEffectImpl;
import mage.constants.Duration;
import mage.game.Game;
import mage.game.events.GameEvent;
/**
* @author JayDi85
*/
public class ConditionalPreventionEffect extends PreventionEffectImpl {
protected PreventionEffect effect;
protected PreventionEffect otherwiseEffect;
protected Condition baseCondition;
protected Condition condition;
protected boolean conditionState;
protected boolean initDone = false;
public ConditionalPreventionEffect(PreventionEffect effect, Condition condition, String text) {
this(effect, null, condition, text);
}
/**
* Only use this if both effects have the same layers
*
* @param effect
* @param otherwiseEffect
* @param condition
* @param text
*/
public ConditionalPreventionEffect(PreventionEffect effect, PreventionEffect otherwiseEffect, Condition condition, String text) {
super(effect.getDuration());
this.effect = effect;
this.otherwiseEffect = otherwiseEffect;
this.baseCondition = condition;
this.staticText = text;
}
public ConditionalPreventionEffect(final ConditionalPreventionEffect effect) {
super(effect);
this.effect = (PreventionEffect) effect.effect.copy();
if (effect.otherwiseEffect != null) {
this.otherwiseEffect = (PreventionEffect) effect.otherwiseEffect.copy();
}
this.condition = effect.condition; // TODO: checks conditional copy -- it's can be usefull for memory leaks fix?
this.conditionState = effect.conditionState;
this.baseCondition = effect.baseCondition;
this.initDone = effect.initDone;
}
@Override
public boolean isDiscarded() {
return this.discarded || effect.isDiscarded() || (otherwiseEffect != null && otherwiseEffect.isDiscarded());
}
@Override
public void init(Ability source, Game game) {
super.init(source, game);
if (baseCondition instanceof LockedInCondition) {
condition = new FixedCondition(((LockedInCondition) baseCondition).getBaseCondition().apply(game, source));
} else {
condition = baseCondition;
}
effect.setTargetPointer(this.targetPointer);
effect.init(source, game);
if (otherwiseEffect != null) {
otherwiseEffect.setTargetPointer(this.targetPointer);
otherwiseEffect.init(source, game);
}
initDone = true;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
if (conditionState) {
effect.setTargetPointer(this.targetPointer);
return effect.replaceEvent(event, source, game);
} else if (otherwiseEffect != null) {
otherwiseEffect.setTargetPointer(this.targetPointer);
return otherwiseEffect.replaceEvent(event, source, game);
}
if (!conditionState && effect.getDuration() == Duration.OneUse) {
used = true;
}
if (!conditionState && effect.getDuration() == Duration.Custom) {
this.discard();
}
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return effect.checksEventType(event, game)
|| (otherwiseEffect != null && otherwiseEffect.checksEventType(event, game));
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (!initDone) { // if simpleStaticAbility, init won't be called
init(source, game);
}
conditionState = condition.apply(game, source);
if (conditionState) {
effect.setTargetPointer(this.targetPointer);
return effect.applies(event, source, game);
} else if (otherwiseEffect != null) {
otherwiseEffect.setTargetPointer(this.targetPointer);
return otherwiseEffect.applies(event, source, game);
}
return false;
}
@Override
public String getText(Mode mode) {
if ((staticText == null || staticText.isEmpty()) && this.effect != null) { // usefull for conditional night/day card abilities
return effect.getText(mode);
}
return staticText;
}
@Override
public ConditionalPreventionEffect copy() {
return new ConditionalPreventionEffect(this);
}
}

View file

@ -1,7 +1,5 @@
package mage.abilities.decorator;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.abilities.condition.FixedCondition;
@ -12,12 +10,13 @@ import mage.constants.EffectType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public class ConditionalRequirementEffect extends RequirementEffect {
public class ConditionalRequirementEffect extends RequirementEffect {
protected RequirementEffect effect;
protected RequirementEffect otherwiseEffect;
@ -27,7 +26,14 @@ public class ConditionalRequirementEffect extends RequirementEffect {
protected boolean initDone = false;
public ConditionalRequirementEffect(RequirementEffect effect, Condition condition) {
this(Duration.WhileOnBattlefield, effect, condition, null, false);
this(effect, condition, null);
}
public ConditionalRequirementEffect(RequirementEffect effect, Condition condition, String text) {
this(effect.getDuration(), effect, condition, null, false);
if (text != null) {
setText(text);
}
}
public ConditionalRequirementEffect(Duration duration, RequirementEffect effect, Condition condition, RequirementEffect otherwiseEffect, boolean lockedInCondition) {
@ -75,7 +81,7 @@ public class ConditionalRequirementEffect extends RequirementEffect {
conditionState = condition.apply(game, source);
if (conditionState) {
effect.setTargetPointer(this.targetPointer);
return effect.applies(permanent, source,game);
return effect.applies(permanent, source, game);
} else if (otherwiseEffect != null) {
otherwiseEffect.setTargetPointer(this.targetPointer);
return otherwiseEffect.applies(permanent, source, game);
@ -138,7 +144,7 @@ public class ConditionalRequirementEffect extends RequirementEffect {
}
return null;
}
@Override
public ConditionalRequirementEffect copy() {
return new ConditionalRequirementEffect(this);

View file

@ -22,14 +22,25 @@ public class ConditionalRestrictionEffect extends RestrictionEffect {
protected boolean initDone = false;
public ConditionalRestrictionEffect(RestrictionEffect effect, Condition condition) {
this(Duration.WhileOnBattlefield, effect, condition, null);
this(effect, condition, null);
}
public ConditionalRestrictionEffect(RestrictionEffect effect, Condition condition, String text) {
this(effect.getDuration(), effect, condition, null, text);
}
public ConditionalRestrictionEffect(Duration duration, RestrictionEffect effect, Condition condition, RestrictionEffect otherwiseEffect) {
this(duration, effect, condition, otherwiseEffect, null);
}
public ConditionalRestrictionEffect(Duration duration, RestrictionEffect effect, Condition condition, RestrictionEffect otherwiseEffect, String text) {
super(duration);
this.effect = effect;
this.baseCondition = condition;
this.otherwiseEffect = otherwiseEffect;
if (text != null) {
this.setText(text);
}
}
public ConditionalRestrictionEffect(final ConditionalRestrictionEffect effect) {

View file

@ -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 PermanentsYouControlCount implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return game.getBattlefield().count(StaticFilters.FILTER_CONTROLLED_PERMANENT, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game);
}
@Override
public PermanentsYouControlCount copy() {
return instance;
}
@Override
public String toString() {
return "X";
}
@Override
public String getMessage() {
return "permanents you control";
}
}

View file

@ -311,14 +311,6 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
}
}
return dependentToEffects;
/*
return allEffectsInLayer.stream()
.filter(effect -> effect.getDependencyTypes().contains(dependendToTypes))
.map(Effect::getId)
.collect(Collectors.toSet());
}
return new HashSet<>();*/
}
@Override

View file

@ -369,6 +369,7 @@ public class ContinuousEffects implements Serializable {
replaceEffects.put(effect, applicableAbilities);
}
}
for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext(); ) {
PreventionEffect effect = iterator.next();
if (!effect.checksEventType(event, game)) {
@ -394,6 +395,7 @@ public class ContinuousEffects implements Serializable {
replaceEffects.put(effect, applicableAbilities);
}
}
return replaceEffects;
}

View file

@ -1,4 +1,3 @@
package mage.abilities.effects.common;
import mage.abilities.Ability;
@ -70,8 +69,8 @@ public class PreventDamageToAttachedEffect extends PreventionEffectImpl {
}
sb.append("damage to ");
sb.append(attachmentType.verb());
sb.append("creature, prevent ").append(amountToPrevent);;
sb.append("of that damage");
sb.append(" creature, prevent ").append(amountToPrevent);;
sb.append(" of that damage");
}
return sb.toString();
}

View file

@ -1,19 +1,13 @@
package mage.abilities.effects.common.continuous;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.TokenImpl;
import mage.game.permanent.token.Token;
import java.util.HashSet;
@ -21,7 +15,6 @@ import java.util.Set;
/**
* @author LevelX2
*
*/
public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
@ -29,13 +22,19 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
protected String theyAreStillType;
private final FilterPermanent filter;
private boolean loseColor = true;
protected boolean loseName = false;
public BecomesCreatureAllEffect(Token token, String theyAreStillType, FilterPermanent filter, Duration duration, boolean loseColor) {
this(token, theyAreStillType, filter, duration, loseColor, false);
}
public BecomesCreatureAllEffect(Token token, String theyAreStillType, FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName) {
super(duration, Outcome.BecomeCreature);
this.token = token;
this.theyAreStillType = theyAreStillType;
this.filter = filter;
this.loseColor = loseColor;
this.loseName = loseName;
}
public BecomesCreatureAllEffect(final BecomesCreatureAllEffect effect) {
@ -44,6 +43,7 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
this.theyAreStillType = effect.theyAreStillType;
this.filter = effect.filter.copy();
this.loseColor = effect.loseColor;
this.loseName = effect.loseName;
}
@Override
@ -65,30 +65,47 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Set<Permanent> affectedPermanents = new HashSet<>();
if (this.affectedObjectsSet) {
for(MageObjectReference ref : affectedObjectList) {
for (MageObjectReference ref : affectedObjectList) {
affectedPermanents.add(ref.getPermanent(game));
}
} else {
affectedPermanents = new HashSet<>(game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game));
}
for(Permanent permanent : affectedPermanents) {
for (Permanent permanent : affectedPermanents) {
if (permanent != null) {
switch (layer) {
case TextChangingEffects_3:
if (sublayer == SubLayer.NA) {
if (loseName) {
permanent.setName(token.getName());
}
}
break;
case TypeChangingEffects_4:
if (sublayer == SubLayer.NA) {
if (!token.getCardType().isEmpty()) {
for (CardType t : token.getCardType()) {
if (!permanent.getCardType().contains(t)) {
permanent.addCardType(t);
if (theyAreStillType != null) {
permanent.getSubtype(game).retainAll(SubType.getLandTypes());
permanent.getSubtype(game).addAll(token.getSubtype(game));
} else {
for (SubType t : token.getSubtype(game)) {
if (!permanent.hasSubtype(t, game)) {
permanent.getSubtype(game).add(t);
}
}
}
if (theyAreStillType == null) {
permanent.getSubtype(game).clear();
for (SuperType t : token.getSuperType()) {
if (!permanent.getSuperType().contains(t)) {
permanent.addSuperType(t);
}
}
if (!token.getSubtype(game).isEmpty()) {
permanent.getSubtype(game).addAll(token.getSubtype(game));
for (CardType t : token.getCardType()) {
if (!permanent.getCardType().contains(t)) {
permanent.addCardType(t);
}
}
}
break;
@ -141,7 +158,11 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
@Override
public boolean hasLayer(Layer layer) {
return layer == Layer.PTChangingEffects_7 || layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4;
return layer == Layer.PTChangingEffects_7
|| layer == Layer.AbilityAddingRemovingEffects_6
|| layer == Layer.ColorChangingEffects_5
|| layer == Layer.TypeChangingEffects_4
|| layer == Layer.TextChangingEffects_3;
}
@Override

View file

@ -69,30 +69,34 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl {
}
}
break;
case TypeChangingEffects_4:
if (sublayer == SubLayer.NA) {
if (loseAllAbilities) {
permanent.getSubtype(game).retainAll(SubType.getLandTypes());
permanent.getSubtype(game).addAll(token.getSubtype(game));
} else {
if (!token.getSubtype(game).isEmpty()) {
for (SubType subtype : token.getSubtype(game)) {
if (!permanent.hasSubtype(subtype, game)) {
permanent.getSubtype(game).add(subtype);
}
for (SubType t : token.getSubtype(game)) {
if (!permanent.hasSubtype(t, game)) {
permanent.getSubtype(game).add(t);
}
}
}
if (!token.getCardType().isEmpty()) {
for (CardType t : token.getCardType()) {
if (!permanent.getCardType().contains(t)) {
permanent.addCardType(t);
}
for (SuperType t : token.getSuperType()) {
if (!permanent.getSuperType().contains(t)) {
permanent.addSuperType(t);
}
}
for (CardType t : token.getCardType()) {
if (!permanent.getCardType().contains(t)) {
permanent.addCardType(t);
}
}
}
break;
case ColorChangingEffects_5:
if (sublayer == SubLayer.NA) {
if (loseAllAbilities) {
@ -107,6 +111,7 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl {
}
}
break;
case AbilityAddingRemovingEffects_6:
if (loseAllAbilities) {
permanent.removeAllAbilities(source.getSourceId(), game);

View file

@ -33,13 +33,13 @@ public class UntapAllDuringEachOtherPlayersUntapStepEffect extends ContinuousEff
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Boolean applied = (Boolean) game.getState().getValue(source.getSourceId() + "applied");
if (applied == null) {
applied = Boolean.FALSE;
}
if (!applied && layer == Layer.RulesEffects) {
if (!source.isControlledBy(game.getActivePlayerId()) && game.getStep().getType() == PhaseStep.UNTAP) {
game.getState().setValue(source.getSourceId() + "applied", true);
if (layer == Layer.RulesEffects && game.getStep().getType() == PhaseStep.UNTAP && !source.isControlledBy(game.getActivePlayerId())) {
Integer appliedTurn = (Integer) game.getState().getValue(source.getSourceId() + "appliedTurn");
if (appliedTurn == null) {
appliedTurn = 0;
}
if (appliedTurn < game.getTurnNum()) {
game.getState().setValue(source.getSourceId() + "appliedTurn", game.getTurnNum());
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) {
boolean untap = true;
for (RestrictionEffect effect : game.getContinuousEffects().getApplicableRestrictionEffects(permanent, game).keySet()) {
@ -50,10 +50,6 @@ public class UntapAllDuringEachOtherPlayersUntapStepEffect extends ContinuousEff
}
}
}
} else if (applied && layer == Layer.RulesEffects) {
if (game.getStep().getType() == PhaseStep.END_TURN) {
game.getState().setValue(source.getSourceId() + "applied", false);
}
}
return true;
}

View file

@ -4,11 +4,12 @@ import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.filter.common.FilterPermanentOrPlayerWithCounter;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetPermanentOrPlayerWithCounter;
import mage.target.common.TargetPermanentOrPlayer;
import java.io.Serializable;
import java.util.HashMap;
@ -47,7 +48,7 @@ public class ProliferateEffect extends OneShotEffect {
if (controller == null) {
return false;
}
Target target = new TargetPermanentOrPlayerWithCounter(0, Integer.MAX_VALUE, true);
Target target = new TargetPermanentOrPlayer(0, Integer.MAX_VALUE, new FilterPermanentOrPlayerWithCounter(), true);
Map<String, Serializable> options = new HashMap<>();
options.put("UI.right.btn.text", "Done");
controller.choose(Outcome.Benefit, target, source.getSourceId(), game, options);

View file

@ -106,7 +106,7 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect {
Cards revealedCards = new CardsImpl();
numberToReveal = Math.min(player.getHand().size(), numberToReveal);
if (player.getHand().size() > numberToReveal) {
TargetCardInHand chosenCards = new TargetCardInHand(numberToReveal, numberToReveal, new FilterCard("card in " + player.getLogName() + "'s hand"));
TargetCardInHand chosenCards = new TargetCardInHand(numberToReveal, numberToReveal, new FilterCard("card in " + player.getName() + "'s hand"));
chosenCards.setNotTarget(true);
if (chosenCards.canChoose(player.getId(), game) && player.chooseTarget(Outcome.Discard, player.getHand(), chosenCards, source, game)) {
if (!chosenCards.getTargets().isEmpty()) {

View file

@ -1,8 +1,10 @@
package mage.abilities.effects.keyword;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.events.GameEvent;
@ -19,10 +21,10 @@ public class AdaptEffect extends OneShotEffect {
public AdaptEffect(int adaptNumber) {
super(Outcome.BoostCreature);
this.adaptNumber = adaptNumber;
staticText = "Adapt " + adaptNumber +
" <i>(If this creature has no +1/+1 counters on it, put " +
CardUtil.numberToText(adaptNumber) + " +1/+1 counter" +
(adaptNumber > 1 ? "s" : "") + " on it.)</i>";
staticText = "Adapt " + adaptNumber
+ " <i>(If this creature has no +1/+1 counters on it, put "
+ CardUtil.numberToText(adaptNumber) + " +1/+1 counter"
+ (adaptNumber > 1 ? "s" : "") + " on it.)</i>";
}
private AdaptEffect(final AdaptEffect effect) {
@ -37,7 +39,15 @@ public class AdaptEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
// Verify source object did not change zone and is on the battlefield
MageObject sourceObject = source.getSourceObjectIfItStillExists(game);
if (sourceObject == null) {
if (game.getState().getZone(source.getSourceId()).equals(Zone.BATTLEFIELD)
&& source.getSourceObjectZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(source.getSourceId())) {
sourceObject = game.getPermanent(source.getSourceId());
}
}
Permanent permanent = ((Permanent) sourceObject);
if (permanent == null) {
return false;
}
@ -48,7 +58,8 @@ public class AdaptEffect extends OneShotEffect {
if (game.replaceEvent(event)) {
return false;
}
if (permanent.getCounters(game).getCount(CounterType.P1P1) == 0 || event.getFlag()) {
if (permanent.getCounters(game).getCount(CounterType.P1P1) == 0
|| event.getFlag()) {
permanent.addCounters(CounterType.P1P1.createInstance(event.getAmount()), source, game);
}
return true;

View file

@ -0,0 +1,26 @@
package mage.abilities.hint.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.common.PermanentsYouControlCount;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.game.Game;
/**
* @author JayDi85
*/
public enum PermanentsYouControlHint implements Hint {
instance;
private static final Hint hint = new ValueHint("Permanents you control", PermanentsYouControlCount.instance);
@Override
public String getText(Game game, Ability ability) {
return hint.getText(game, ability);
}
@Override
public Hint copy() {
return instance;
}
}

View file

@ -690,27 +690,33 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
sourceId = source.getSourceId();
}
}
GameEvent countersEvent = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTERS, objectId, sourceId, getControllerOrOwner(), counter.getName(), counter.getCount());
countersEvent.setAppliedEffects(appliedEffects);
countersEvent.setFlag(isEffect);
if (!game.replaceEvent(countersEvent)) {
int amount = countersEvent.getAmount();
GameEvent addingAllEvent = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTERS, objectId, sourceId, getControllerOrOwner(), counter.getName(), counter.getCount());
addingAllEvent.setAppliedEffects(appliedEffects);
addingAllEvent.setFlag(isEffect);
if (!game.replaceEvent(addingAllEvent)) {
int amount = addingAllEvent.getAmount();
boolean isEffectFlag = addingAllEvent.getFlag();
int finalAmount = amount;
for (int i = 0; i < amount; i++) {
Counter eventCounter = counter.copy();
eventCounter.remove(eventCounter.getCount() - 1);
GameEvent event = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTER, objectId, sourceId, getControllerOrOwner(), counter.getName(), 1);
event.setAppliedEffects(appliedEffects);
if (!game.replaceEvent(event)) {
GameEvent addingOneEvent = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTER, objectId, sourceId, getControllerOrOwner(), counter.getName(), 1);
addingOneEvent.setAppliedEffects(appliedEffects);
addingOneEvent.setFlag(isEffectFlag);
if (!game.replaceEvent(addingOneEvent)) {
getCounters(game).addCounter(eventCounter);
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER_ADDED, objectId, sourceId, getControllerOrOwner(), counter.getName(), 1));
GameEvent addedOneEvent = GameEvent.getEvent(GameEvent.EventType.COUNTER_ADDED, objectId, sourceId, getControllerOrOwner(), counter.getName(), 1);
addedOneEvent.setFlag(addingOneEvent.getFlag());
game.fireEvent(addedOneEvent);
} else {
finalAmount--;
returnCode = false;
}
}
if (finalAmount > 0) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERS_ADDED, objectId, sourceId, getControllerOrOwner(), counter.getName(), amount));
GameEvent addedAllEvent = GameEvent.getEvent(GameEvent.EventType.COUNTERS_ADDED, objectId, sourceId, getControllerOrOwner(), counter.getName(), amount);
addedAllEvent.setFlag(isEffectFlag);
game.fireEvent(addedAllEvent);
}
} else {
returnCode = false;

View file

@ -1,5 +1,6 @@
package mage.cards.decks.exporter;
import com.google.common.collect.ImmutableMap;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.decks.DeckFileFilter;
@ -13,9 +14,11 @@ import java.util.*;
*/
public class MtgArenaDeckExporter extends DeckExporter {
private final String ext = "mtga";
private final String description = "MTG Arena's deck format (*.mtga)";
private final FileFilter fileFilter = new DeckFileFilter(ext, description);
private static final String ext = "mtga";
private static final String description = "MTG Arena's deck format (*.mtga)";
private static final FileFilter fileFilter = new DeckFileFilter(ext, description);
private static final Map<String, String> SET_CODE_REPLACEMENTS = ImmutableMap.of("DOM", "DAR");
@Override
public void writeDeck(PrintWriter out, DeckCardLists deck) {
@ -33,7 +36,9 @@ public class MtgArenaDeckExporter extends DeckExporter {
private List<String> prepareCardsList(List<DeckCardInfo> sourceCards, Map<String, Integer> amount, String prefix) {
List<String> res = new ArrayList<>();
for (DeckCardInfo card : sourceCards) {
String name = card.getCardName() + " (" + card.getSetCode().toUpperCase(Locale.ENGLISH) + ") " + card.getCardNum();
String setCode = card.getSetCode().toUpperCase(Locale.ENGLISH);
setCode = SET_CODE_REPLACEMENTS.getOrDefault(setCode, setCode);
String name = card.getCardName() + " (" + setCode + ") " + card.getCardNum();
String code = prefix + name;
int curAmount = amount.getOrDefault(code, 0);
if (curAmount == 0) {

View file

@ -22,6 +22,7 @@ public class MtgArenaDeckExporterTest {
deck.getCards().add(new DeckCardInfo("Plains", "2", "RNA", 3));
deck.getCards().add(new DeckCardInfo("Plains", "2", "RNA", 5)); // must combine
deck.getCards().add(new DeckCardInfo("Mountain", "3", "RNA", 1));
deck.getCards().add(new DeckCardInfo("Goblin Chainwhirler", "129", "DOM", 4));
deck.getSideboard().add(new DeckCardInfo("Island", "1", "RNA", 2));
deck.getSideboard().add(new DeckCardInfo("Island", "1", "RNA", 5)); // must combine
deck.getSideboard().add(new DeckCardInfo("Mountain", "2", "RNA", 3));
@ -30,6 +31,7 @@ public class MtgArenaDeckExporterTest {
assertEquals("2 Forest (RNA) 1" + System.lineSeparator() +
"8 Plains (RNA) 2" + System.lineSeparator() +
"1 Mountain (RNA) 3" + System.lineSeparator() +
"4 Goblin Chainwhirler (DAR) 129" + System.lineSeparator() +
System.lineSeparator() +
"7 Island (RNA) 1" + System.lineSeparator() +
"3 Mountain (RNA) 2" + System.lineSeparator(),

View file

@ -1,13 +1,11 @@
package mage.filter.common;
import mage.MageItem;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import java.util.UUID;
import mage.MageItem;
/**
* @author nantuko
@ -28,24 +26,24 @@ public class FilterPermanentOrPlayerWithCounter extends FilterPermanentOrPlayer
@Override
public boolean match(MageItem o, Game game) {
if (o instanceof Player) {
if (((Player)o).getCounters().isEmpty()) {
return false;
}
} else if (o instanceof Permanent) {
if (((Permanent)o).getCounters(game).isEmpty()) {
return false;
if (super.match(o, game)) {
if (o instanceof Player) {
return !((Player) o).getCounters().isEmpty();
} else if (o instanceof Permanent) {
return !((Permanent) o).getCounters(game).isEmpty();
}
}
return super.match(o, game);
return false;
}
@Override
public boolean match(MageItem o, UUID sourceId, UUID playerId, Game game) {
if (o instanceof Player) {
return playerFilter.match((Player) o, sourceId, playerId, game);
} else if (o instanceof Permanent) {
return permanentFilter.match((Permanent) o, sourceId, playerId, game);
if (super.match(o, sourceId, playerId, game)) {
if (o instanceof Player) {
return !((Player) o).getCounters().isEmpty();
} else if (o instanceof Permanent) {
return !((Permanent) o).getCounters(game).isEmpty();
}
}
return false;
}

View file

@ -432,7 +432,7 @@ public interface Game extends MageItem, Serializable {
// game cheats (for tests only)
void cheat(UUID ownerId, Map<Zone, String> commands);
void cheat(UUID ownerId, UUID activePlayerId, List<Card> library, List<Card> hand, List<PermanentCard> battlefield, List<Card> graveyard);
void cheat(UUID ownerId, UUID activePlayerId, List<Card> library, List<Card> hand, List<PermanentCard> battlefield, List<Card> graveyard, List<Card> command);
// controlling the behaviour of replacement effects while permanents entering the battlefield
void setScopeRelevant(boolean scopeRelevant);

View file

@ -40,7 +40,6 @@ public abstract class GameCommanderImpl extends GameImpl {
@Override
protected void init(UUID choosingPlayerId) {
Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects"));
//Move commander to command zone
for (UUID playerId : state.getPlayerList(startingPlayerId)) {
Player player = getPlayer(playerId);
@ -49,7 +48,7 @@ public abstract class GameCommanderImpl extends GameImpl {
for (UUID commanderId : player.getCommandersIds()) {
Card commander = this.getCard(commanderId);
if (commander != null) {
initCommander(commander, ability, player);
initCommander(commander, player);
}
}
} else {
@ -57,20 +56,20 @@ public abstract class GameCommanderImpl extends GameImpl {
Card commander = this.getCard(player.getSideboard().iterator().next());
if (commander != null) {
player.addCommanderId(commander.getId());
initCommander(commander, ability, player);
initCommander(commander, player);
}
}
}
}
}
this.getState().addAbility(ability, null);
super.init(choosingPlayerId);
if (startingPlayerSkipsDraw) {
state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW));
}
}
private void initCommander(Card commander, Ability ability, Player player) {
public void initCommander(Card commander, Player player) {
Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects"));
commander.moveToZone(Zone.COMMAND, null, this, true);
commander.getAbilities().setControllerId(player.getId());
ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary));
@ -79,6 +78,7 @@ public abstract class GameCommanderImpl extends GameImpl {
CommanderInfoWatcher watcher = new CommanderInfoWatcher(commander.getId(), checkCommanderDamage);
getState().addWatcher(watcher);
watcher.addCardInfoToCommander(this);
this.getState().addAbility(ability, null);
}
//20130711

View file

@ -2851,26 +2851,39 @@ public abstract class GameImpl implements Game, Serializable {
}
@Override
public void cheat(UUID ownerId, UUID activePlayerId, List<Card> library, List<Card> hand, List<PermanentCard> battlefield, List<Card> graveyard) {
public void cheat(UUID ownerId, UUID activePlayerId, List<Card> library, List<Card> hand, List<PermanentCard> battlefield, List<Card> graveyard, List<Card> command) {
Player player = getPlayer(ownerId);
if (player != null) {
loadCards(ownerId, library);
loadCards(ownerId, hand);
loadCards(ownerId, battlefield);
loadCards(ownerId, graveyard);
loadCards(ownerId, command);
for (Card card : library) {
player.getLibrary().putOnTop(card, this);
}
for (Card card : hand) {
card.setZone(Zone.HAND, this);
player.getHand().add(card);
}
for (Card card : graveyard) {
card.setZone(Zone.GRAVEYARD, this);
player.getGraveyard().add(card);
}
// as commander (only commander games, look at init code in GameCommanderImpl)
if (this instanceof GameCommanderImpl) {
for (Card card : command) {
player.addCommanderId(card.getId());
// no needs in initCommander call -- it's uses on game startup (init)
}
} else if (!command.isEmpty()) {
throw new IllegalArgumentException("Command zone supports in commander test games");
}
// warning, permanents go to battlefield without resolve, continuus effects must be init
for (PermanentCard permanentCard : battlefield) {
permanentCard.setZone(Zone.BATTLEFIELD, this);

View file

@ -1,9 +1,5 @@
package mage.game;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
@ -21,8 +17,11 @@ import mage.game.turn.TurnMod;
import mage.players.Player;
import mage.watchers.common.CommanderInfoWatcher;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
*
* @author JRHerlehy
*/
public abstract class GameTinyLeadersImpl extends GameImpl {
@ -43,7 +42,6 @@ public abstract class GameTinyLeadersImpl extends GameImpl {
@Override
protected void init(UUID choosingPlayerId) {
Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects"));
//Move tiny leader to command zone
for (UUID playerId : state.getPlayerList(startingPlayerId)) {
Player player = getPlayer(playerId);
@ -55,6 +53,7 @@ public abstract class GameTinyLeadersImpl extends GameImpl {
this.loadCards(cards, playerId);
player.addCommanderId(commander.getId());
commander.moveToZone(Zone.COMMAND, null, this, true);
Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects"));
ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary));
ability.addEffect(new CommanderCostModification(commander.getId()));
// Commander rule #4 was removed Jan. 18, 2016
@ -63,13 +62,13 @@ public abstract class GameTinyLeadersImpl extends GameImpl {
CommanderInfoWatcher watcher = new CommanderInfoWatcher(commander.getId(), false);
getState().addWatcher(watcher);
watcher.addCardInfoToCommander(this);
this.getState().addAbility(ability, null);
} else {
throw new UnknownError("Commander card could not be created. Name: [" + player.getMatchPlayer().getDeck().getName() + ']');
}
}
}
this.getState().addAbility(ability, null);
super.init(choosingPlayerId);
if (startingPlayerSkipsDraw) {
state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW));

View file

@ -18,6 +18,9 @@ public class GameEvent implements Serializable {
protected UUID sourceId;
protected UUID playerId;
protected int amount;
// flags:
// for counters: event is result of effect (+1 from planeswalkers is cost, not effect)
// for combat damage: event is preventable damage
protected boolean flag;
protected String data;
protected Zone zone;
@ -433,6 +436,17 @@ public class GameEvent implements Serializable {
this.amount = amount;
}
public void setAmountForCounters(int amount, boolean isEffect) {
this.amount = amount;
// cost event must be "transformed" to effect event, as example:
// planeswalker's +1 cost will be affected by Pir, Imaginative Rascal (1 + 1) and applied as effect by Doubling Season (2 * 2)
// https://github.com/magefree/mage/issues/5802
if (isEffect) {
setFlag(true);
}
}
public boolean getFlag() {
return flag;
}

View file

@ -909,7 +909,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (countersToRemove > getCounters(game).getCount(CounterType.LOYALTY)) {
countersToRemove = getCounters(game).getCount(CounterType.LOYALTY);
}
getCounters(game).removeCounter(CounterType.LOYALTY, countersToRemove);
removeCounters(CounterType.LOYALTY.getName(), countersToRemove, game);
game.fireEvent(new DamagedPlaneswalkerEvent(objectId, sourceId, controllerId, actualDamage, combat));
return actualDamage;
}

View file

@ -569,6 +569,7 @@ public interface Player extends MageItem, Copyable<Player> {
// set the value for non mana X costs
int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost);
// TODO: rework choose replacement effects to use array, not map (it'a random order now)
int chooseReplacementEffect(Map<String, String> abilityMap, Game game);
TriggeredAbility chooseTriggeredAbility(List<TriggeredAbility> abilities, Game game);

View file

@ -182,7 +182,6 @@ public abstract class PlayerImpl implements Player, Serializable {
protected final Map<PhaseStep, Step.StepPart> silentPhaseSteps = ImmutableMap.<PhaseStep, Step.StepPart>builder().
put(PhaseStep.DECLARE_ATTACKERS, Step.StepPart.PRE).build();
public PlayerImpl(String name, RangeOfInfluence range) {
this(UUID.randomUUID());
this.name = name;
@ -611,7 +610,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
if (abilities.containsKey(HexproofAbility.getInstance().getId())) {
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
&& null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, this.getId(), game)) {
&& null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)) {
return false;
}
}
@ -2011,24 +2010,30 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean addCounters(Counter counter, Game game) {
boolean returnCode = true;
GameEvent countersEvent = GameEvent.getEvent(EventType.ADD_COUNTERS, playerId, null, playerId, counter.getName(), counter.getCount());
if (!game.replaceEvent(countersEvent)) {
int amount = countersEvent.getAmount();
GameEvent addingAllEvent = GameEvent.getEvent(EventType.ADD_COUNTERS, playerId, null, playerId, counter.getName(), counter.getCount());
if (!game.replaceEvent(addingAllEvent)) {
int amount = addingAllEvent.getAmount();
int finalAmount = amount;
boolean isEffectFlag = addingAllEvent.getFlag();
for (int i = 0; i < amount; i++) {
Counter eventCounter = counter.copy();
eventCounter.remove(eventCounter.getCount() - 1);
GameEvent event = GameEvent.getEvent(EventType.ADD_COUNTER, playerId, null, playerId, counter.getName(), 1);
if (!game.replaceEvent(event)) {
GameEvent addingOneEvent = GameEvent.getEvent(EventType.ADD_COUNTER, playerId, null, playerId, counter.getName(), 1);
addingOneEvent.setFlag(isEffectFlag);
if (!game.replaceEvent(addingOneEvent)) {
getCounters().addCounter(eventCounter);
game.fireEvent(GameEvent.getEvent(EventType.COUNTER_ADDED, playerId, null, playerId, counter.getName(), 1));
GameEvent addedOneEvent = GameEvent.getEvent(EventType.COUNTER_ADDED, playerId, null, playerId, counter.getName(), 1);
addedOneEvent.setFlag(addingOneEvent.getFlag());
game.fireEvent(addedOneEvent);
} else {
finalAmount--;
returnCode = false;
}
}
if (finalAmount > 0) {
game.fireEvent(GameEvent.getEvent(EventType.COUNTERS_ADDED, playerId, null, playerId, counter.getName(), amount));
GameEvent addedAllEvent = GameEvent.getEvent(EventType.COUNTERS_ADDED, playerId, null, playerId, counter.getName(), amount);
addedAllEvent.setFlag(addingAllEvent.getFlag());
game.fireEvent(addedAllEvent);
}
} else {
returnCode = false;

View file

@ -0,0 +1,221 @@
package mage.players;
import mage.MageItem;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.Modes;
import mage.abilities.TriggeredAbility;
import mage.abilities.costs.VariableCost;
import mage.abilities.costs.mana.ManaCost;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.decks.Deck;
import mage.choices.Choice;
import mage.constants.Outcome;
import mage.constants.RangeOfInfluence;
import mage.game.Game;
import mage.game.combat.CombatGroup;
import mage.game.draft.Draft;
import mage.game.match.Match;
import mage.game.permanent.Permanent;
import mage.game.tournament.Tournament;
import mage.target.Target;
import mage.target.TargetAmount;
import mage.target.TargetCard;
import mage.target.TargetPlayer;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static com.google.common.collect.Iterables.getOnlyElement;
import static java.util.stream.Collectors.toList;
public class StubPlayer extends PlayerImpl implements Player {
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) {
if (target instanceof TargetPlayer) {
for (Player player : game.getPlayers().values()) {
if (player.getId().equals(getId()) && target.canTarget(getId(), game)) {
target.add(player.getId(), game);
return true;
}
}
}
return false;
}
public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) {
cards.getCards(game).stream().map(MageItem::getId).forEach(cardId -> target.add(cardId, game));
return true;
}
@Override
public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
if ("cards to PUT on the BOTTOM of your library (Discard for Mulligan)".equals(target.getFilter().getMessage())) {
chooseDiscardBottom(game, target.getMinNumberOfTargets(), cards.getCards(game)
.stream().map(MageItem::getId).collect(toList())).forEach(cardId -> target.add(cardId, game));
} else {
UUID cardId = getOnlyElement(cards.getCards(game)).getId();
if (chooseScry(game, cardId)) {
target.add(cardId, game);
return true;
}
}
return false;
}
public List<UUID> chooseDiscardBottom(Game game, int count, List<UUID> cardIds) {
return cardIds.subList(0, count);
}
public boolean chooseScry(Game game, UUID cardId) {
return false;
}
@Override
public void shuffleLibrary(Ability source, Game game) {
}
public StubPlayer(String name, RangeOfInfluence range) {
super(name, range);
}
@Override
public void abort() {
}
@Override
public void skip() {
}
@Override
public Player copy() {
return null;
}
@Override
public boolean priority(Game game) {
return false;
}
@Override
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map<String, Serializable> options) {
return false;
}
@Override
public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) {
return false;
}
@Override
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) {
return false;
}
@Override
public boolean chooseMulligan(Game game) {
return false;
}
@Override
public boolean chooseUse(Outcome outcome, String message, Ability source, Game game) {
return false;
}
@Override
public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) {
return false;
}
@Override
public boolean choose(Outcome outcome, Choice choice, Game game) {
return false;
}
@Override
public boolean choosePile(Outcome outcome, String message, List<? extends Card> pile1, List<? extends Card> pile2, Game game) {
return false;
}
@Override
public boolean playMana(Ability ability, ManaCost unpaid, String promptText, Game game) {
return false;
}
@Override
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
return 0;
}
@Override
public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost) {
return 0;
}
@Override
public int chooseReplacementEffect(Map<String, String> abilityMap, Game game) {
return 0;
}
@Override
public TriggeredAbility chooseTriggeredAbility(List<TriggeredAbility> abilities, Game game) {
return null;
}
@Override
public Mode chooseMode(Modes modes, Ability source, Game game) {
return null;
}
@Override
public void selectAttackers(Game game, UUID attackingPlayerId) {
}
@Override
public void selectBlockers(Game game, UUID defendingPlayerId) {
}
@Override
public UUID chooseAttackerOrder(List<Permanent> attacker, Game game) {
return null;
}
@Override
public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup, List<UUID> blockerOrder, Game game) {
return null;
}
@Override
public void assignDamage(int damage, List<UUID> targets, String singleTargetName, UUID sourceId, Game game) {
}
@Override
public int getAmount(int min, int max, String message, Game game) {
return 0;
}
@Override
public void sideboard(Match match, Deck deck) {
}
@Override
public void construct(Tournament tournament, Deck deck) {
}
@Override
public void pickCard(List<Card> cards, Deck deck, Draft draft) {
}
}

View file

@ -66,7 +66,7 @@ public class Targets extends ArrayList<Target> {
if (!canChoose(source.getSourceId(), playerId, game)) {
return false;
}
int state = game.bookmarkState();
//int state = game.bookmarkState();
while (!isChosen()) {
Target target = this.getUnchosen().get(0);
UUID targetController = playerId;
@ -93,7 +93,7 @@ public class Targets extends ArrayList<Target> {
// Check if there are some rules for targets are violated, if so reset the targets and start again
if (this.getUnchosen().isEmpty()
&& game.replaceEvent(new GameEvent(GameEvent.EventType.TARGETS_VALID, source.getSourceId(), source.getSourceId(), source.getControllerId()), source)) {
game.restoreState(state, "Targets");
//game.restoreState(state, "Targets");
clearChosen();
}
}

View file

@ -8,23 +8,22 @@ package mage.target.common;
import mage.filter.common.FilterOpponentOrPlaneswalker;
/**
*
* @author LevelX2
*/
public class TargetOpponentOrPlaneswalker extends TargetPermanentOrPlayer {
public TargetOpponentOrPlaneswalker() {
this(1, 1, new FilterOpponentOrPlaneswalker("opponent or planeswalker"), false);
}
public TargetOpponentOrPlaneswalker(int numTargets) {
this(numTargets, numTargets, new FilterOpponentOrPlaneswalker(), false);
this(1);
}
public TargetOpponentOrPlaneswalker(FilterOpponentOrPlaneswalker filter) {
this(1, 1, filter, false);
}
public TargetOpponentOrPlaneswalker(int numTargets) {
this(numTargets, numTargets, new FilterOpponentOrPlaneswalker("opponent or planeswalker"), false);
}
public TargetOpponentOrPlaneswalker(int minNumTargets, int maxNumTargets, FilterOpponentOrPlaneswalker filter, boolean notTarget) {
super(minNumTargets, maxNumTargets, filter, notTarget);
}

View file

@ -1,28 +1,26 @@
package mage.target.common;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.constants.Zone;
import mage.filter.Filter;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.common.FilterPermanentOrPlayer;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetImpl;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
*
* @author nantuko
*/
public class TargetPermanentOrPlayer extends TargetImpl {
protected FilterPermanentOrPlayer filter;
protected FilterPermanent filterPermanent;
public TargetPermanentOrPlayer() {
this(1, 1);
@ -46,14 +44,12 @@ public class TargetPermanentOrPlayer extends TargetImpl {
this.zone = Zone.ALL;
this.filter = filter;
this.targetName = filter.getMessage();
this.filterPermanent = this.filter.getPermanentFilter();
this.notTarget = notTarget;
}
public TargetPermanentOrPlayer(final TargetPermanentOrPlayer target) {
super(target);
this.filter = target.filter.copy();
this.filterPermanent = target.filterPermanent.copy();
}
@Override
@ -61,10 +57,6 @@ public class TargetPermanentOrPlayer extends TargetImpl {
return filter;
}
public void setFilter(FilterPermanentOrPlayer filter) {
this.filter = filter;
}
@Override
public boolean canTarget(UUID id, Game game) {
Permanent permanent = game.getPermanent(id);
@ -118,7 +110,7 @@ public class TargetPermanentOrPlayer extends TargetImpl {
* {@link mage.players.Player} that can be chosen. Should only be used for
* Ability targets since this checks for protection, shroud etc.
*
* @param sourceId - the target event source
* @param sourceId - the target event source
* @param sourceControllerId - controller of the target event source
* @param game
* @return - true if enough valid {@link mage.game.permanent.Permanent} or
@ -130,14 +122,14 @@ public class TargetPermanentOrPlayer extends TargetImpl {
MageObject targetSource = game.getObject(sourceId);
for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
Player player = game.getPlayer(playerId);
if (player != null && player.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.getPlayerFilter().match(player, sourceId, sourceControllerId, game)) {
if (player != null && player.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(player, sourceId, sourceControllerId, game)) {
count++;
if (count >= this.minNumberOfTargets) {
return true;
}
}
}
for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT, sourceControllerId, game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) {
if (permanent.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(permanent, sourceId, sourceControllerId, game)) {
count++;
if (count >= this.minNumberOfTargets) {
@ -170,7 +162,7 @@ public class TargetPermanentOrPlayer extends TargetImpl {
}
}
}
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterPermanent, sourceControllerId, game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) {
if (filter.match(permanent, null, sourceControllerId, game) && filter.match(permanent, game)) {
count++;
if (count >= this.minNumberOfTargets) {
@ -187,11 +179,11 @@ public class TargetPermanentOrPlayer extends TargetImpl {
MageObject targetSource = game.getObject(sourceId);
for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
Player player = game.getPlayer(playerId);
if (player != null && (notTarget || player.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.getPlayerFilter().match(player, sourceId, sourceControllerId, game)) {
if (player != null && (notTarget || player.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(player, sourceId, sourceControllerId, game)) {
possibleTargets.add(playerId);
}
}
for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterPermanent(), sourceControllerId, game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) {
if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(permanent, sourceId, sourceControllerId, game)) {
possibleTargets.add(permanent.getId());
}
@ -204,11 +196,11 @@ public class TargetPermanentOrPlayer extends TargetImpl {
Set<UUID> possibleTargets = new HashSet<>();
for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
Player player = game.getPlayer(playerId);
if (player != null && filter.getPlayerFilter().match(player, game)) {
if (player != null && filter.match(player, game)) {
possibleTargets.add(playerId);
}
}
for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, sourceControllerId, game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) {
if (filter.match(permanent, null, sourceControllerId, game)) {
possibleTargets.add(permanent.getId());
}
@ -237,6 +229,6 @@ public class TargetPermanentOrPlayer extends TargetImpl {
}
public FilterPermanent getFilterPermanent() {
return filterPermanent.copy();
return filter.getPermanentFilter().copy();
}
}

View file

@ -1,87 +0,0 @@
package mage.target.common;
import mage.abilities.Ability;
import mage.filter.common.FilterPermanentOrPlayerWithCounter;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import java.util.UUID;
import mage.filter.FilterPermanent;
import mage.filter.predicate.permanent.CounterPredicate;
/**
*
* @author nantuko
*/
public class TargetPermanentOrPlayerWithCounter extends TargetPermanentOrPlayer {
protected final FilterPermanentOrPlayerWithCounter targetFilter;
public TargetPermanentOrPlayerWithCounter() {
this(1, 1);
}
public TargetPermanentOrPlayerWithCounter(int numTargets) {
this(numTargets, numTargets);
}
public TargetPermanentOrPlayerWithCounter(int minNumTargets, int maxNumTargets) {
this(minNumTargets, maxNumTargets, false);
}
public TargetPermanentOrPlayerWithCounter(int minNumTargets, int maxNumTargets, boolean notTarget) {
super(minNumTargets, maxNumTargets, notTarget);
this.targetFilter = new FilterPermanentOrPlayerWithCounter();
this.filterPermanent = new FilterPermanent();
this.filterPermanent.add(new CounterPredicate(null));
this.targetName = targetFilter.getMessage();
}
public TargetPermanentOrPlayerWithCounter(final TargetPermanentOrPlayerWithCounter target) {
super(target);
this.targetFilter = target.targetFilter.copy();
super.setFilter(this.targetFilter);
}
@Override
public TargetPermanentOrPlayerWithCounter copy() {
return new TargetPermanentOrPlayerWithCounter(this);
}
@Override
public boolean canTarget(UUID id, Game game) {
Permanent permanent = game.getPermanent(id);
if (permanent != null) {
if (permanent.getCounters(game).isEmpty()) {
return false;
}
}
Player player = game.getPlayer(id);
if (player != null) {
if (player.getCounters().isEmpty()) {
return false;
}
}
return super.canTarget(id, game);
}
@Override
public boolean canTarget(UUID id, Ability source, Game game) {
Permanent permanent = game.getPermanent(id);
if (permanent != null) {
if (permanent.getCounters(game).isEmpty()) {
return false;
}
}
Player player = game.getPlayer(id);
if (player != null) {
if (player.getCounters().isEmpty()) {
return false;
}
}
return super.canTarget(id, source, game);
}
}

View file

@ -37,10 +37,12 @@ public class DragonOnTheBattlefieldWhileSpellWasCastWatcher extends Watcher {
// revealed a Dragon card or controlled a Dragon as you cast the spell
if (spell != null) {
boolean revealedOrOnBattlefield = false;
for (Cost cost : spell.getSpellAbility().getCosts()) {
if (cost instanceof RevealTargetFromHandCost) {
revealedOrOnBattlefield = ((RevealTargetFromHandCost) cost).getNumberRevealedCards() > 0;
break;
if (spell.getSpellAbility() != null) {
for (Cost cost : spell.getSpellAbility().getCosts()) {
if (cost instanceof RevealTargetFromHandCost) {
revealedOrOnBattlefield = ((RevealTargetFromHandCost) cost).getNumberRevealedCards() > 0;
break;
}
}
}
if (!revealedOrOnBattlefield) {