Arcane Savant and friends

This commit is contained in:
Adam Pantel
2020-01-08 16:38:51 -05:00
parent 1f01e2ae16
commit f401c3900c
19 changed files with 210 additions and 22 deletions

View File

@@ -579,7 +579,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
@Override @Override
public PaymentDecision visit(CostReveal cost) { public PaymentDecision visit(CostReveal cost) {
final String type = cost.getType(); final String type = cost.getType();
CardCollectionView hand = player.getCardsIn(ZoneType.Hand); CardCollectionView hand = player.getCardsIn(cost.getRevealFrom());
if (cost.payCostFromSource()) { if (cost.payCostFromSource()) {
if (!hand.contains(source)) { if (!hand.contains(source)) {

View File

@@ -1558,8 +1558,7 @@ public class GameAction {
// Where there are none, it should bring up speed controls // Where there are none, it should bring up speed controls
game.fireEvent(new GameEventGameStarted(gameType, first, game.getPlayers())); game.fireEvent(new GameEventGameStarted(gameType, first, game.getPlayers()));
// Emissary's Plot runPreOpeningHandActions(first);
// runPreOpeningHandActions(first);
game.setAge(GameStage.Mulligan); game.setAge(GameStage.Mulligan);
for (final Player p1 : game.getPlayers()) { for (final Player p1 : game.getPlayers()) {

View File

@@ -1,5 +1,6 @@
package forge.game; package forge.game;
import com.google.common.base.Enums;
import com.google.common.base.Function; import com.google.common.base.Function;
import forge.StaticData; import forge.StaticData;
import forge.deck.CardPool; import forge.deck.CardPool;
@@ -144,4 +145,8 @@ public enum GameType {
public String getDescription() { public String getDescription() {
return description; return description;
} }
public static GameType smartValueOf(String name) {
return Enums.getIfPresent(GameType.class, name).orNull();
}
} }

View File

@@ -260,6 +260,8 @@ public class AbilityUtils {
list = sa.getRootAbility().getPaidList("SacrificedCards"); list = sa.getRootAbility().getPaidList("SacrificedCards");
} else if (defined.startsWith("Sacrificed")) { } else if (defined.startsWith("Sacrificed")) {
list = sa.getRootAbility().getPaidList("Sacrificed"); list = sa.getRootAbility().getPaidList("Sacrificed");
} else if (defined.startsWith("Revealed")) {
list = sa.getRootAbility().getPaidList("Revealed");
} else if (defined.startsWith("DiscardedCards")) { } else if (defined.startsWith("DiscardedCards")) {
list = sa.getRootAbility().getPaidList("DiscardedCards"); list = sa.getRootAbility().getPaidList("DiscardedCards");
} else if (defined.startsWith("Discarded")) { } else if (defined.startsWith("Discarded")) {

View File

@@ -58,6 +58,15 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
validDesc = sa.getParam("ValidDesc"); validDesc = sa.getParam("ValidDesc");
} }
String message;
if (sa.hasParam("SelectPrompt")) {
message = sa.getParam("SelectPrompt");
} else if (validDesc.equals("card")) {
message = Localizer.getInstance().getMessage("lblChooseACardName");
} else {
message = Localizer.getInstance().getMessage("lblChooseASpecificCard", validDesc);
}
boolean randomChoice = sa.hasParam("AtRandom"); boolean randomChoice = sa.hasParam("AtRandom");
boolean chooseFromDefined = sa.hasParam("ChooseFromDefinedCards"); boolean chooseFromDefined = sa.hasParam("ChooseFromDefinedCards");
for (final Player p : tgtPlayers) { for (final Player p : tgtPlayers) {
@@ -100,12 +109,9 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
} }
} }
Collections.sort(faces); Collections.sort(faces);
chosen = p.getController().chooseCardName(sa, faces, Localizer.getInstance().getMessage("lblChooseACardName")); chosen = p.getController().chooseCardName(sa, faces, message);
} else { } else {
// use CardFace because you might name a alternate name // use CardFace because you might name a alternate names
//"name a card" in mtg card oracle text is "choose a card name",change text
final String message = validDesc.equals("card") ? Localizer.getInstance().getMessage("lblChooseACardName") : Localizer.getInstance().getMessage("lblChooseASpecificCard", validDesc);
Predicate<ICardFace> cpp = Predicates.alwaysTrue(); Predicate<ICardFace> cpp = Predicates.alwaysTrue();
if (sa.hasParam("ValidCards")) { if (sa.hasParam("ValidCards")) {
cpp = CardFacePredicates.valid(valid); cpp = CardFacePredicates.valid(valid);
@@ -119,6 +125,9 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
p.getGame().getAction().nofityOfValue(sa, host, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), chosen), p); p.getGame().getAction().nofityOfValue(sa, host, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), chosen), p);
p.setNamedCard(chosen); p.setNamedCard(chosen);
} }
if (sa.hasParam("NoteFor")) {
p.addNoteForName(sa.getParam("NoteFor"), "Name:" + chosen);
}
} }
} }
} }

