mirror of
https://github.com/correl/mage.git
synced 2024-12-25 11:11:16 +00:00
* 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:
parent
8105d8b26c
commit
90c6637dc2
10 changed files with 78 additions and 18 deletions
|
@ -57,7 +57,7 @@ public final class GisaAndGeralf extends CardImpl {
|
||||||
class GisaAndGeralfCastFromGraveyardEffect extends AsThoughEffectImpl {
|
class GisaAndGeralfCastFromGraveyardEffect extends AsThoughEffectImpl {
|
||||||
|
|
||||||
GisaAndGeralfCastFromGraveyardEffect() {
|
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";
|
staticText = "During each of your turns, you may cast a Zombie creature card from your graveyard";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ class KaradorGhostChieftainCostReductionEffect extends CostModificationEffectImp
|
||||||
class KaradorGhostChieftainCastFromGraveyardEffect extends AsThoughEffectImpl {
|
class KaradorGhostChieftainCastFromGraveyardEffect extends AsThoughEffectImpl {
|
||||||
|
|
||||||
KaradorGhostChieftainCastFromGraveyardEffect() {
|
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";
|
staticText = "During each of your turns, you may cast one creature card from your graveyard";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ public final class KessDissidentMage extends CardImpl {
|
||||||
class KessDissidentMageCastFromGraveyardEffect extends AsThoughEffectImpl {
|
class KessDissidentMageCastFromGraveyardEffect extends AsThoughEffectImpl {
|
||||||
|
|
||||||
KessDissidentMageCastFromGraveyardEffect() {
|
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";
|
staticText = "During each of your turns, you may cast an instant or sorcery card from your graveyard";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ enum LurrusOfTheDreamDenCompanionCondition implements CompanionCondition {
|
||||||
class LurrusOfTheDreamDenCastFromGraveyardEffect extends AsThoughEffectImpl {
|
class LurrusOfTheDreamDenCastFromGraveyardEffect extends AsThoughEffectImpl {
|
||||||
|
|
||||||
LurrusOfTheDreamDenCastFromGraveyardEffect() {
|
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";
|
staticText = "During each of your turns, you may cast one permanent spell with converted mana cost 2 or less from your graveyard";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ public final class MuldrothaTheGravetide extends CardImpl {
|
||||||
class MuldrothaTheGravetideCastFromGraveyardEffect extends AsThoughEffectImpl {
|
class MuldrothaTheGravetideCastFromGraveyardEffect extends AsThoughEffectImpl {
|
||||||
|
|
||||||
public MuldrothaTheGravetideCastFromGraveyardEffect() {
|
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. "
|
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>";
|
+ "<i>(If a card has multiple permanent types, choose one as you play it.)</i>";
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ public class KaradorGhostChieftainTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
|
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 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);
|
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 7);
|
||||||
|
|
||||||
// Exile target creature you control, then return that card to the battlefield under your control.
|
// 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
|
@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() {
|
public void test_castFromGraveyardWithDifferentApprovers() {
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
|
|
||||||
|
@ -125,6 +125,8 @@ public class KaradorGhostChieftainTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
|
||||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Accursed Horde");
|
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");
|
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion");
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,4 +20,6 @@ public interface AsThoughEffect extends ContinuousEffect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
AsThoughEffect copy();
|
AsThoughEffect copy();
|
||||||
|
|
||||||
|
boolean isConsumable();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,16 +17,23 @@ import mage.players.Player;
|
||||||
public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements AsThoughEffect {
|
public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements AsThoughEffect {
|
||||||
|
|
||||||
protected AsThoughEffectType type;
|
protected AsThoughEffectType type;
|
||||||
|
boolean consumable;
|
||||||
|
|
||||||
public AsThoughEffectImpl(AsThoughEffectType type, Duration duration, Outcome outcome) {
|
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);
|
super(duration, outcome);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.effectType = EffectType.ASTHOUGH;
|
this.effectType = EffectType.ASTHOUGH;
|
||||||
|
this.consumable = consumable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AsThoughEffectImpl(final AsThoughEffectImpl effect) {
|
public AsThoughEffectImpl(final AsThoughEffectImpl effect) {
|
||||||
super(effect);
|
super(effect);
|
||||||
this.type = effect.type;
|
this.type = effect.type;
|
||||||
|
this.consumable = effect.consumable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -93,4 +100,9 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConsumable() {
|
||||||
|
return consumable;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package mage.abilities.effects;
|
package mage.abilities.effects;
|
||||||
|
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.MageObjectReference;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.MageSingleton;
|
import mage.abilities.MageSingleton;
|
||||||
import mage.abilities.StaticAbility;
|
import mage.abilities.StaticAbility;
|
||||||
|
@ -31,6 +30,8 @@ import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import mage.ApprovingObject;
|
import mage.ApprovingObject;
|
||||||
|
import mage.choices.Choice;
|
||||||
|
import mage.choices.ChoiceImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
|
@ -333,7 +334,7 @@ public class ContinuousEffects implements Serializable {
|
||||||
* @param event
|
* @param event
|
||||||
* @param game
|
* @param game
|
||||||
* @return a list of all {@link ReplacementEffect} that apply to the current
|
* @return a list of all {@link ReplacementEffect} that apply to the current
|
||||||
* event
|
* event
|
||||||
*/
|
*/
|
||||||
private Map<ReplacementEffect, Set<Ability>> getApplicableReplacementEffects(GameEvent event, Game game) {
|
private Map<ReplacementEffect, Set<Ability>> getApplicableReplacementEffects(GameEvent event, Game game) {
|
||||||
Map<ReplacementEffect, Set<Ability>> replaceEffects = new HashMap<>();
|
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);
|
// boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT);
|
||||||
//get all applicable transient Replacement effects
|
//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();
|
ReplacementEffect effect = iterator.next();
|
||||||
if (!effect.checksEventType(event, game)) {
|
if (!effect.checksEventType(event, game)) {
|
||||||
continue;
|
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();
|
PreventionEffect effect = iterator.next();
|
||||||
if (!effect.checksEventType(event, game)) {
|
if (!effect.checksEventType(event, game)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -506,7 +507,7 @@ public class ContinuousEffects implements Serializable {
|
||||||
* @param controllerId
|
* @param controllerId
|
||||||
* @param game
|
* @param game
|
||||||
* @return sourceId of the permitting effect if any exists otherwise returns
|
* @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) {
|
public ApprovingObject asThough(UUID objectId, AsThoughEffectType type, Ability affectedAbility, UUID controllerId, Game game) {
|
||||||
List<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(type, 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) {
|
for (AsThoughEffect effect : asThoughEffectsList) {
|
||||||
Set<Ability> abilities = asThoughEffectsMap.get(type).getAbility(effect.getId());
|
Set<Ability> abilities = asThoughEffectsMap.get(type).getAbility(effect.getId());
|
||||||
for (Ability ability : abilities) {
|
for (Ability ability : abilities) {
|
||||||
if (affectedAbility == null) {
|
if (affectedAbility == null) {
|
||||||
// applies to own ability (one effect can be used in multiple abilities)
|
// applies to own ability (one effect can be used in multiple abilities)
|
||||||
if (effect.applies(idToCheck, ability, controllerId, game)) {
|
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 {
|
} else {
|
||||||
// applies to affected ability
|
// applies to affected ability
|
||||||
|
@ -548,11 +554,38 @@ public class ContinuousEffects implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (effect.applies(idToCheck, affectedAbility, ability, game, controllerId)) {
|
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;
|
return null;
|
||||||
|
|
||||||
|
@ -800,7 +833,7 @@ public class ContinuousEffects implements Serializable {
|
||||||
do {
|
do {
|
||||||
Map<ReplacementEffect, Set<Ability>> rEffects = getApplicableReplacementEffects(event, game);
|
Map<ReplacementEffect, Set<Ability>> rEffects = getApplicableReplacementEffects(event, game);
|
||||||
// Remove all consumed effects (ability dependant)
|
// 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();
|
ReplacementEffect entry = it1.next();
|
||||||
if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9.
|
if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9.
|
||||||
Set<UUID> consumedAbilitiesIds = consumed.get(entry.getId());
|
Set<UUID> consumedAbilitiesIds = consumed.get(entry.getId());
|
||||||
|
@ -985,7 +1018,7 @@ public class ContinuousEffects implements Serializable {
|
||||||
.entrySet()
|
.entrySet()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(entry -> dependentTo.contains(entry.getKey().getId())
|
.filter(entry -> dependentTo.contains(entry.getKey().getId())
|
||||||
&& entry.getValue().contains(effect.getId()))
|
&& entry.getValue().contains(effect.getId()))
|
||||||
.forEach(entry -> {
|
.forEach(entry -> {
|
||||||
entry.getValue().remove(effect.getId());
|
entry.getValue().remove(effect.getId());
|
||||||
dependentTo.remove(entry.getKey().getId());
|
dependentTo.remove(entry.getKey().getId());
|
||||||
|
@ -1019,7 +1052,7 @@ public class ContinuousEffects implements Serializable {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// check if waiting effects can be applied now
|
// 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();
|
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
|
if (!appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -215,6 +215,17 @@ public class ChoiceImpl implements Choice, Serializable {
|
||||||
answers.remove(needChoice);
|
answers.remove(needChoice);
|
||||||
return true;
|
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 {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue