* Implemeented consumable flag for asThoughtEffects and a player choice which effect to use if multiple consumable effects allow the same action.

This commit is contained in:
LevelX2 2020-08-21 20:49:43 +02:00
parent 8105d8b26c
commit 90c6637dc2
10 changed files with 78 additions and 18 deletions

View file

@ -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";
}

View file

@ -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";
}

View file

@ -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";
}

View file

@ -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";
}

View file

@ -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. "
+ "<i>(If a card has multiple permanent types, choose one as you play it.)</i>";
}

View file

@ -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");

View file

@ -20,4 +20,6 @@ public interface AsThoughEffect extends ContinuousEffect {
@Override
AsThoughEffect copy();
boolean isConsumable();
}

View file

@ -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;
}
}

View file

@ -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<ReplacementEffect, Set<Ability>> getApplicableReplacementEffects(GameEvent event, Game game) {
Map<ReplacementEffect, Set<Ability>> 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<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext(); ) {
for (Iterator<ReplacementEffect> 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<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext(); ) {
for (Iterator<PreventionEffect> 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<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(type, game);
@ -531,13 +532,18 @@ public class ContinuousEffects implements Serializable {
}
}
Set<ApprovingObject> possibleApprovingObjects = new HashSet<>();
for (AsThoughEffect effect : asThoughEffectsList) {
Set<Ability> 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<String, String> 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<ReplacementEffect, Set<Ability>> rEffects = getApplicableReplacementEffects(event, game);
// Remove all consumed effects (ability dependant)
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext(); ) {
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext();) {
ReplacementEffect entry = it1.next();
if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9.
Set<UUID> consumedAbilitiesIds = consumed.get(entry.getId());
@ -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<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) {
for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<ContinuousEffect, Set<UUID>> entry = iterator.next();
if (!appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself
continue;

View file

@ -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<String, String> 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
}
}
}