diff --git a/Mage.Sets/src/mage/cards/g/GisaAndGeralf.java b/Mage.Sets/src/mage/cards/g/GisaAndGeralf.java index 53c0cb9314..ba9f3e14d4 100644 --- a/Mage.Sets/src/mage/cards/g/GisaAndGeralf.java +++ b/Mage.Sets/src/mage/cards/g/GisaAndGeralf.java @@ -57,7 +57,7 @@ public final class GisaAndGeralf extends CardImpl { class GisaAndGeralfCastFromGraveyardEffect extends AsThoughEffectImpl { GisaAndGeralfCastFromGraveyardEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.PutCreatureInPlay); + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.PutCreatureInPlay, true); staticText = "During each of your turns, you may cast a Zombie creature card from your graveyard"; } diff --git a/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java b/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java index 958ce26958..185018949c 100644 --- a/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java +++ b/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java @@ -98,7 +98,7 @@ class KaradorGhostChieftainCostReductionEffect extends CostModificationEffectImp class KaradorGhostChieftainCastFromGraveyardEffect extends AsThoughEffectImpl { KaradorGhostChieftainCastFromGraveyardEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.PutCreatureInPlay); + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.PutCreatureInPlay, true); staticText = "During each of your turns, you may cast one creature card from your graveyard"; } diff --git a/Mage.Sets/src/mage/cards/k/KessDissidentMage.java b/Mage.Sets/src/mage/cards/k/KessDissidentMage.java index af71d68ec9..a5d2a732a1 100644 --- a/Mage.Sets/src/mage/cards/k/KessDissidentMage.java +++ b/Mage.Sets/src/mage/cards/k/KessDissidentMage.java @@ -71,7 +71,7 @@ public final class KessDissidentMage extends CardImpl { class KessDissidentMageCastFromGraveyardEffect extends AsThoughEffectImpl { KessDissidentMageCastFromGraveyardEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit, true); staticText = "During each of your turns, you may cast an instant or sorcery card from your graveyard"; } diff --git a/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java b/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java index ccbfd97635..2bc0fa566f 100644 --- a/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java +++ b/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java @@ -79,7 +79,7 @@ enum LurrusOfTheDreamDenCompanionCondition implements CompanionCondition { class LurrusOfTheDreamDenCastFromGraveyardEffect extends AsThoughEffectImpl { LurrusOfTheDreamDenCastFromGraveyardEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit, true); staticText = "During each of your turns, you may cast one permanent spell with converted mana cost 2 or less from your graveyard"; } diff --git a/Mage.Sets/src/mage/cards/m/MuldrothaTheGravetide.java b/Mage.Sets/src/mage/cards/m/MuldrothaTheGravetide.java index 7cd525cb39..a0db30df28 100644 --- a/Mage.Sets/src/mage/cards/m/MuldrothaTheGravetide.java +++ b/Mage.Sets/src/mage/cards/m/MuldrothaTheGravetide.java @@ -66,7 +66,7 @@ public final class MuldrothaTheGravetide extends CardImpl { class MuldrothaTheGravetideCastFromGraveyardEffect extends AsThoughEffectImpl { public MuldrothaTheGravetideCastFromGraveyardEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit, true); staticText = "During each of your turns, you may play up to one permanent card of each permanent type from your graveyard. " + "(If a card has multiple permanent types, choose one as you play it.)"; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/KaradorGhostChieftainTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/KaradorGhostChieftainTest.java index 216a9d4faf..4049c98290 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/KaradorGhostChieftainTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/KaradorGhostChieftainTest.java @@ -67,7 +67,7 @@ public class KaradorGhostChieftainTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); - addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 7); // Exile target creature you control, then return that card to the battlefield under your control. @@ -96,7 +96,7 @@ public class KaradorGhostChieftainTest extends CardTestPlayerBase { } @Test - @Ignore // It's not possible yet to select which ability to use to allow a asThoughtAs effect + // @Ignore // It's not possible yet to select which ability to use to allow a asThoughtAs effect public void test_castFromGraveyardWithDifferentApprovers() { setStrictChooseMode(true); @@ -125,6 +125,8 @@ public class KaradorGhostChieftainTest extends CardTestPlayerBase { castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Accursed Horde"); + setChoice(playerA, "During each of your turns, you may cast a Zombie creature card from your graveyard"); // Choose the permitting object + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); diff --git a/Mage/src/main/java/mage/abilities/effects/AsThoughEffect.java b/Mage/src/main/java/mage/abilities/effects/AsThoughEffect.java index 1c58869db3..2933328297 100644 --- a/Mage/src/main/java/mage/abilities/effects/AsThoughEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/AsThoughEffect.java @@ -20,4 +20,6 @@ public interface AsThoughEffect extends ContinuousEffect { @Override AsThoughEffect copy(); + + boolean isConsumable(); } diff --git a/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java index 52bf7f4436..8397a6e718 100644 --- a/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java @@ -17,16 +17,23 @@ import mage.players.Player; public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements AsThoughEffect { protected AsThoughEffectType type; + boolean consumable; public AsThoughEffectImpl(AsThoughEffectType type, Duration duration, Outcome outcome) { + this(type, duration, outcome, false); + } + + public AsThoughEffectImpl(AsThoughEffectType type, Duration duration, Outcome outcome, boolean consumable) { super(duration, outcome); this.type = type; this.effectType = EffectType.ASTHOUGH; + this.consumable = consumable; } public AsThoughEffectImpl(final AsThoughEffectImpl effect) { super(effect); this.type = effect.type; + this.consumable = effect.consumable; } @Override @@ -93,4 +100,9 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements return true; } + @Override + public boolean isConsumable() { + return consumable; + } + } diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java index 9e1d89bd8f..db27aea89c 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java @@ -1,7 +1,6 @@ package mage.abilities.effects; import mage.MageObject; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.MageSingleton; import mage.abilities.StaticAbility; @@ -31,6 +30,8 @@ import java.util.*; import java.util.Map.Entry; import java.util.stream.Collectors; import mage.ApprovingObject; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; /** * @author BetaSteward_at_googlemail.com @@ -333,7 +334,7 @@ public class ContinuousEffects implements Serializable { * @param event * @param game * @return a list of all {@link ReplacementEffect} that apply to the current - * event + * event */ private Map> getApplicableReplacementEffects(GameEvent event, Game game) { Map> replaceEffects = new HashMap<>(); @@ -342,7 +343,7 @@ public class ContinuousEffects implements Serializable { } // boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT); //get all applicable transient Replacement effects - for (Iterator iterator = replacementEffects.iterator(); iterator.hasNext(); ) { + for (Iterator iterator = replacementEffects.iterator(); iterator.hasNext();) { ReplacementEffect effect = iterator.next(); if (!effect.checksEventType(event, game)) { continue; @@ -375,7 +376,7 @@ public class ContinuousEffects implements Serializable { } } - for (Iterator iterator = preventionEffects.iterator(); iterator.hasNext(); ) { + for (Iterator iterator = preventionEffects.iterator(); iterator.hasNext();) { PreventionEffect effect = iterator.next(); if (!effect.checksEventType(event, game)) { continue; @@ -506,7 +507,7 @@ public class ContinuousEffects implements Serializable { * @param controllerId * @param game * @return sourceId of the permitting effect if any exists otherwise returns - * null + * null */ public ApprovingObject asThough(UUID objectId, AsThoughEffectType type, Ability affectedAbility, UUID controllerId, Game game) { List asThoughEffectsList = getApplicableAsThoughEffects(type, game); @@ -531,13 +532,18 @@ public class ContinuousEffects implements Serializable { } } + Set possibleApprovingObjects = new HashSet<>(); for (AsThoughEffect effect : asThoughEffectsList) { Set abilities = asThoughEffectsMap.get(type).getAbility(effect.getId()); for (Ability ability : abilities) { if (affectedAbility == null) { // applies to own ability (one effect can be used in multiple abilities) if (effect.applies(idToCheck, ability, controllerId, game)) { - return new ApprovingObject(ability, game); + if (effect.isConsumable() && !game.inCheckPlayableState()) { + possibleApprovingObjects.add(new ApprovingObject(ability, game)); + } else { + return new ApprovingObject(ability, game); + } } } else { // applies to affected ability @@ -548,11 +554,38 @@ public class ContinuousEffects implements Serializable { } if (effect.applies(idToCheck, affectedAbility, ability, game, controllerId)) { - return new ApprovingObject(ability, game); + if (effect.isConsumable() && !game.inCheckPlayableState()) { + possibleApprovingObjects.add(new ApprovingObject(ability, game)); + } else { + return new ApprovingObject(ability, game); + } } } } } + if (possibleApprovingObjects.size() == 1) { + return possibleApprovingObjects.iterator().next(); + } else if (possibleApprovingObjects.size() > 1) { + // Select the ability that you use to permit the action + Map keyChoices = new HashMap<>(); + for(ApprovingObject approvingObject :possibleApprovingObjects) { + MageObject mageObject = game.getObject(approvingObject.getApprovingAbility().getSourceId()); + keyChoices.put(approvingObject.getApprovingAbility().getId().toString(), + (approvingObject.getApprovingAbility().getRule(mageObject == null ? "" : mageObject.getName())) + + (mageObject == null ? "" : " (" + mageObject.getIdName() + ")")); + } + Choice choicePermitting = new ChoiceImpl(true); + choicePermitting.setMessage("Choose the permitting object"); + choicePermitting.setKeyChoices(keyChoices); + Player player = game.getPlayer(controllerId); + player.choose(Outcome.Detriment, choicePermitting, game); + for(ApprovingObject approvingObject: possibleApprovingObjects) { + if (approvingObject.getApprovingAbility().getId().toString().equals(choicePermitting.getChoiceKey())) { + return approvingObject; + } + } + } + } return null; @@ -800,7 +833,7 @@ public class ContinuousEffects implements Serializable { do { Map> rEffects = getApplicableReplacementEffects(event, game); // Remove all consumed effects (ability dependant) - for (Iterator it1 = rEffects.keySet().iterator(); it1.hasNext(); ) { + for (Iterator it1 = rEffects.keySet().iterator(); it1.hasNext();) { ReplacementEffect entry = it1.next(); if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9. Set consumedAbilitiesIds = consumed.get(entry.getId()); @@ -985,7 +1018,7 @@ public class ContinuousEffects implements Serializable { .entrySet() .stream() .filter(entry -> dependentTo.contains(entry.getKey().getId()) - && entry.getValue().contains(effect.getId())) + && entry.getValue().contains(effect.getId())) .forEach(entry -> { entry.getValue().remove(effect.getId()); dependentTo.remove(entry.getKey().getId()); @@ -1019,7 +1052,7 @@ public class ContinuousEffects implements Serializable { continue; } // check if waiting effects can be applied now - for (Iterator>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) { + for (Iterator>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) { Map.Entry> entry = iterator.next(); if (!appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself continue; diff --git a/Mage/src/main/java/mage/choices/ChoiceImpl.java b/Mage/src/main/java/mage/choices/ChoiceImpl.java index 66ea9d37c1..0809bea419 100644 --- a/Mage/src/main/java/mage/choices/ChoiceImpl.java +++ b/Mage/src/main/java/mage/choices/ChoiceImpl.java @@ -215,6 +215,17 @@ public class ChoiceImpl implements Choice, Serializable { answers.remove(needChoice); return true; } + + } + } + // no key answer found, try to macht by text starting with + for (String needChoice : answers) { + for (Map.Entry currentChoice : this.getKeyChoices().entrySet()) { + if (currentChoice.getValue().startsWith(needChoice)) { + this.setChoiceByKey(currentChoice.getKey()); + answers.remove(needChoice); + return true; + } } } } else { @@ -231,4 +242,4 @@ public class ChoiceImpl implements Choice, Serializable { } return false; // can't find answer } -} \ No newline at end of file +}