View File

@@ -345,6 +345,14 @@ public class PumpEffect extends SpellAbilityEffect {
pumpForget = sa.getParam("ForgetObjects"); pumpForget = sa.getParam("ForgetObjects");
} }
if (sa.hasParam("NoteCardsFor")) {
for (final Card c : AbilityUtils.getDefinedCards(host, sa.getParam("NoteCards"), sa)) {
for (Player p : tgtPlayers) {
p.addNoteForName(sa.getParam("NoteCardsFor"), "Id:" + c.getId());
}
}
}
if (pumpForget != null) { if (pumpForget != null) {
for (final Object o : AbilityUtils.getDefinedObjects(host, pumpForget, sa)) { for (final Object o : AbilityUtils.getDefinedObjects(host, pumpForget, sa)) {
host.removeRemembered(o); host.removeRemembered(o);
@@ -394,7 +402,7 @@ public class PumpEffect extends SpellAbilityEffect {
if (sa.hasParam("AtEOT") && !tgtCards.isEmpty()) { if (sa.hasParam("AtEOT") && !tgtCards.isEmpty()) {
registerDelayedTrigger(sa, sa.getParam("AtEOT"), tgtCards); registerDelayedTrigger(sa, sa.getParam("AtEOT"), tgtCards);
} }
for (final Card tgtC : untargetedCards) { for (final Card tgtC : untargetedCards) {
// only pump things in PumpZone // only pump things in PumpZone
if (!tgtC.isInZone(pumpZone)) { if (!tgtC.isInZone(pumpZone)) {

View File

@@ -1817,7 +1817,7 @@ public class Card extends GameEntity implements Comparable<Card> {
sbLong.append(getName()).append(" can block ") sbLong.append(getName()).append(" can block ")
.append(CardType.getPluralType(k[1])) .append(CardType.getPluralType(k[1]))
.append(" as though it had reach.\r\n"); .append(" as though it had reach.\r\n");
} else if (keyword.startsWith("MayEffectFromOpeningHand")) { } else if (keyword.startsWith("MayEffectFromOpening")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
// need to get SpellDescription from Svar // need to get SpellDescription from Svar
String desc = AbilityFactory.getMapParams(getSVar(k[1])).get("SpellDescription"); String desc = AbilityFactory.getMapParams(getSVar(k[1])).get("SpellDescription");

View File

@@ -1759,6 +1759,17 @@ public class CardProperty {
if (!card.isCommander()) { if (!card.isCommander()) {
return false; return false;
} }
} else if (property.startsWith("NotedFor")) {
final String key = property.substring("NotedFor".length());
for (String note : sourceController.getNotesForName(key)) {
if (note.equals("Name:" + card.getName())) {
return true;
}
if (note.equals("Id:" + card.getId())) {
return true;
}
}
return false;
} else { } else {
// StringType done in CardState // StringType done in CardState
if (!card.getCurrentState().hasProperty(property, sourceController, source, spellAbility)) { if (!card.getCurrentState().hasProperty(property, sourceController, source, spellAbility)) {

View File

@@ -286,6 +286,8 @@ public class Cost implements Serializable {
costParts.add(0, cp); costParts.add(0, cp);
} }
} }
sort();
} }
private static CostPart parseCostPart(String parse, boolean tapCost, boolean untapCost) { private static CostPart parseCostPart(String parse, boolean tapCost, boolean untapCost) {
@@ -457,6 +459,12 @@ public class Cost implements Serializable {
return new CostReveal(splitStr[0], splitStr[1], description); return new CostReveal(splitStr[0], splitStr[1], description);
} }
if (parse.startsWith("RevealFromExile<")) {
final String[] splitStr = abCostParse(parse, 3);
final String description = splitStr.length > 2 ? splitStr[2] : null;
return new CostReveal(splitStr[0], splitStr[1], description, ZoneType.Exile);
}
if (parse.startsWith("ExiledMoveToGrave<")) { if (parse.startsWith("ExiledMoveToGrave<")) {
final String[] splitStr = abCostParse(parse, 3); final String[] splitStr = abCostParse(parse, 3);
final String description = splitStr.length > 2 ? splitStr[2] : null; final String description = splitStr.length > 2 ? splitStr[2] : null;

View File

@@ -38,26 +38,37 @@ public class CostReveal extends CostPartWithList {
*/ */
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private ZoneType revealFrom = ZoneType.Hand;
public CostReveal(final String amount, final String type, final String description) { public CostReveal(final String amount, final String type, final String description) {
super(amount, type, description); super(amount, type, description);
} }
public CostReveal(final String amount, final String type, final String description, final ZoneType zoneType) {
super(amount, type, description);
this.revealFrom = zoneType;
}
@Override @Override
public boolean isReusable() { return true; } public boolean isReusable() { return true; }
@Override @Override
public boolean isRenewable() { return true; } public boolean isRenewable() { return true; }
public ZoneType getRevealFrom() {
return revealFrom;
}
@Override @Override
public final boolean canPay(final SpellAbility ability, final Player payer) { public final boolean canPay(final SpellAbility ability, final Player payer) {
final Card source = ability.getHostCard(); final Card source = ability.getHostCard();
CardCollectionView handList = payer.getCardsIn(ZoneType.Hand); CardCollectionView handList = payer.getCardsIn(revealFrom);
final String type = this.getType(); final String type = this.getType();
final Integer amount = this.convertAmount(); final Integer amount = this.convertAmount();
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
return source.isInZone(ZoneType.Hand); return source.isInZone(revealFrom);
} else if (this.getType().equals("Hand")) { } else if (this.getType().equals("Hand")) {
return true; return true;
} else if (this.getType().equals("SameColor")) { } else if (this.getType().equals("SameColor")) {
@@ -100,7 +111,7 @@ public class CostReveal extends CostPartWithList {
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
sb.append(this.getType()); sb.append(this.getType());
} else if (this.getType().equals("Hand")) { } else if (this.getType().equals("Hand")) {
return ("Reveal you hand"); return ("Reveal your hand");
} else if (this.getType().equals("SameColor")) { } else if (this.getType().equals("SameColor")) {
return ("Reveal " + i + " cards from your hand that share a color"); return ("Reveal " + i + " cards from your hand that share a color");
} else { } else {
@@ -115,7 +126,9 @@ public class CostReveal extends CostPartWithList {
sb.append(Cost.convertAmountTypeToWords(i, this.getAmount(), desc.toString())); sb.append(Cost.convertAmountTypeToWords(i, this.getAmount(), desc.toString()));
} }
sb.append(" from your hand");
sb.append(" from your ");
sb.append(revealFrom.getTranslatedName());
return sb.toString(); return sb.toString();
} }
@@ -154,4 +167,11 @@ public class CostReveal extends CostPartWithList {
public <T> T accept(ICostVisitor<T> visitor) { public <T> T accept(ICostVisitor<T> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }
@Override
public int paymentOrder() {
// Caller of the Untamed needs the reveal to happen before the mana cost
if (!revealFrom.equals(ZoneType.Hand)) { return -1; }
return 5;
}
} }

View File

@@ -36,6 +36,7 @@ import forge.game.event.*;
import forge.game.keyword.Keyword; import forge.game.keyword.Keyword;
import forge.game.keyword.KeywordCollection; import forge.game.keyword.KeywordCollection;
import forge.game.keyword.KeywordCollection.KeywordCollectionView; import forge.game.keyword.KeywordCollection.KeywordCollectionView;
import forge.game.keyword.KeywordInterface;
import forge.game.keyword.KeywordsChange; import forge.game.keyword.KeywordsChange;
import forge.game.mana.ManaPool; import forge.game.mana.ManaPool;
import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseHandler;
@@ -108,6 +109,7 @@ public class Player extends GameEntity implements Comparable<Player> {
private int numDrawnThisDrawStep = 0; private int numDrawnThisDrawStep = 0;
private int numDiscardedThisTurn = 0; private int numDiscardedThisTurn = 0;
private int numCardsInHandStartedThisTurnWith = 0; private int numCardsInHandStartedThisTurnWith = 0;
private final Map<String, FCollection<String>> notes = Maps.newHashMap();
private boolean revolt = false; private boolean revolt = false;
@@ -1601,6 +1603,19 @@ public class Player extends GameEntity implements Comparable<Player> {
numCardsInHandStartedThisTurnWith = num; numCardsInHandStartedThisTurnWith = num;
} }
public void addNoteForName(String notedFor, String noted) {
if (!notes.containsKey(notedFor)) {
notes.put(notedFor, new FCollection<>());
}
notes.get(notedFor).add(noted);
}
public FCollection<String> getNotesForName(String notedFor) {
if (!notes.containsKey(notedFor)) {
notes.put(notedFor, new FCollection<>());
}
return notes.get(notedFor);
}
public final CardCollectionView mill(final int n, final ZoneType destination, public final CardCollectionView mill(final int n, final ZoneType destination,
final boolean bottom, SpellAbility sa, CardZoneTable table) { final boolean bottom, SpellAbility sa, CardZoneTable table) {
final CardCollectionView lib = getCardsIn(ZoneType.Library); final CardCollectionView lib = getCardsIn(ZoneType.Library);
@@ -2748,6 +2763,21 @@ public class Player extends GameEntity implements Comparable<Player> {
} }
com.add(conspire); com.add(conspire);
} }
for (final Card c : getCardsIn(ZoneType.Library)) {
for (KeywordInterface inst : c.getKeywords()) {
String kw = inst.getOriginal();
if (kw.startsWith("MayEffectFromOpeningDeck")) {
String[] split = kw.split(":");
final String effName = split[1];
final SpellAbility effect = AbilityFactory.getAbility(c.getSVar(effName), c);
effect.setActivatingPlayer(this);
getController().playSpellAbilityNoStack(effect, true);
}
}
}
} }
public static DetachedCardEffect createCommanderEffect(Game game, Card commander) { public static DetachedCardEffect createCommanderEffect(Game game, Card commander) {

View File

@@ -17,9 +17,11 @@
*/ */
package forge.game.spellability; package forge.game.spellability;
import com.google.common.collect.Lists;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.game.Game; import forge.game.Game;
import forge.game.GameObject; import forge.game.GameObject;
import forge.game.GameType;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollectionView; import forge.game.card.CardCollectionView;
@@ -36,11 +38,7 @@ import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import java.util.ArrayList; import java.util.*;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/** /**
* <p> * <p>
@@ -147,6 +145,15 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
this.setPhases(PhaseType.parseRange(params.get("ConditionPhases"))); this.setPhases(PhaseType.parseRange(params.get("ConditionPhases")));
} }
if (params.containsKey("ConditionGameTypes")) {
String[] gameTypeNames = params.get("ConditionGameTypes").split(",");
List<GameType> gameTypes = Lists.newArrayList();
for (String name : gameTypeNames) {
gameTypes.add(GameType.smartValueOf(name));
}
this.setGameTypes(gameTypes);
}
if (params.containsKey("ConditionChosenColor")) { if (params.containsKey("ConditionChosenColor")) {
this.setColorToCheck(params.get("ConditionChosenColor")); this.setColorToCheck(params.get("ConditionChosenColor"));
} }
@@ -322,6 +329,13 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
} }
} }
if (this.getGameTypes().size() > 0) {
GameType currGameType = sa.getHostCard().getGame().getRules().getGameType();
if (!getGameTypes().contains(currGameType)) {
return false;
}
}
if (this.getCardsInHand() != -1) { if (this.getCardsInHand() != -1) {
// Can handle Library of Alexandria, or Hellbent // Can handle Library of Alexandria, or Hellbent
if (activator.getCardsIn(ZoneType.Hand).size() != this.getCardsInHand()) { if (activator.getCardsIn(ZoneType.Hand).size() != this.getCardsInHand()) {

View File

@@ -20,7 +20,9 @@ package forge.game.spellability;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.collect.Lists;
import forge.game.Game; import forge.game.Game;
import forge.game.GameType;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollectionView; import forge.game.card.CardCollectionView;
@@ -141,6 +143,15 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
this.setPhases(PhaseType.parseRange(params.get("ActivationPhases"))); this.setPhases(PhaseType.parseRange(params.get("ActivationPhases")));
} }
if (params.containsKey("ActivationGameTypes")) {
String[] gameTypeNames = params.get("ActivationGameTypes").split(",");
List<GameType> gameTypes = Lists.newArrayList();
for (String name : gameTypeNames) {
gameTypes.add(GameType.smartValueOf(name));
}
this.setGameTypes(gameTypes);
}
if (params.containsKey("ActivationCardsInHand")) { if (params.containsKey("ActivationCardsInHand")) {
this.setActivateCardsInHand(Integer.parseInt(params.get("ActivationCardsInHand"))); this.setActivateCardsInHand(Integer.parseInt(params.get("ActivationCardsInHand")));
} }

View File

@@ -21,6 +21,7 @@ import java.util.List;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.game.GameType;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -98,6 +99,9 @@ public class SpellAbilityVariables implements Cloneable {
/** The phases. */ /** The phases. */
private List<PhaseType> phases = Lists.newArrayList(); private List<PhaseType> phases = Lists.newArrayList();
/** The GameTypes */
private List<GameType> gameTypes = Lists.newArrayList();
/** The b sorcery speed. */ /** The b sorcery speed. */
private boolean sorcerySpeed = false; private boolean sorcerySpeed = false;
@@ -386,6 +390,18 @@ public class SpellAbilityVariables implements Cloneable {
this.phases.addAll(phases); this.phases.addAll(phases);
} }
/**
* <p>
* Setter for the field <code>gameTypes</code>.
* </p>
*
* @param gameTypes
*/
public final void setGameTypes(final List<GameType> gameTypes) {
this.gameTypes.clear();
this.gameTypes.addAll(gameTypes);
}
/** /**
* <p> * <p>
* setActivateCardsInHand. * setActivateCardsInHand.
@@ -686,6 +702,15 @@ public class SpellAbilityVariables implements Cloneable {
return this.phases; return this.phases;
} }
/**
* Gets the game types.
*
* @return the phases
*/
public final List<GameType> getGameTypes() {
return this.gameTypes;
}
/** /**
* Gets the present defined. * Gets the present defined.

View File

@@ -0,0 +1,13 @@
Name:Arcane Savant
ManaCost:3 U U
Types:Creature Human Wizard
PT:3/3
K:MayEffectFromOpeningDeck:DBReveal
SVar:DBReveal:DB$ Reveal | RevealDefined$ Self | SubAbility$ DBExile | SpellDescription$ Before you shuffle your deck to start the game, you may reveal this card from your deck and exile an instant or sorcery card you drafted that isnt in your deck.
SVar:DBExile:DB$ ChangeZone | Origin$ Sideboard | Destination$ Exile | ChangeType$ Instant.YouOwn,Sorcery.YouOwn | ChangeNum$ 1 | Optional$ True | RememberChanged$ True | SubAbility$ DBPump | ConditionGameTypes$ Draft,QuestDraft | SelectPrompt$ Exile with Arcane Savant
SVar:DBPump:DB$ Pump | NoteCards$ Remembered | NoteCardsFor$ ArcaneSavant | SubAbility$ DBCleanup
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPlay | TriggerDescription$ When CARDNAME enters the battlefield, copy a card you exiled with cards named Arcane Savant. You may cast the copy without paying its mana cost.
SVar:TrigPlay:DB$ Play | Valid$ Card.YouOwn+NotedForArcaneSavant | ValidZone$ Exile | Amount$ 1 | CopyOnce$ True | WithoutManaCost$ True | Optional$ True | CopyCard$ True | SpellDescription$ You may copy the exiled card. If you do, you may cast the copy without paying its mana cost. | SubAbility$ DBCleanup
SVar:Picture:https://img.scryfall.com/cards/large/en/cn2/27.jpg?1517813031
Oracle:Before you shuffle your deck to start the game, you may reveal this card from your deck and exile an instant or sorcery card you drafted that isnt in your deck.\nWhen Arcane Savant enters the battlefield, copy a card you exiled with cards named Arcane Savant. You may cast the copy without paying its mana cost.

View File

@@ -0,0 +1,13 @@
Name:Caller of the Untamed
ManaCost:3 G
Types:Creature Elf Shaman
PT:2/4
K:MayEffectFromOpeningDeck:DBReveal
SVar:DBReveal:DB$ Reveal | RevealDefined$ Self | SubAbility$ DBExile | SpellDescription$ Before you shuffle your deck to start the game, you may reveal this card from your deck and exile an instant or sorcery card you drafted that isnt in your deck.
SVar:DBExile:DB$ ChangeZone | Origin$ Sideboard | Destination$ Exile | ChangeType$ Creature.YouOwn | ChangeNum$ 1 | Optional$ True | RememberChanged$ True | SubAbility$ DBPump | ConditionGameTypes$ Draft,QuestDraft | SelectPrompt$ Exile with Caller of the Untamed
SVar:DBPump:DB$ Pump | NoteCards$ Remembered | NoteCardsFor$ CallerOfTheUntamed | SubAbility$ DBCleanup
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
A:AB$ CopyPermanent | Cost$ RevealFromExile<1/Creature.YouOwn+NotedForCallerOfTheUntamed> X T | CostDesc$ {X}, {T} | Defined$ Revealed | References$ X | SpellDescription$ Create a token thats a copy of a card you exiled with cards named Caller of the Untamed. X is the converted mana cost of that card. | SubAbility$ DBCleanup
SVar:X:Revealed$CardManaCost
SVar:Picture:https://img.scryfall.com/cards/large/en/cn2/62.jpg?1517813031
Oracle:Before you shuffle your deck to start the game, you may reveal this card from your deck and exile a creature card you drafted that isnt in your deck.\n{X}, {T}: Create a token thats a copy of a card you exiled with cards named Caller of the Untamed. X is the converted mana cost of that card.

View File

@@ -0,0 +1,16 @@
Name:Volatile Chimera
ManaCost:2 R
Types:Creature Elemental Chimera
PT:3/2
K:MayEffectFromOpeningDeck:DBReveal
SVar:DBReveal:DB$ Reveal | RevealDefined$ Self | SubAbility$ DBChoose | SpellDescription$ Before you shuffle your deck to start the game, you may reveal this card from your deck and exile three or more creature cards you drafted that aren't in your deck.
SVar:DBChoose:DB$ ChooseCard | MinAmount$ 0 | Amount$ X | References$ X | Choices$ Creature.YouOwn | ChoiceTitle$ Exile with Volatile Chimera | ChoiceZone$ Sideboard | RememberChosen$ True | ConditionGameTypes$ Draft,QuestDraft | SubAbility$ DBExile
SVar:DBExile:DB$ ChangeZone | Origin$ Sideboard | Destination$ Exile | Defined$ Remembered | ConditionCheckSVar$ Y | ConditionSVarCompare$ GE3 | SubAbility$ DBPump | References$ Y
SVar:DBPump:DB$ Pump | NoteCards$ Remembered | NoteCardsFor$ VolatileChimera | SubAbility$ DBCleanup
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
A:AB$ ChooseCard | Cost$ 1 R | ChoiceZone$ Exile | AtRandom$ True | Choices$ Card.YouOwn+NotedForVolatileChimera | SubAbility$ DBClone | SpellDescription$ Choose a card at random you exiled with cards named Volatile Chimera. Volatile Chimera becomes a copy of that card, except it has this ability.
SVar:DBClone:DB$ Clone | Defined$ ChosenCard | GainThisAbility$ True
SVar:X:Count$InYourSideboard
SVar:Y:Count$RememberedSize
SVar:Picture:https://img.scryfall.com/cards/large/en/cn2/62.jpg?1517813031
Oracle:Before you shuffle your deck to start the game, you may reveal this card from your deck and exile three or more creature cards you drafted that aren't in your deck.\n{1}{R}: Choose a card at random you exiled with cards named Volatile Chimera. Volatile Chimera becomes a copy of that card, except it has this ability.

View File

@@ -811,7 +811,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
InputSelectCardsFromList inp = null; InputSelectCardsFromList inp = null;
if (cost.getType().equals("SameColor")) { if (cost.getType().equals("SameColor")) {
final Integer num = cost.convertAmount(); final Integer num = cost.convertAmount();
CardCollectionView hand = player.getCardsIn(ZoneType.Hand); CardCollectionView hand = player.getCardsIn(cost.getRevealFrom());
final CardCollectionView hand2 = hand; final CardCollectionView hand2 = hand;
hand = CardLists.filter(hand, new Predicate<Card>() { hand = CardLists.filter(hand, new Predicate<Card>() {
@Override @Override
@@ -844,7 +844,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
else { else {
Integer num = cost.convertAmount(); Integer num = cost.convertAmount();
CardCollectionView hand = player.getCardsIn(ZoneType.Hand); CardCollectionView hand = player.getCardsIn(cost.getRevealFrom());
hand = CardLists.getValidCards(hand, cost.getType().split(";"), player, source, ability); hand = CardLists.getValidCards(hand, cost.getType().split(";"), player, source, ability);
if (num == null) { if (num == null) {
@@ -861,6 +861,9 @@ public class HumanCostDecision extends CostDecisionMakerBase {
if (num == 0) { if (num == 0) {
return PaymentDecision.number(0); return PaymentDecision.number(0);
} }
if (hand.size() == num) {
return PaymentDecision.card(hand);
}
inp = new InputSelectCardsFromList(controller, num, num, hand, ability); inp = new InputSelectCardsFromList(controller, num, num, hand, ability);
inp.setMessage("Select %d more " + cost.getDescriptiveType() + " card(s) to reveal."); inp.setMessage("Select %d more " + cost.getDescriptiveType() + " card(s) to reveal.");

View File

@@ -606,7 +606,8 @@ public class HumanPlay {
} }
} }
else if (part instanceof CostReveal) { else if (part instanceof CostReveal) {
CardCollectionView list = CardLists.getValidCards(p.getCardsIn(ZoneType.Hand), part.getType(), p, source); CostReveal costReveal = (CostReveal) part;
CardCollectionView list = CardLists.getValidCards(p.getCardsIn(costReveal.getRevealFrom()), part.getType(), p, source);
int amount = getAmountFromPartX(part, source, sourceAbility); int amount = getAmountFromPartX(part, source, sourceAbility);
boolean hasPaid = payCostPart(controller, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblReveal") + orString); boolean hasPaid = payCostPart(controller, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblReveal") + orString);
if (!hasPaid) { return false; } if (!hasPaid) { return false; }