From 61a627a36a16f591752b24927180e50283747680 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sat, 8 Sep 2018 11:58:07 +0200 Subject: [PATCH 1/2] Surveil Effect --- .../java/forge/ai/PlayerControllerAi.java | 18 +++++++ .../src/main/java/forge/ai/SpellApiToAi.java | 1 + .../main/java/forge/ai/ability/ScryAi.java | 4 +- .../main/java/forge/ai/ability/SurveilAi.java | 44 ++++++++++++++++ .../src/main/java/forge/game/GameAction.java | 2 +- .../java/forge/game/GameLogFormatter.java | 20 ++++++- .../main/java/forge/game/ability/ApiType.java | 1 + .../game/ability/effects/ScryEffect.java | 2 +- .../game/ability/effects/SurveilEffect.java | 46 ++++++++++++++++ .../forge/game/event/GameEventSurveil.java | 21 ++++++++ .../forge/game/event/IGameEventVisitor.java | 2 + .../main/java/forge/game/player/Player.java | 52 +++++++++++++++---- .../forge/game/player/PlayerController.java | 2 + .../util/PlayerControllerForTests.java | 5 ++ .../forge/player/PlayerControllerHuman.java | 37 +++++++++++++ 15 files changed, 241 insertions(+), 16 deletions(-) create mode 100644 forge-ai/src/main/java/forge/ai/ability/SurveilAi.java create mode 100644 forge-game/src/main/java/forge/game/ability/effects/SurveilEffect.java create mode 100644 forge-game/src/main/java/forge/game/event/GameEventSurveil.java diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 7a21356b85b..23d12786ec5 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -292,8 +292,26 @@ public class PlayerControllerAi extends PlayerController { return ImmutablePair.of(toTop, toBottom); } + /* (non-Javadoc) + * @see forge.game.player.PlayerController#arrangeForSurveil(forge.game.card.CardCollection) + */ + @Override + public ImmutablePair arrangeForSurveil(CardCollection topN) { + CardCollection toGraveyard = new CardCollection(); + CardCollection toTop = new CardCollection(); + + // TODO add AI logic there, similar to Scry + + toTop.addAll(topN); + + Collections.shuffle(toTop, MyRandom.getRandom()); + return ImmutablePair.of(toTop, toGraveyard); + } + @Override public boolean willPutCardOnTop(Card c) { + // TODO add Logic there similar to Scry. this is used for Clash + return true; // AI does not know what will happen next (another clash or that would become his topdeck) } diff --git a/forge-ai/src/main/java/forge/ai/SpellApiToAi.java b/forge-ai/src/main/java/forge/ai/SpellApiToAi.java index ba1b33e24fe..d5b9473a39e 100644 --- a/forge-ai/src/main/java/forge/ai/SpellApiToAi.java +++ b/forge-ai/src/main/java/forge/ai/SpellApiToAi.java @@ -145,6 +145,7 @@ public enum SpellApiToAi { .put(ApiType.SkipTurn, SkipTurnAi.class) .put(ApiType.StoreMap, StoreMapAi.class) .put(ApiType.StoreSVar, StoreSVarAi.class) + .put(ApiType.Surveil, SurveilAi.class) .put(ApiType.Tap, TapAi.class) .put(ApiType.TapAll, TapAllAi.class) .put(ApiType.TapOrUntap, TapOrUntapAi.class) diff --git a/forge-ai/src/main/java/forge/ai/ability/ScryAi.java b/forge-ai/src/main/java/forge/ai/ability/ScryAi.java index a831818c7da..ea10f386b9e 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ScryAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ScryAi.java @@ -11,7 +11,6 @@ import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.player.PlayerActionConfirmMode; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; import forge.game.zone.ZoneType; import forge.util.MyRandom; @@ -22,9 +21,8 @@ public class ScryAi extends SpellAbilityAi { */ @Override protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { - final TargetRestrictions tgt = sa.getTargetRestrictions(); - if (tgt != null) { // It doesn't appear that Scry ever targets + if (sa.usesTargeting()) { // It doesn't appear that Scry ever targets // ability is targeted sa.resetTargets(); diff --git a/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java b/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java new file mode 100644 index 00000000000..47c64349396 --- /dev/null +++ b/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java @@ -0,0 +1,44 @@ +package forge.ai.ability; + +import forge.ai.SpellAbilityAi; +import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; +import forge.game.spellability.SpellAbility; + +public class SurveilAi extends SpellAbilityAi { + + /* + * (non-Javadoc) + * @see forge.ai.SpellAbilityAi#doTriggerAINoCost(forge.game.player.Player, forge.game.spellability.SpellAbility, boolean) + */ + @Override + protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { + + if (sa.usesTargeting()) { // It doesn't appear that Scry ever targets + // ability is targeted + sa.resetTargets(); + + sa.getTargets().add(ai); + } + + return true; + } + + /* + * (non-Javadoc) + * @see forge.ai.SpellAbilityAi#chkAIDrawback(forge.game.spellability.SpellAbility, forge.game.player.Player) + */ + @Override + public boolean chkAIDrawback(SpellAbility sa, Player ai) { + return doTriggerAINoCost(ai, sa, false); + } + + /* + * (non-Javadoc) + * @see forge.ai.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.game.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String) + */ + @Override + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + return true; + } +} diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 37a1a96a89e..2741c83f892 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -1848,7 +1848,7 @@ public class GameAction { //Vancouver Mulligan for(Player p : whoCanMulligan) { if (p.getStartingHandSize() > p.getZone(ZoneType.Hand).size()) { - p.scry(1); + p.scry(1, null); } } } diff --git a/forge-game/src/main/java/forge/game/GameLogFormatter.java b/forge-game/src/main/java/forge/game/GameLogFormatter.java index 9756a7b79b6..40a9ac87c8f 100644 --- a/forge-game/src/main/java/forge/game/GameLogFormatter.java +++ b/forge-game/src/main/java/forge/game/GameLogFormatter.java @@ -24,6 +24,7 @@ import forge.game.event.GameEventPlayerPoisoned; import forge.game.event.GameEventScry; import forge.game.event.GameEventSpellAbilityCast; import forge.game.event.GameEventSpellResolved; +import forge.game.event.GameEventSurveil; import forge.game.event.GameEventTurnBegan; import forge.game.event.GameEventTurnPhase; import forge.game.event.IGameEventVisitor; @@ -65,7 +66,24 @@ public class GameLogFormatter extends IGameEventVisitor.Base { return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, scryOutcome); } - + + @Override + public GameLogEntry visit(GameEventSurveil ev) { + String scryOutcome = ""; + String toLibrary = Lang.nounWithAmount(ev.toLibrary, "card") + " to the top of the library"; + String toGraveyard = Lang.nounWithAmount(ev.toGraveyard, "card") + " to the graveyard"; + + if (ev.toLibrary > 0 && ev.toGraveyard > 0) { + scryOutcome = ev.player.toString() + " surveil " + toLibrary + " and " + toGraveyard; + } else if (ev.toGraveyard == 0) { + scryOutcome = ev.player.toString() + " surveil " + toLibrary; + } else { + scryOutcome = ev.player.toString() + " surveil " + toGraveyard; + } + + return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, scryOutcome); + } + @Override public GameLogEntry visit(GameEventSpellResolved ev) { String messageForLog = ev.hasFizzled ? ev.spell.getHostCard().getName() + " ability fizzles." : ev.spell.getStackDescription(); diff --git a/forge-game/src/main/java/forge/game/ability/ApiType.java b/forge-game/src/main/java/forge/game/ability/ApiType.java index c4a76bc2f63..a5d9572fde2 100644 --- a/forge-game/src/main/java/forge/game/ability/ApiType.java +++ b/forge-game/src/main/java/forge/game/ability/ApiType.java @@ -144,6 +144,7 @@ public enum ApiType { SkipTurn (SkipTurnEffect.class), StoreSVar (StoreSVarEffect.class), StoreMap (StoreMapEffect.class), + Surveil (SurveilEffect.class), Tap (TapEffect.class), TapAll (TapAllEffect.class), TapOrUntap (TapOrUntapEffect.class), diff --git a/forge-game/src/main/java/forge/game/ability/effects/ScryEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ScryEffect.java index 423479c7782..2f5d606fbe3 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ScryEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ScryEffect.java @@ -45,7 +45,7 @@ public class ScryEffect extends SpellAbilityEffect { continue; } - p.scry(num); + p.scry(num, sa); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/SurveilEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SurveilEffect.java new file mode 100644 index 00000000000..740ce564b33 --- /dev/null +++ b/forge-game/src/main/java/forge/game/ability/effects/SurveilEffect.java @@ -0,0 +1,46 @@ +package forge.game.ability.effects; + +import forge.game.ability.AbilityUtils; +import forge.game.ability.SpellAbilityEffect; +import forge.game.player.Player; +import forge.game.spellability.SpellAbility; +import forge.util.Lang; + +public class SurveilEffect extends SpellAbilityEffect { + @Override + protected String getStackDescription(SpellAbility sa) { + final StringBuilder sb = new StringBuilder(); + + sb.append(Lang.joinHomogenous(getTargetPlayers(sa))); + + int num = 1; + if (sa.hasParam("Amount")) { + num = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa); + } + + sb.append(" surveil (").append(num).append(")."); + return sb.toString(); + } + + @Override + public void resolve(SpellAbility sa) { + int num = 1; + if (sa.hasParam("Amount")) { + num = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa); + } + + boolean isOptional = sa.hasParam("Optional"); + + for (final Player p : getTargetPlayers(sa)) { + if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { + if (isOptional && !p.getController().confirmAction(sa, null, "Do you want to surveil?")) { + continue; + } + + p.surveil(num, sa); + } + } + } + + +} diff --git a/forge-game/src/main/java/forge/game/event/GameEventSurveil.java b/forge-game/src/main/java/forge/game/event/GameEventSurveil.java new file mode 100644 index 00000000000..db003097bb1 --- /dev/null +++ b/forge-game/src/main/java/forge/game/event/GameEventSurveil.java @@ -0,0 +1,21 @@ +package forge.game.event; + +import forge.game.player.Player; + +public class GameEventSurveil extends GameEvent { + + public final Player player; + public final int toLibrary, toGraveyard; + + public GameEventSurveil(Player player, int toLibrary, int toGraveyard) { + this.player = player; + this.toLibrary = toLibrary; + this.toGraveyard = toGraveyard; + } + + @Override + public T visit(IGameEventVisitor visitor) { + return visitor.visit(this); + } +} + diff --git a/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java b/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java index 918f3fa0d0f..7e038506226 100644 --- a/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java +++ b/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java @@ -42,6 +42,7 @@ public interface IGameEventVisitor { T visit(GameEventSpellAbilityCast gameEventSpellAbilityCast); T visit(GameEventSpellResolved event); T visit(GameEventSpellRemovedFromStack event); + T visit(GameEventSurveil event); T visit(GameEventTokenCreated event); T visit(GameEventTurnBegan gameEventTurnBegan); T visit(GameEventTurnEnded event); @@ -87,6 +88,7 @@ public interface IGameEventVisitor { public T visit(GameEventSpellResolved event) { return null; } public T visit(GameEventSpellAbilityCast event) { return null; } public T visit(GameEventSpellRemovedFromStack event) { return null; } + public T visit(GameEventSurveil event) { return null; } public T visit(GameEventTokenCreated event) { return null; } public T visit(GameEventTurnBegan event) { return null; } public T visit(GameEventTurnEnded event) { return null; } diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 2e7ea3fdabe..fec5a9e27b2 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1248,15 +1248,11 @@ public class Player extends GameEntity implements Comparable { return drawCards(1); } - public void scry(final int numScry) { - final CardCollection topN = new CardCollection(); - final PlayerZone library = getZone(ZoneType.Library); - final int actualNumScry = Math.min(numScry, library.size()); + public void scry(final int numScry, SpellAbility cause) { + final CardCollection topN = new CardCollection(this.getCardsIn(ZoneType.Library, numScry)); - if (actualNumScry == 0) { return; } - - for (int i = 0; i < actualNumScry; i++) { - topN.add(library.get(i)); + if (topN.isEmpty()) { + return; } final ImmutablePair lists = getController().arrangeForScry(topN); @@ -1268,7 +1264,7 @@ public class Player extends GameEntity implements Comparable { if (toBottom != null) { for(Card c : toBottom) { - getGame().getAction().moveToBottomOfLibrary(c, null, null); + getGame().getAction().moveToBottomOfLibrary(c, cause, null); numToBottom++; } } @@ -1276,7 +1272,7 @@ public class Player extends GameEntity implements Comparable { if (toTop != null) { Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus. for(Card c : toTop) { - getGame().getAction().moveToLibrary(c, null, null); + getGame().getAction().moveToLibrary(c, cause, null); numToTop++; } } @@ -1288,6 +1284,42 @@ public class Player extends GameEntity implements Comparable { getGame().getTriggerHandler().runTrigger(TriggerType.Scry, runParams, false); } + public void surveil(final int num, SpellAbility cause) { + final CardCollection topN = new CardCollection(this.getCardsIn(ZoneType.Library, num)); + + if (topN.isEmpty()) { + return; + } + + final ImmutablePair lists = getController().arrangeForSurveil(topN); + final CardCollection toTop = lists.getLeft(); + final CardCollection toGrave = lists.getRight(); + + int numToGrave = 0; + int numToTop = 0; + + if (toGrave != null) { + for(Card c : toGrave) { + getGame().getAction().moveToGraveyard(c, cause, null); + numToGrave++; + } + } + + if (toTop != null) { + Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus. + for(Card c : toTop) { + getGame().getAction().moveToLibrary(c, cause, null); + numToTop++; + } + } + + getGame().fireEvent(new GameEventSurveil(this, numToTop, numToGrave)); + + //final Map runParams = Maps.newHashMap(); + //runParams.put("Player", this); + //getGame().getTriggerHandler().runTrigger(TriggerType.Scry, runParams, false); + } + public boolean canMulligan() { return !getZone(ZoneType.Hand).isEmpty(); } diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index 3d02dcea56d..adc6bd77754 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -140,6 +140,8 @@ public abstract class PlayerController { /** Shows message to player to reveal chosen cardName, creatureType, number etc. AI must analyze API to understand what that is */ public abstract void notifyOfValue(SpellAbility saSource, GameObject realtedTarget, String value); public abstract ImmutablePair arrangeForScry(CardCollection topN); + public abstract ImmutablePair arrangeForSurveil(CardCollection topN); + public abstract boolean willPutCardOnTop(Card c); public final CardCollectionView orderMoveToZoneList(CardCollectionView cards, ZoneType destinationZone) { return orderMoveToZoneList(cards, destinationZone, null); diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index 0994bcc54f1..4b0afe447b1 100644 --- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -246,6 +246,11 @@ public class PlayerControllerForTests extends PlayerController { return ImmutablePair.of(topN, null); } + @Override + public ImmutablePair arrangeForSurveil(CardCollection topN) { + return ImmutablePair.of(topN, null); + } + @Override public boolean willPutCardOnTop(Card c) { return false; diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 49b32d26133..c86b17dc87f 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -769,6 +769,43 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont return ImmutablePair.of(toTop, toBottom); } + @Override + public ImmutablePair arrangeForSurveil(final CardCollection topN) { + CardCollection toGrave = null; + CardCollection toTop = null; + + tempShowCards(topN); + if (topN.size() == 1) { + final Card c = topN.getFirst(); + final CardView view = CardView.get(c); + + tempShowCard(c); + getGui().setCard(view); + boolean result = false; + result = InputConfirm.confirm(this, view, TextUtil.concatNoSpace("Put ", view.toString(), " on the top of library or graveyard?"), + true, ImmutableList.of("Library", "Graveyard")); + if (result) { + toTop = topN; + } else { + toGrave = topN; + } + } else { + toGrave = game.getCardList(getGui().many("Select cards to be put into the graveyard", + "Cards to put in the graveyard", -1, CardView.getCollection(topN), null)); + topN.removeAll((Collection) toGrave); + if (topN.isEmpty()) { + toTop = null; + } else if (topN.size() == 1) { + toTop = topN; + } else { + toTop = game.getCardList(getGui().order("Arrange cards to be put on top of your library", + "Top of Library", CardView.getCollection(topN), null)); + } + } + endTempShowCards(); + return ImmutablePair.of(toTop, toGrave); + } + @Override public boolean willPutCardOnTop(final Card c) { final CardView view = CardView.get(c); From d8f5b870a7e05bb7a0a1d0c6ed74266a60b9cd31 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sat, 8 Sep 2018 11:59:22 +0200 Subject: [PATCH 2/2] cards: update for surveil --- forge-gui/res/cardsfolder/upcoming/deadly_visit.txt | 4 ++-- forge-gui/res/cardsfolder/upcoming/sinister_sabotage.txt | 4 ++-- forge-gui/res/cardsfolder/upcoming/thought_erasure.txt | 4 ++-- .../res/cardsfolder/upcoming/unexplained_disappearance.txt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/forge-gui/res/cardsfolder/upcoming/deadly_visit.txt b/forge-gui/res/cardsfolder/upcoming/deadly_visit.txt index dfe3c9c4c9a..26792d9be3a 100644 --- a/forge-gui/res/cardsfolder/upcoming/deadly_visit.txt +++ b/forge-gui/res/cardsfolder/upcoming/deadly_visit.txt @@ -1,6 +1,6 @@ Name:Deadly Visit ManaCost:3 B B Types:Sorcery -A:SP$ Destroy | Cost$ 3 B B | ValidTgts$ Creature | TgtPrompt$ Select target creature | Subability$ DBSurveil | SpellDescription$ Destroy target creature. -SVar:DBSurveil:DB$ Surveil | SurveilNum$ 2 +A:SP$ Destroy | Cost$ 3 B B | ValidTgts$ Creature | TgtPrompt$ Select target creature | Subability$ DBSurveil | SpellDescription$ Destroy target creature. Surveil 2 (Look at the top two cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.) +SVar:DBSurveil:DB$ Surveil | Amount$ 2 Oracle:Destroy target creature.\nSurveil 2. (Look at the top two cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.) diff --git a/forge-gui/res/cardsfolder/upcoming/sinister_sabotage.txt b/forge-gui/res/cardsfolder/upcoming/sinister_sabotage.txt index 3d0148dc975..5dcbd3c772a 100644 --- a/forge-gui/res/cardsfolder/upcoming/sinister_sabotage.txt +++ b/forge-gui/res/cardsfolder/upcoming/sinister_sabotage.txt @@ -1,6 +1,6 @@ Name:Sinister Sabotage ManaCost:1 U U Types:Instant -A:SP$ Counter | Cost$ 1 U U | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | Subability$ DBSurveil | SpellDescription$ Counter target spell.1 (Look at the top card of your library. You may put that card into your graveyard.) -SVar:DBSurveil:DB$ Surveil | SurveilNum$ 1 +A:SP$ Counter | Cost$ 1 U U | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | Subability$ DBSurveil | SpellDescription$ Counter target spell. Surveil 1 (Look at the top card of your library. You may put that card into your graveyard.) +SVar:DBSurveil:DB$ Surveil | Amount$ 1 Oracle:Counter target spell.\nSurveil 1. (Look at the top card of your library. You may put that card into your graveyard.) diff --git a/forge-gui/res/cardsfolder/upcoming/thought_erasure.txt b/forge-gui/res/cardsfolder/upcoming/thought_erasure.txt index a46dc44c604..02aab8b1c92 100644 --- a/forge-gui/res/cardsfolder/upcoming/thought_erasure.txt +++ b/forge-gui/res/cardsfolder/upcoming/thought_erasure.txt @@ -1,6 +1,6 @@ Name:Thought Erasure ManaCost:U B Types:Sorcery -A:SP$ Discard | Cost$ U B | ValidTgts$ Opponent | DiscardValid$ Card.nonLand | NumCards$ 1 | Mode$ RevealYouChoose | Subability$ DBSurveil | SpellDescription$ Target opponent reveals their hand. You choose a nonland card from it. That player discards that card. -SVar:DBSurveil:DB$ Surveil | SurveilNum$ 1 +A:SP$ Discard | Cost$ U B | ValidTgts$ Opponent | DiscardValid$ Card.nonLand | NumCards$ 1 | Mode$ RevealYouChoose | Subability$ DBSurveil | SpellDescription$ Target opponent reveals their hand. You choose a nonland card from it. That player discards that card. Surveil 1 (Look at the top card of your library. You may put that card into your graveyard.) +SVar:DBSurveil:DB$ Surveil | Amount$ 1 Oracle:Target opponent reveals their hand. You choose a nonland card from it. That player discards that card.\nSurveil 1. (Look at the top card of your library. You may put it into your graveyard.) diff --git a/forge-gui/res/cardsfolder/upcoming/unexplained_disappearance.txt b/forge-gui/res/cardsfolder/upcoming/unexplained_disappearance.txt index c2162ec9303..5f90caabb59 100644 --- a/forge-gui/res/cardsfolder/upcoming/unexplained_disappearance.txt +++ b/forge-gui/res/cardsfolder/upcoming/unexplained_disappearance.txt @@ -1,6 +1,6 @@ Name:Unexplained Disappearance ManaCost:1 U Types:Instant -A:SP$ ChangeZone | Cost$ 1 U | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Hand | Subability$ DBSurveil | SpellDescription$ Return target creature to its owner's hand. -SVar:DBSurveil:DB$ Surveil | SurveilNum$ 1 +A:SP$ ChangeZone | Cost$ 1 U | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Hand | Subability$ DBSurveil | SpellDescription$ Return target creature to its owner's hand. Surveil 1 (Look at the top card of your library. You may put that card into your graveyard.) +SVar:DBSurveil:DB$ Surveil | Amount$ 1 Oracle:Return target creature to its owner's hand.\nSurveil 1. (Look at the top card of your library. You may put that card into your graveyard.)