diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 7c2a25b1efe..e2bf5ae96e7 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -64,7 +64,6 @@ import forge.util.Aggregates; import forge.util.ComparatorUtil; import forge.util.Expressions; import forge.util.MyRandom; -import forge.util.collect.FCollectionView; import io.sentry.Breadcrumb; import io.sentry.Sentry; @@ -437,11 +436,11 @@ public class AiController { } landList = CardLists.filter(landList, c -> { - CardCollectionView battlefield = player.getCardsIn(ZoneType.Battlefield); if (canPlaySpellBasic(c, null) != AiPlayDecision.WillPlay) { return false; } String name = c.getName(); + CardCollectionView battlefield = player.getCardsIn(ZoneType.Battlefield); if (c.getType().isLegendary() && !name.equals("Flagstones of Trokair")) { if (Iterables.any(battlefield, CardPredicates.nameEquals(name))) { return false; @@ -461,11 +460,8 @@ public class AiController { } // don't play the land if it has cycling and enough lands are available - final FCollectionView spellAbilities = c.getSpellAbilities(); - for (final SpellAbility sa : spellAbilities) { - if (sa.isCycling()) { - return false; - } + if (c.hasKeyword(Keyword.CYCLING)) { + return false; } } return Iterables.any(c.getAllPossibleAbilities(player, true), SpellAbility::isLandAbility); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java b/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java index d316ea836a5..b08e4e3bd3e 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java @@ -14,7 +14,6 @@ import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; -import forge.game.card.CardPredicates.Presets; import forge.game.cost.CostPart; import forge.game.cost.CostPayEnergy; import forge.game.cost.CostPutCounter; @@ -32,18 +31,12 @@ public class ComputerUtilAbility { if (!game.getStack().isEmpty() || !game.getPhaseHandler().getPhase().isMain()) { return null; } - final CardCollection hand = new CardCollection(player.getCardsIn(ZoneType.Hand)); - hand.addAll(player.getCardsIn(ZoneType.Exile)); - CardCollection landList = CardLists.filter(hand, Presets.LANDS); + CardCollection landList = new CardCollection(player.getCardsIn(ZoneType.Hand)); //filter out cards that can't be played landList = CardLists.filter(landList, c -> { - if (!c.getSVar("NeedsToPlay").isEmpty()) { - final String needsToPlay = c.getSVar("NeedsToPlay"); - CardCollection list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), needsToPlay, c.getController(), c, null); - if (list.isEmpty()) { - return false; - } + if (!c.hasPlayableLandFace()) { + return false; } return player.canPlayLand(c, false, c.getFirstSpellAbility()); }); @@ -54,7 +47,7 @@ public class ComputerUtilAbility { landsNotInHand.add(player.getCardsIn(ZoneType.Library).get(0)); } for (final Card crd : landsNotInHand) { - if (!(crd.isLand() || (crd.isFaceDown() && crd.getState(CardStateName.Original).getType().isLand()))) { + if (!(crd.hasPlayableLandFace() || (crd.isFaceDown() && crd.getState(CardStateName.Original).getType().isLand()))) { continue; } if (!crd.mayPlay(player).isEmpty()) { diff --git a/forge-game/src/main/java/forge/game/GameLogFormatter.java b/forge-game/src/main/java/forge/game/GameLogFormatter.java index faf9fe64314..9027a6d3bbb 100644 --- a/forge-game/src/main/java/forge/game/GameLogFormatter.java +++ b/forge-game/src/main/java/forge/game/GameLogFormatter.java @@ -2,7 +2,6 @@ package forge.game; import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map.Entry; import com.google.common.collect.Iterables; @@ -93,11 +92,7 @@ public class GameLogFormatter extends IGameEventVisitor.Base { if (event.sa.getTargetRestrictions() != null) { StringBuilder sb = new StringBuilder(); - List targets = event.sa.getAllTargetChoices(); - // Include the TargetChoices from the stack instance, since the real target choices - // are on that object at this point (see SpellAbilityStackInstance constructor). - targets.add(event.si.getTargetChoices()); - for (TargetChoices ch : targets) { + for (TargetChoices ch : event.sa.getAllTargetChoices()) { if (null != ch) { sb.append(ch); } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index f94ad7292d5..ddbf16e1a2f 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -5524,6 +5524,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return isInstant() || isSorcery() || (isAura() && !isInZone(ZoneType.Battlefield)); } + public final boolean hasPlayableLandFace() { return isLand() || (isModal() && getState(CardStateName.Modal).getType().isLand()); } + public final boolean isLand() { return getType().isLand(); } public final boolean isBasicLand() { return getType().isBasicLand(); } public final boolean isSnow() { return getType().isSnow(); } diff --git a/forge-game/src/main/java/forge/game/card/CardPredicates.java b/forge-game/src/main/java/forge/game/card/CardPredicates.java index 5a75387682f..5e2289da035 100644 --- a/forge-game/src/main/java/forge/game/card/CardPredicates.java +++ b/forge-game/src/main/java/forge/game/card/CardPredicates.java @@ -22,7 +22,6 @@ import java.util.Comparator; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; -import forge.card.CardStateName; import forge.game.CardTraitBase; import forge.game.GameEntity; import forge.game.combat.CombatUtil; @@ -379,7 +378,7 @@ public final class CardPredicates { /** * a Predicate to get all lands. */ - public static final Predicate LANDS = c -> c.isLand() || (!c.isInZone(ZoneType.Battlefield) && c.isModal() && c.getState(CardStateName.Modal).getType().isLand()); + public static final Predicate LANDS = c -> c.isLand(); /** * a Predicate to get all mana-producing lands. */ diff --git a/forge-gui/res/cardsfolder/i/ignite_the_future.txt b/forge-gui/res/cardsfolder/i/ignite_the_future.txt index 742fe2f0e6e..fd2bd99348a 100644 --- a/forge-gui/res/cardsfolder/i/ignite_the_future.txt +++ b/forge-gui/res/cardsfolder/i/ignite_the_future.txt @@ -2,7 +2,7 @@ Name:Ignite the Future ManaCost:3 R Types:Sorcery A:SP$ Dig | Defined$ You | DigNum$ 3 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ Exile the top three cards of your library. Until the end of your next turn, you may play those cards. If this spell was cast from a graveyard, you may play cards this way without paying their mana costs. -SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | ForgetOnMoved$ Exile | Duration$ UntilTheEndOfYourNextTurn | ConditionDefined$ Self | ConditionPresent$ Card.wasCastFromGraveyard | ConditionCompare$ EQ0 | SubAbility$ DBEffect2 +SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | ForgetOnMoved$ Exile | Duration$ UntilTheEndOfYourNextTurn | SubAbility$ DBEffect2 SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play remembered card. SVar:DBEffect2:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play2 | ForgetOnMoved$ Exile | Duration$ UntilTheEndOfYourNextTurn | ConditionDefined$ Self | ConditionPresent$ Card.wasCastFromGraveyard | ConditionCompare$ EQ1 | SubAbility$ DBCleanup SVar:Play2:Mode$ Continuous | MayPlay$ True | MayPlayWithoutManaCost$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play remembered card without paying their mana costs. diff --git a/forge-gui/res/cardsfolder/n/nahiris_lithoforming.txt b/forge-gui/res/cardsfolder/n/nahiris_lithoforming.txt index d091434a374..c03aff4a5ac 100644 --- a/forge-gui/res/cardsfolder/n/nahiris_lithoforming.txt +++ b/forge-gui/res/cardsfolder/n/nahiris_lithoforming.txt @@ -2,16 +2,15 @@ Name:Nahiri's Lithoforming ManaCost:X R R Types:Sorcery A:SP$ Sacrifice | SacValid$ Land | Amount$ X | RememberSacrificed$ True | SubAbility$ DBDraw | StackDescription$ SpellDescription | SpellDescription$ Sacrifice X lands. For each land sacrificed this way, draw a card. You may play X additional lands this turn. Lands you control enter tapped this turn. -SVar:DBDraw:DB$ Draw | NumCards$ Y | SubAbility$ DBStoreSVar | StackDescription$ None -SVar:DBStoreSVar:DB$ StoreSVar | SVar$ XLands | Type$ CountSVar | Expression$ X | SubAbility$ DBEffect -SVar:DBEffect:DB$ Effect | StaticAbilities$ PlayMoreLand | ReplacementEffects$ LandETB | SubAbility$ DBCleanup -SVar:PlayMoreLand:Mode$ Continuous | Affected$ You | AdjustLandPlays$ XLands | EffectZone$ Command | Description$ You may play X additional lands this turn. +SVar:DBDraw:DB$ Draw | NumCards$ Y | SubAbility$ DBEffect | StackDescription$ None +SVar:DBEffect:DB$ Effect | SetChosenNumber$ X | StaticAbilities$ PlayMoreLand | ReplacementEffects$ LandETB | SubAbility$ DBCleanup +SVar:PlayMoreLand:Mode$ Continuous | Affected$ You | AdjustLandPlays$ Z | EffectZone$ Command | Description$ You may play X additional lands this turn. SVar:LandETB:Event$ Moved | ValidCard$ Land.YouCtrl | Destination$ Battlefield | ReplaceWith$ ETBTapped | ReplacementResult$ Updated | Description$ Lands you control enter tapped this turn. SVar:ETBTapped:DB$ Tap | ETB$ True | Defined$ ReplacedCard SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:X:Count$xPaid SVar:Y:Count$RememberedSize -SVar:XLands:Number$0 +SVar:Z:Count$ChosenNumber DeckHas:Ability$Sacrifice AI:RemoveDeck:All Oracle:Sacrifice X lands. For each land sacrificed this way, draw a card. You may play X additional lands this turn. Lands you control enter tapped this turn. diff --git a/forge-gui/res/cardsfolder/n/nyssa_of_traken.txt b/forge-gui/res/cardsfolder/n/nyssa_of_traken.txt index 1d56a8be081..ca2392612fa 100644 --- a/forge-gui/res/cardsfolder/n/nyssa_of_traken.txt +++ b/forge-gui/res/cardsfolder/n/nyssa_of_traken.txt @@ -3,8 +3,8 @@ ManaCost:3 U Types:Legendary Creature Human Scientist PT:3/4 S:Mode$ Continuous | Affected$ You | SetMaxHandSize$ Unlimited | Description$ You have no maximum hand size. -T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | OptionalDecider$ You | TriggerDescription$ Sonic Booster — Whenever CARDNAME attacks, sacrifice X artifacts. When you sacrifice one or more artifacts this way, tap up to X target creatures and you draw X cards. -SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ Sac | Execute$ TrigTap | TriggerDescription$ When you sacrifice one or more artifacts this way, tap up to X target creatures and you draw X cards. +T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | OptionalDecider$ You | TriggerDescription$ Sonic Booster — Whenever CARDNAME attacks, sacrifice any number artifacts. When you sacrifice one or more artifacts this way, tap up to that many target creatures and draw that many cards. +SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ Sac | Execute$ TrigTap | TriggerDescription$ When you sacrifice one or more artifacts this way, tap up to that many target creatures and draw that many cards. SVar:TrigTap:DB$ Tap | TargetMin$ 0 | TargetMax$ X | TgtPrompt$ Select up to X target creatures to tap | ValidTgts$ Creature | SubAbility$ TrigDraw SVar:TrigDraw:DB$ Draw | NumCards$ X SVar:X:Count$xPaid @@ -12,4 +12,4 @@ K:Doctor's companion SVar:HasAttackEffect:TRUE DeckHas:Ability$Sacrifice DeckNeeds:Type$Artifact -Oracle:You have no maximum hand size.\nSonic Booster — Whenever Nyssa of Traken attacks, sacrifice X artifacts. When you sacrifice one or more artifacts this way, tap up to X target creatures and you draw X cards.\nDoctor's companion (You can have two commanders if the other is the Doctor.) +Oracle:You have no maximum hand size.\nSonic Booster — Whenever Nyssa of Traken attacks, sacrifice any number of artifacts. When you sacrifice one or more artifacts this way, tap up to that many target creatures and draw that many cards.\nDoctor's companion (You can have two commanders if the other is the Doctor.) diff --git a/forge-gui/res/cardsfolder/w/wheel_of_potential.txt b/forge-gui/res/cardsfolder/w/wheel_of_potential.txt index 453284c8ec5..a862ebbc54b 100644 --- a/forge-gui/res/cardsfolder/w/wheel_of_potential.txt +++ b/forge-gui/res/cardsfolder/w/wheel_of_potential.txt @@ -1,18 +1,18 @@ Name:Wheel of Potential ManaCost:2 R Types:Sorcery -A:SP$ PutCounter | Defined$ You | CounterType$ ENERGY | CounterNum$ 3 | SubAbility$ ChooseX | StackDescription$ REP You get_{p:You} gets & you_ | SpellDescription$ You get {E}{E}{E} (three energy counters), then you may pay X {E}.,,,,,, +A:SP$ PutCounter | Defined$ You | CounterType$ ENERGY | CounterNum$ 3 | SubAbility$ ChooseX | StackDescription$ REP You get_{p:You} gets & you_ | SpellDescription$ You get {E}{E}{E} (three energy counters), then you may pay any amount of {E}.,,,,,, SVar:ChooseX:DB$ ChooseNumber | Max$ Count$YourCountersEnergy | ListTitle$ amount of energy to pay | SubAbility$ Pay | StackDescription$ None SVar:Pay:DB$ Pump | UnlessCost$ Mandatory PayEnergy | UnlessPayer$ You | UnlessSwitched$ True | SubAbility$ Choose | StackDescription$ None -SVar:Choose:DB$ GenericChoice | TempRemember$ Chooser | ShowChoice$ ExceptSelf | Defined$ Player | Choices$ ExileDraw,No | SubAbility$ DBExile | StackDescription$ SpellDescription | SpellDescription$ Each player may exile their hand and draw X cards. +SVar:Choose:DB$ GenericChoice | TempRemember$ Chooser | ShowChoice$ ExceptSelf | Defined$ Player | Choices$ ExileDraw,No | SubAbility$ Exile | StackDescription$ SpellDescription | SpellDescription$ Each player may exile their hand and draw cards equal to the amount of {E} paid this way. SVar:ExileDraw:DB$ Pump | Defined$ Remembered | NoteCards$ Self | NoteCardsFor$ ExileDraw | SpellDescription$ Exile your hand and draw X cards. SVar:No:DB$ Pump | SpellDescription$ Keep your hand. -SVar:ExileDraw:DB$ ChangeZoneAll | Origin$ Hand | Destination$ Exile | ChangeType$ Card.OwnedBy Player.NotedForExile | RememberChanged$ True | SubAbility$ Draw | StackDescription$ None -SVar:Draw:DB$ Draw | Defined$ Player.NotedForExile | NumCards$ X | SubAbility$ Effect | StackDescription$ None -SVar:Effect:DB$ Effect | ConditionCheckSVar$ X | ConditionSVarCompare$ GE7 | RememberObjects$ Remembered.YouOwn | StaticAbilities$ Play | Duration$ UntilTheEndOfYourNextTurn | ForgetOnMoved$ Exile | SubAbility$ DBCleanup | SpellDescription$ If X is 7 or more, you may play cards you own exiled this way until the end of your next turn. +SVar:Exile:DB$ ChangeZoneAll | Origin$ Hand | Destination$ Exile | Defined$ Player.NotedForExileDraw | RememberChanged$ True | SubAbility$ Draw | StackDescription$ None +SVar:Draw:DB$ Draw | Defined$ Player.NotedForExileDraw | NumCards$ X | SubAbility$ Effect | StackDescription$ None +SVar:Effect:DB$ Effect | ConditionCheckSVar$ X | ConditionSVarCompare$ GE7 | RememberObjects$ Remembered.YouOwn | StaticAbilities$ Play | Duration$ UntilTheEndOfYourNextTurn | ForgetOnMoved$ Exile | SubAbility$ DBCleanup | SpellDescription$ If 7 or more {E} was paid this way, you may play cards you own exiled this way until the end of your next turn. SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.YouOwn+IsRemembered | AffectedZone$ Exile | Description$ You may play cards you own exiled this way until the end of your next turn. SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBClearNotes -SVar:DBClearNotes:DB$ Pump | Defined$ Player | ClearNotedCardsFor$ Exile +SVar:DBClearNotes:DB$ Pump | Defined$ Player | ClearNotedCardsFor$ ExileDraw SVar:X:Count$ChosenNumber AI:RemoveDeck:All -Oracle:You get {E}{E}{E} (three energy counters), then you may pay X {E}.\nEach player may exile their hand and draw X cards. If X is 7 or more, you may play cards you own exiled this way until the end of your next turn. +Oracle:You get {E}{E}{E} (three energy counters), then you may pay any amount of {E}.\nEach player may exile their hand and draw cards equal to the amount of {E} paid this way. If 7 or more {E} was paid this way, you may play cards you own exiled this way until the end of your next turn.