diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 878bc7c1c65..5b183d5f182 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -32,6 +32,7 @@ import com.google.common.collect.Lists; import forge.ai.ability.ChangeZoneAi; import forge.ai.ability.ExploreAi; +import forge.ai.ability.LearnAi; import forge.ai.simulation.SpellAbilityPicker; import forge.card.MagicColor; import forge.card.mana.ManaCost; @@ -2095,8 +2096,11 @@ public class AiController { if (useSimulation) { return simPicker.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player2, decider); } + if (sa.getApi() == ApiType.Explore) { return ExploreAi.shouldPutInGraveyard(fetchList, decider); + } else if (sa.getApi() == ApiType.Learn) { + return LearnAi.chooseCardToLearn(fetchList, decider, sa); } else { return ChangeZoneAi.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player2, decider); } diff --git a/forge-ai/src/main/java/forge/ai/SpellApiToAi.java b/forge-ai/src/main/java/forge/ai/SpellApiToAi.java index 7f6c3b81469..c95593f5c71 100644 --- a/forge-ai/src/main/java/forge/ai/SpellApiToAi.java +++ b/forge-ai/src/main/java/forge/ai/SpellApiToAi.java @@ -95,6 +95,7 @@ public enum SpellApiToAi { .put(ApiType.Haunt, HauntAi.class) .put(ApiType.ImmediateTrigger, ImmediateTriggerAi.class) .put(ApiType.Investigate, InvestigateAi.class) + .put(ApiType.Learn, LearnAi.class) .put(ApiType.LoseLife, LifeLoseAi.class) .put(ApiType.LosesGame, GameLossAi.class) .put(ApiType.Mana, ManaEffectAi.class) diff --git a/forge-ai/src/main/java/forge/ai/ability/LearnAi.java b/forge-ai/src/main/java/forge/ai/ability/LearnAi.java new file mode 100644 index 00000000000..a8b87b8f399 --- /dev/null +++ b/forge-ai/src/main/java/forge/ai/ability/LearnAi.java @@ -0,0 +1,56 @@ +package forge.ai.ability; + + +import forge.ai.ComputerUtilCard; +import forge.ai.PlayerControllerAi; +import forge.ai.SpellAbilityAi; +import forge.game.card.Card; +import forge.game.card.CardCollection; +import forge.game.card.CardLists; +import forge.game.card.CardPredicates; +import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; +import forge.game.spellability.SpellAbility; +import forge.game.zone.ZoneType; + +public class LearnAi extends SpellAbilityAi { + @Override + protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) { + // For the time being, Learn is treated as universally positive due to being optional + + return true; + } + + @Override + protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) { + return mandatory || canPlayAI(aiPlayer, sa); + } + + @Override + public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) { + return canPlayAI(aiPlayer, sa); + } + + @Override + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + return true; + } + + public static Card chooseCardToLearn(CardCollection options, Player ai, SpellAbility sa) { + CardCollection sideboard = CardLists.filter(options, CardPredicates.inZone(ZoneType.Sideboard)); + CardCollection hand = CardLists.filter(options, CardPredicates.inZone(ZoneType.Hand)); + hand.remove(sa.getHostCard()); // this card will be used in the process, don't consider it for discard + + CardCollection lessons = CardLists.filter(sideboard, CardPredicates.isType("Lesson")); + CardCollection goodDiscards = ((PlayerControllerAi)ai.getController()).getAi().getCardsToDiscard(1, 1, hand, sa); + + if (!lessons.isEmpty()) { + return ComputerUtilCard.getBestAI(lessons); + } else if (!goodDiscards.isEmpty()) { + return ComputerUtilCard.getWorstAI(goodDiscards); + } + + // Don't choose anything if there's no good option + return null; + } +} diff --git a/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java b/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java index 913e42a45ce..06340311dda 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java +++ b/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java @@ -11,6 +11,7 @@ import forge.ai.ComputerUtilAbility; import forge.ai.ComputerUtilCost; import forge.ai.ability.ChangeZoneAi; import forge.ai.ability.ExploreAi; +import forge.ai.ability.LearnAi; import forge.ai.simulation.GameStateEvaluator.Score; import forge.game.Game; import forge.game.ability.ApiType; @@ -423,6 +424,8 @@ public class SpellAbilityPicker { } if (sa.getApi() == ApiType.Explore) { return ExploreAi.shouldPutInGraveyard(fetchList, decider); + } else if (sa.getApi() == ApiType.Learn) { + return LearnAi.chooseCardToLearn(fetchList, decider, sa); } else { return ChangeZoneAi.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player2, decider); } diff --git a/forge-game/src/main/java/forge/game/GameFormat.java b/forge-game/src/main/java/forge/game/GameFormat.java index 5af6fbf2c59..dc6e41eec8f 100644 --- a/forge-game/src/main/java/forge/game/GameFormat.java +++ b/forge-game/src/main/java/forge/game/GameFormat.java @@ -134,7 +134,7 @@ public class GameFormat implements Comparable { if (!this.allowedSetCodes_ro.isEmpty()) { p = Predicates.and(p, printed ? IPaperCard.Predicates.printedInSets(this.allowedSetCodes_ro, printed) : - (Predicate)(StaticData.instance().getCommonCards().wasPrintedInSets(this.allowedSetCodes_ro))); + StaticData.instance().getCommonCards().wasPrintedInSets(this.allowedSetCodes_ro)); } if (!this.allowedRarities.isEmpty()) { List> crp = Lists.newArrayList(); 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 56aef8eab84..e540e28f6b1 100644 --- a/forge-game/src/main/java/forge/game/ability/ApiType.java +++ b/forge-game/src/main/java/forge/game/ability/ApiType.java @@ -93,6 +93,7 @@ public enum ApiType { Haunt (HauntEffect.class), Investigate (InvestigateEffect.class), ImmediateTrigger (ImmediateTriggerEffect.class), + Learn (LearnEffect.class), LookAt (LookAtEffect.class), LoseLife (LifeLoseEffect.class), LosesGame (GameLossEffect.class), diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index 31140d7ed5b..4dce18f4695 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -4,6 +4,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import com.google.common.base.Predicate; @@ -942,6 +943,12 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } } + // for Wish cards, if the player is controlled by someone else + // they can't fetch from the outside the game/sideboard + if (player.isControlled()) { + origin.remove(ZoneType.Sideboard); + } + CardCollection fetchList; boolean shuffleMandatory = true; boolean searchedLibrary = false; @@ -1179,6 +1186,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } } + boolean combatChanged = false; + final CardZoneTable triggerList = new CardZoneTable(); + for (Player player : HiddenOriginChoicesMap.keySet()) { boolean searchedLibrary = HiddenOriginChoicesMap.get(player).searchedLibrary; boolean shuffleMandatory = HiddenOriginChoicesMap.get(player).shuffleMandatory; @@ -1188,12 +1198,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { ZoneType destination = HiddenOriginChoicesMap.get(player).destination; CardCollection movedCards = new CardCollection(); long ts = game.getNextTimestamp(); - final CardZoneTable triggerList = new CardZoneTable(); - boolean combatChanged = false; - Player decider = chooser; - if (decider == null) { - decider = player; - } + Player decider = ObjectUtils.firstNonNull(chooser, player); for (final Card c : chosenCards) { Card movedCard = null; @@ -1423,15 +1428,15 @@ public class ChangeZoneEffect extends SpellAbilityEffect { registerDelayedTrigger(sa, sa.getParam("AtEOT"), movedCards); } - if (combatChanged) { - game.updateCombatForView(); - game.fireEvent(new GameEventCombatChanged()); - } - triggerList.triggerChangesZoneAll(game); + } + if (combatChanged) { + game.updateCombatForView(); + game.fireEvent(new GameEventCombatChanged()); + } + triggerList.triggerChangesZoneAll(game); - if (sa.hasParam("UntilHostLeavesPlay")) { - source.addLeavesPlayCommand(untilHostLeavesPlayCommand(triggerList, source)); - } + if (sa.hasParam("UntilHostLeavesPlay")) { + source.addLeavesPlayCommand(untilHostLeavesPlayCommand(triggerList, source)); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/LearnEffect.java b/forge-game/src/main/java/forge/game/ability/effects/LearnEffect.java new file mode 100644 index 00000000000..4fb7b2039b0 --- /dev/null +++ b/forge-game/src/main/java/forge/game/ability/effects/LearnEffect.java @@ -0,0 +1,27 @@ +package forge.game.ability.effects; + +import forge.game.Game; +import forge.game.ability.SpellAbilityEffect; +import forge.game.card.Card; +import forge.game.card.CardZoneTable; +import forge.game.player.Player; +import forge.game.spellability.SpellAbility; + +public class LearnEffect extends SpellAbilityEffect { + + @Override + protected String getStackDescription(SpellAbility sa) { + return "Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.)"; + } + @Override + public void resolve(SpellAbility sa) { + final Card source = sa.getHostCard(); + final Game game = source.getGame(); + CardZoneTable table = new CardZoneTable(); + for (Player p : getTargetPlayers(sa)) { + p.learnLesson(sa, table); + } + table.triggerChangesZoneAll(game); + } + +} 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 ae0bb4295c0..43f96afa75d 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -2667,6 +2667,11 @@ public class Player extends GameEntity implements Comparable { return null; } + public final boolean isControlled() { + Player ctrlPlayer = this.getControllingPlayer(); + return ctrlPlayer != null && ctrlPlayer != this; + } + public void addController(long timestamp, Player pl) { final IGameEntitiesFactory master = (IGameEntitiesFactory)pl.getLobbyPlayer(); addController(timestamp, pl, master.createMindSlaveController(pl, this), true); @@ -3604,4 +3609,49 @@ public class Player extends GameEntity implements Comparable { game.getAction().revealTo(revealCards, otherPlayers, Localizer.getInstance().getMessage("lblRevealFaceDownCards")); } } + + public void learnLesson(SpellAbility sa, CardZoneTable table) { + // Replacement effects + Map repParams = AbilityKey.mapFromAffected(this); + repParams.put(AbilityKey.Cause, sa); + if (game.getReplacementHandler().run(ReplacementType.Learn, repParams) != ReplacementResult.NotReplaced) { + return; + } + + CardCollection list = new CardCollection(); + if (!isControlled()) { + list.addAll(CardLists.getType(getZone(ZoneType.Sideboard), "Lesson")); + } + list.addAll(getZone(ZoneType.Hand)); + if (list.isEmpty()) { + return; + } + + Card c = getController().chooseSingleCardForZoneChange(ZoneType.Hand, ImmutableList.of(ZoneType.Sideboard, ZoneType.Hand), + sa, list, null, "Learn a Lesson", true, this); + if (c == null) { + return; + } + if (c.isInZone(ZoneType.Sideboard)) { // Sideboard Lesson to Hand + Card moved = game.getAction().moveTo(ZoneType.Hand, c, sa); + table.put(ZoneType.Sideboard, ZoneType.Hand, moved); + } else if (c.isInZone(ZoneType.Hand)) { // Discard and Draw + boolean firstDiscard = getNumDiscardedThisTurn() == 0; + if (discard(c, sa, table) != null) { + // Change this if something would make multiple player learn at the same time + + // Discard Trigger outside Effect + final Map runParams = AbilityKey.newMap(); + runParams.put(AbilityKey.Player, this); + runParams.put(AbilityKey.Cards, new CardCollection(c)); + runParams.put(AbilityKey.Cause, sa); + runParams.put(AbilityKey.FirstTime, firstDiscard); + getGame().getTriggerHandler().runTrigger(TriggerType.DiscardedAll, runParams, false); + + for (Card d : drawCards(1, sa)) { + table.put(ZoneType.Library, ZoneType.Hand, d); // does a ChangesZoneAll care about moving from Library to Hand + } + } + } + } } diff --git a/forge-game/src/main/java/forge/game/player/PlayerView.java b/forge-game/src/main/java/forge/game/player/PlayerView.java index b55dc7d5954..6dcaa8faf70 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerView.java +++ b/forge-game/src/main/java/forge/game/player/PlayerView.java @@ -485,6 +485,8 @@ public class PlayerView extends GameEntityView { return TrackableProperty.Library; case Flashback: return TrackableProperty.Flashback; + case Sideboard: + return TrackableProperty.Sideboard; default: return null; //other zones not represented } diff --git a/forge-game/src/main/java/forge/game/replacement/ReplaceLearn.java b/forge-game/src/main/java/forge/game/replacement/ReplaceLearn.java new file mode 100644 index 00000000000..95e7e3473ff --- /dev/null +++ b/forge-game/src/main/java/forge/game/replacement/ReplaceLearn.java @@ -0,0 +1,52 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.game.replacement; + +import java.util.Map; + +import forge.game.ability.AbilityKey; +import forge.game.card.Card; +import forge.game.spellability.SpellAbility; + +public class ReplaceLearn extends ReplacementEffect { + + public ReplaceLearn(Map map, Card host, boolean intrinsic) { + super(map, host, intrinsic); + } + + /* (non-Javadoc) + * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) + */ + @Override + public boolean canReplace(Map runParams) { + if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Affected))) { + return false; + } + + return true; + } + + /* (non-Javadoc) + * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) + */ + @Override + public void setReplacingObjects(Map runParams, SpellAbility sa) { + sa.setReplacingObject(AbilityKey.Player, runParams.get(AbilityKey.Affected)); + } + +} diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementType.java b/forge-game/src/main/java/forge/game/replacement/ReplacementType.java index abc4c0605ec..335564f6c62 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementType.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementType.java @@ -28,6 +28,7 @@ public enum ReplacementType { DrawCards(ReplaceDrawCards.class), GainLife(ReplaceGainLife.class), GameLoss(ReplaceGameLoss.class), + Learn(ReplaceLearn.class), Mill(ReplaceMill.class), Moved(ReplaceMoved.class), ProduceMana(ReplaceProduceMana.class), diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index a6a8e0096e3..8b4dce81525 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -171,6 +171,7 @@ public enum TrackableProperty { Graveyard(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze), Hand(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze), Library(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze), + Sideboard(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze), Mana(TrackableTypes.ManaMapType, FreezeMode.IgnoresFreeze), IsExtraTurn(TrackableTypes.BooleanType), ExtraTurnCount(TrackableTypes.IntegerType), diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java b/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java index 305912eb4f1..01887035ddb 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java @@ -484,6 +484,7 @@ public final class CMatchUI case Exile: case Flashback: case Command: + case Sideboard: if (FloatingZone.show(this,player,zone)) { updatedPlayerZones.add(update); } diff --git a/forge-gui/res/cardsfolder/upcoming/academic_dispute.txt b/forge-gui/res/cardsfolder/upcoming/academic_dispute.txt new file mode 100644 index 00000000000..058fe529a74 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/academic_dispute.txt @@ -0,0 +1,7 @@ +Name:Academic Dispute +ManaCost:R +Types:Instant +A:SP$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ HIDDEN CARDNAME blocks each combat if able. | SubAbility$ DBPump | StackDescription$ SpellDescription | SpellDescription$ Target creature blocks this turn if able. +SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Reach | OptionQuestion$ Do you want TARGETS to gain reach? | SubAbility$ DBLearn | StackDescription$ SpellDescription | SpellDescription$ You may have it gain reach until end of turn. +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +Oracle:Target creature blocks this turn if able. You may have it gain reach until end of turn.\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/arcane_subtraction.txt b/forge-gui/res/cardsfolder/upcoming/arcane_subtraction.txt new file mode 100644 index 00000000000..8ef6be8d5f0 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/arcane_subtraction.txt @@ -0,0 +1,6 @@ +Name:Arcane Subtraction +ManaCost:1 U +Types:Instant +A:SP$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ -4 | IsCurse$ True | SubAbility$ DBLearn | SpellDescription$ Target creature gets -4/-0 until end of turn. +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +Oracle:Target creature gets -4/-0 until end of turn.\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/cram_session.txt b/forge-gui/res/cardsfolder/upcoming/cram_session.txt new file mode 100644 index 00000000000..bbbd82aa967 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/cram_session.txt @@ -0,0 +1,7 @@ +Name:Cram Session +ManaCost:1 BG +Types:Sorcery +A:SP$ GainLife | LifeAmount$ 4 | Defined$ You | SubAbility$ DBLearn | SpellDescription$ You gain 4 life. +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +DeckHas:Ability$LifeGain +Oracle:You gain 4 life.\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/divide_by_zero.txt b/forge-gui/res/cardsfolder/upcoming/divide_by_zero.txt new file mode 100644 index 00000000000..8cabcd3fd82 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/divide_by_zero.txt @@ -0,0 +1,6 @@ +Name:Divide by Zero +ManaCost:2 U +Types:Instant +A:SP$ ChangeZone | ValidTgts$ Permanent.cmcGE1,Card.inZoneStack+cmcGE1 | TgtZone$ Stack,Battlefield | Origin$ Battlefield,Stack | Fizzle$ True | Destination$ Hand | SubAbility$ DBLearn | SpellDescription$ Return target spell or permanent with mana value 1 or greater to its owner’s hand. +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +Oracle:Return target spell or permanent with mana value 1 or greater to its owner’s hand.\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/dream_strix.txt b/forge-gui/res/cardsfolder/upcoming/dream_strix.txt new file mode 100644 index 00000000000..878d4627b08 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/dream_strix.txt @@ -0,0 +1,11 @@ +Name:Dream Strix +ManaCost:2 U +Types:Creature Bird Ilusion +PT:2/2 +K:Flying +T:Mode$ BecomesTarget | ValidTarget$ Card.Self | SourceType$ Spell | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ When CARDNAME becomes the target of a spell, sacrifice it. +SVar:TrigSac:DB$ Sacrifice | ValidCard$ Card.Self +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigLearn | TriggerDescription$ When CARDNAME dies, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +SVar:TrigLearn:DB$ Learn +DeckHas:Ability$Sacrifice +Oracle:Flying\nWhen Dream Strix becomes the target of a spell, sacrifice it.\nWhen Dream Strix dies, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/enthusiastic_study.txt b/forge-gui/res/cardsfolder/upcoming/enthusiastic_study.txt new file mode 100644 index 00000000000..be7b4efe9cb --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/enthusiastic_study.txt @@ -0,0 +1,6 @@ +Name:Enthusiastic Study +ManaCost:2 R +Types:Instant +A:SP$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +3 | NumDef$ +1 | KW$ Trample | SubAbility$ DBLearn | SpellDescription$ Target creature gets +3/+1 and gains trample until end of turn. +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +Oracle:Target creature gets +3/+1 and gains trample until end of turn.\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/eyetwitch.txt b/forge-gui/res/cardsfolder/upcoming/eyetwitch.txt new file mode 100644 index 00000000000..b7c2a185903 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/eyetwitch.txt @@ -0,0 +1,9 @@ +Name:Eyetwitch +ManaCost:B +Types:Creature Eye Bat +PT:1/1 +K:Flying +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigLearn | TriggerDescription$ When CARDNAME dies, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +SVar:TrigLearn:DB$ Learn +SVar:SacMe:1 +Oracle:Flying\nWhen Eyetwitch dies, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/field_trip.txt b/forge-gui/res/cardsfolder/upcoming/field_trip.txt new file mode 100644 index 00000000000..7ae182c7ef5 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/field_trip.txt @@ -0,0 +1,6 @@ +Name:Field Trip +ManaCost:2 G +Types:Sorcery +A:SP$ ChangeZone | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Forest+Basic | ChangeNum$ 1 | SubAbility$ DBLearn | SpellDescription$ Search your library for a basic Forest card, put that card onto the battlefield tapped, then shuffle. +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +Oracle:Search your library for a basic Forest card, put that card onto the battlefield tapped, then shuffle.\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/first_day_of_class.txt b/forge-gui/res/cardsfolder/upcoming/first_day_of_class.txt new file mode 100644 index 00000000000..747f5b1bb00 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/first_day_of_class.txt @@ -0,0 +1,10 @@ +Name:First Day of Class +ManaCost:1 R +Types:Instant +A:SP$ Effect | Triggers$ CreatureETB | SubAbility$ DBLearn | SpellDescription$ Whenever a creature enters the battlefield under your control this turn, put a +1/+1 counter on it and it gains haste until end of turn. +SVar:CreatureETB:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.YouCtrl | TriggerZones$ Command | Execute$ TrigPutCounter | TriggerDescription$ Whenever a creature enters the battlefield under your control this turn, put a +1/+1 counter on it and it gains haste until end of turn. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredCardLKICopy | CounterType$ P1P1 | SubAbility$ DBPump +SVar:DBPump:DB$ Pump | Defined$ TriggeredCard | KW$ Haste +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +DeckHas:Ability$Counters +Oracle:Whenever a creature enters the battlefield under your control this turn, put a +1/+1 counter on it and it gains haste until end of turn.\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/gnarled_professor.txt b/forge-gui/res/cardsfolder/upcoming/gnarled_professor.txt new file mode 100644 index 00000000000..e3629801525 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/gnarled_professor.txt @@ -0,0 +1,8 @@ +Name:Gnarled Professor +ManaCost:2 G G +Types:Creature Treefolk Druid +PT:5/4 +K:Trample +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigLearn | TriggerDescription$ When CARDNAME enters the battlefield, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +SVar:TrigLearn:DB$ Learn +Oracle:Trample\nWhen Gnarled Professor enters the battlefield, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/guiding_voice.txt b/forge-gui/res/cardsfolder/upcoming/guiding_voice.txt new file mode 100644 index 00000000000..e54677e7ac0 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/guiding_voice.txt @@ -0,0 +1,7 @@ +Name:Guiding Voice +ManaCost:W +Types:Sorcery +A:SP$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBLearn | SpellDescription$ Put a +1/+1 counter on target creature. +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +DeckHas:Ability$Counters +Oracle:Put a +1/+1 counter on target creature.\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/hunt_for_specimens.txt b/forge-gui/res/cardsfolder/upcoming/hunt_for_specimens.txt new file mode 100644 index 00000000000..e7be60135f9 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/hunt_for_specimens.txt @@ -0,0 +1,7 @@ +Name:Hunt for Specimens +ManaCost:1 B +Types:Sorcery +A:SP$ Token | TokenScript$ bg_1_1_pest_lifegain | SubAbility$ DBLearn | SpellDescription$ Create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life." +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +DeckHas:Ability$Token & Ability$LifeGain +Oracle:Create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/igneous_inspiration.txt b/forge-gui/res/cardsfolder/upcoming/igneous_inspiration.txt new file mode 100644 index 00000000000..61917c5c354 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/igneous_inspiration.txt @@ -0,0 +1,6 @@ +Name:Igneous Inspiration +ManaCost:2 R +Types:Sorcery +A:SP$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 3 | SubAbility$ DBLearn | SpellDescription$ CARDNAME deals 3 damage to any target. +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +Oracle:Igneous Inspiration deals 3 damage to any target.\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/overgrown_arch.txt b/forge-gui/res/cardsfolder/upcoming/overgrown_arch.txt new file mode 100644 index 00000000000..c22c8b93651 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/overgrown_arch.txt @@ -0,0 +1,9 @@ +Name:Overgrown Arch +ManaCost:1 G +Types:Creature Plant Wall +PT:0/4 +K:Defender +A:AB$ GainLife | Cost$ T | LifeAmount$ 1 | SpellDescription$ You gain 1 life. +A:AB$ Learn | Cost$ 2 Sac<1/CARDNAME> | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +DeckHas:Ability$LifeGain & Ability$Sacrifice +Oracle:Defender\n{T}: You gain 1 life.\n{2}, Sacrifice Overgrown Arch: Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/poets_quill.txt b/forge-gui/res/cardsfolder/upcoming/poets_quill.txt new file mode 100644 index 00000000000..2a7625fb525 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/poets_quill.txt @@ -0,0 +1,8 @@ +Name:Poet's Quill +ManaCost:1 B +Types:Artifact — Equipment +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigLearn | TriggerDescription$ When CARDNAME enters the battlefield, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +SVar:TrigLearn:DB$ Learn +S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 1 | AddToughness$ 1 | AddKeyword$ Lifelink | Description$ Equipped creature gets +1/+1 and has lifelink. +K:Equip:1 B +Oracle:When Poet's Quill enters the battlefield, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.)\nEquipped creature gets +1/+1 and has lifelink.\nEquip {1}{B} diff --git a/forge-gui/res/cardsfolder/upcoming/pop_quiz.txt b/forge-gui/res/cardsfolder/upcoming/pop_quiz.txt new file mode 100644 index 00000000000..1f74fed6e50 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/pop_quiz.txt @@ -0,0 +1,6 @@ +Name:Pop Quiz +ManaCost:2 U +Types:Instant +A:SP$ Draw | Cost$ 2 U | Defined$ You | NumCards$ 1 | SubAbility$ DBLearn | SpellDescription$ Draw a card. +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +Oracle:Draw a card.\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/professor_of_symbology.txt b/forge-gui/res/cardsfolder/upcoming/professor_of_symbology.txt new file mode 100644 index 00000000000..aaaa1b2ba4c --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/professor_of_symbology.txt @@ -0,0 +1,7 @@ +Name:Professor of Symbology +ManaCost:1 W +Types:Creature Kor Cleric +PT:2/1 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigLearn | TriggerDescription$ When CARDNAME enters the battlefield, Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +SVar:TrigLearn:DB$ Learn +Oracle:When Professor of Symbology enters the battlefield, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/retriever_phoenix.txt b/forge-gui/res/cardsfolder/upcoming/retriever_phoenix.txt new file mode 100644 index 00000000000..fbd3cc86afc --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/retriever_phoenix.txt @@ -0,0 +1,11 @@ +Name:Retriever Phoenix +ManaCost:3 R +Types:Creature Phoenix +PT:2/2 +K:Flying +K:Haste +T:Mode$ ChangesZone | ValidCard$ Card.wasCast+Self | Destination$ Battlefield | Execute$ TrigLearn | TriggerDescription$ When CARDNAME enters the battlefield, if you cast it, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +R:Event$ Learn | ActiveZones$ Graveyard | ValidPlayer$ You | Optional$ True | ReplaceWith$ Return | IsPresent$ Card.Self | Description$ As long as CARDNAME is in your graveyard, if you would learn, you may instead return CARDNAME to the battlefield. +SVar:Return:DB$ ChangeZone | Defined$ Self | Origin$ Graveyard | Destination$ Battlefield +DeckHas:Ability$Graveyard +Oracle:Flying, haste\nWhen Retriever Phoenix enters the battlefield, if you cast it, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.)\nAs long as Retriever Phoenix is in your graveyard, if you would learn, you may instead return Retriever Phoenix to the battlefield. diff --git a/forge-gui/res/cardsfolder/upcoming/rise_of_extus.txt b/forge-gui/res/cardsfolder/upcoming/rise_of_extus.txt new file mode 100644 index 00000000000..97569d38772 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/rise_of_extus.txt @@ -0,0 +1,7 @@ +Name:Rise of Extus +ManaCost:4 W/B W/B +Types:Sorcery +A:SP$ ChangeZone | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBExile | SpellDescription$ Exile target creature. +SVar:DBExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | TargetMin$ 0 | ValidTgts$ Instant,Sorcery | TgtPrompt$ Select up to one target instant or sorcery card from a graveyard | SubAbility$ DBLearn | SpellDescription$ Exile up to one target instant or sorcery card from a graveyard. +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +Oracle:Exile target creature. Exile up to one target instant or sorcery card from a graveyard.\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/res/cardsfolder/upcoming/sparring_regimen.txt b/forge-gui/res/cardsfolder/upcoming/sparring_regimen.txt new file mode 100644 index 00000000000..0f56e7ee597 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/sparring_regimen.txt @@ -0,0 +1,10 @@ +Name:Sparring Regimen +ManaCost:2 W +Types:Enchantment +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigLearn | TriggerDescription$ When CARDNAME enters the battlefield, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +SVar:TrigLearn:DB$ Learn +T:Mode$ AttackersDeclared | AttackingPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you attack, put a +1/+1 counter on target attacking creature and untap it. +SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature | SubAbility$ DBUntap +SVar:DBUntap:DB$ Untap | Defined$ Targeted +DeckHas:Ability$Counters +Oracle:When Sparring Regimen enters the battlefield, learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.)\nWhenever you attack, put a +1/+1 counter on target attacking creature and untap it. diff --git a/forge-gui/res/cardsfolder/upcoming/study_break.txt b/forge-gui/res/cardsfolder/upcoming/study_break.txt new file mode 100644 index 00000000000..fef7f477b30 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/study_break.txt @@ -0,0 +1,6 @@ +Name:Study Break +ManaCost:1 W +Types:Instant +A:SP$ Tap | Cost$ 1 W | TargetMin$ 0 | TargetMax$ 2 | TgtPrompt$ Choose up to two target creatures | ValidTgts$ Creature | SpellDescription$ Tap up to two target creatures. | SubAbility$ DBLearn +SVar:DBLearn:DB$ Learn | SpellDescription$ Learn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) +Oracle:Tap up to two target creatures.\nLearn. (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.) diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 8c6bcdd50fa..6ae278e4374 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -476,8 +476,9 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont final boolean useUiPointAtCard = (FModel.getPreferences().getPrefBoolean(FPref.UI_SELECT_FROM_CARD_DISPLAYS) && (!GuiBase.getInterface().isLibgdxPort())) ? (cz.is(ZoneType.Battlefield) || cz.is(ZoneType.Hand) || cz.is(ZoneType.Library) || - cz.is(ZoneType.Graveyard) || cz.is(ZoneType.Exile) || cz.is(ZoneType.Flashback) || cz.is(ZoneType.Command)) : - (cz.is(ZoneType.Hand) && cz.getPlayer() == player || cz.is(ZoneType.Battlefield)); + cz.is(ZoneType.Graveyard) || cz.is(ZoneType.Exile) || cz.is(ZoneType.Flashback) || + cz.is(ZoneType.Command) || cz.is(ZoneType.Sideboard)) : + (cz.is(ZoneType.Hand, player) || cz.is(ZoneType.Battlefield)); if (!useUiPointAtCard) { return false; }