diff --git a/forge-game/src/main/java/forge/game/ForgeScript.java b/forge-game/src/main/java/forge/game/ForgeScript.java index ea64739495d..ac712839495 100644 --- a/forge-game/src/main/java/forge/game/ForgeScript.java +++ b/forge-game/src/main/java/forge/game/ForgeScript.java @@ -7,6 +7,7 @@ import forge.game.card.Card; import forge.game.card.CardState; import forge.game.cost.Cost; import forge.game.player.Player; +import forge.game.spellability.AbilityManaPart; import forge.game.spellability.SpellAbility; import forge.game.staticability.StaticAbility; import forge.util.Expressions; @@ -189,6 +190,18 @@ public class ForgeScript { if (!Expressions.compare(y, property, x)) { return false; } + } else if (property.equals("ManaAbilityCantPaidFor")) { + if (!sa.isManaAbility()) { + return false; + } + SpellAbility paidFor = sourceController.getPaidForSA(); + do { + AbilityManaPart mana = sa.getManaPart(); + if (paidFor != null && mana.meetsManaRestrictions(paidFor) && !mana.getExpressChoice().isEmpty()) { + return false; + } + sa = sa.getSubAbility(); + } while(sa != null); } else if (sa.getHostCard() != null) { return sa.getHostCard().hasProperty(property, sourceController, source, spellAbility); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java index 7b2fd9522d7..4da7d9620ba 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java @@ -62,6 +62,8 @@ public class PlayEffect extends SpellAbilityEffect { public void resolve(final SpellAbility sa) { final Card source = sa.getHostCard(); Player activator = sa.getActivatingPlayer(); + Player controlledByPlayer = null; + long controlledByTimeStamp = -1; final Game game = activator.getGame(); final boolean optional = sa.hasParam("Optional"); boolean remember = sa.hasParam("RememberPlayed"); @@ -74,6 +76,11 @@ public class PlayEffect extends SpellAbilityEffect { activator = AbilityUtils.getDefinedPlayers(source, sa.getParam("Controller"), sa).get(0); } + if (sa.hasParam("ControlledByPlayer")) { + controlledByTimeStamp = game.getNextTimestamp(); + controlledByPlayer = AbilityUtils.getDefinedPlayers(source, sa.getParam("ControlledByPlayer"), sa).get(0); + } + final Player controller = activator; CardCollection tgtCards; CardCollection showCards = new CardCollection(); @@ -164,13 +171,17 @@ public class PlayEffect extends SpellAbilityEffect { amount = tgtCards.size(); } + if (controlledByPlayer != null) { + activator.addController(controlledByTimeStamp, controlledByPlayer); + } + final CardCollection saidNoTo = new CardCollection(); while (tgtCards.size() > saidNoTo.size() && saidNoTo.size() < amount && amount > 0) { activator.getController().tempShowCards(showCards); Card tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblSelectCardToPlay"), null); activator.getController().endTempShowCards(); if (tgtCard == null) { - return; + break; } final boolean wasFaceDown; @@ -300,6 +311,9 @@ public class PlayEffect extends SpellAbilityEffect { tgtSA.setSVar("IsCastFromPlayEffect", "True"); + // Add controlled by player to target SA so when the spell is resolving, the controller would be changed again + tgtSA.setControlledByPlayer(controlledByTimeStamp, controlledByPlayer); + if (controller.getController().playSaFromPlayEffect(tgtSA)) { if (remember) { source.addRemembered(tgtSA.getHostCard()); @@ -317,6 +331,11 @@ public class PlayEffect extends SpellAbilityEffect { amount--; } + + // Remove controlled by player if any + if (controlledByPlayer != null) { + activator.removeController(controlledByTimeStamp); + } } // end resolve 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 7954026a69c..7b929bda99f 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -17,10 +17,12 @@ */ package forge.game.player; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Deque; import java.util.EnumSet; import java.util.HashSet; import java.util.List; @@ -219,6 +221,8 @@ public class Player extends GameEntity implements Comparable { private CardCollection lostOwnership = new CardCollection(); private CardCollection gainedOwnership = new CardCollection(); private int numManaConversion = 0; + // The SA currently being paid for + private Deque paidForStack = new ArrayDeque<>(); private Card monarchEffect = null; private Card blessingEffect = null; @@ -3281,6 +3285,16 @@ public class Player extends GameEntity implements Comparable { return view; } + public SpellAbility getPaidForSA() { + return paidForStack.peek(); + } + public void pushPaidForSA(SpellAbility sa) { + paidForStack.push(sa); + } + public void popPaidForSA() { + paidForStack.pop(); + } + public boolean isMonarch() { return equals(game.getMonarch()); } diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index 5a93430c4a5..84617e32ce2 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -28,6 +28,7 @@ import java.util.Objects; import java.util.Set; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @@ -108,6 +109,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private ManaCost multiKickerManaCost = null; private Player activatingPlayer = null; private Player targetingPlayer = null; + private Pair controlledByPlayer = null; private SpellAbility grantorOriginal = null; private StaticAbility grantorStatic = null; @@ -454,6 +456,24 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit targetingPlayer = targetingPlayer0; } + /** + * @return returns who controls the controller of this sa when it is resolving (for Word of Command effect). Null means not being controlled by other + */ + public Pair getControlledByPlayer() { + return controlledByPlayer; + } + /** + * @param ts time stamp of the control player effect + * @param controller the player who will control the controller of this sa + */ + public void setControlledByPlayer(long ts, Player controller) { + if (controller != null) { + controlledByPlayer = Pair.of(ts, controller); + } else { + controlledByPlayer = null; + } + } + public boolean isSpell() { return false; } public boolean isAbility() { return true; } public boolean isActivatedAbility() { return false; } diff --git a/forge-game/src/main/java/forge/game/zone/MagicStack.java b/forge-game/src/main/java/forge/game/zone/MagicStack.java index 91cf2d2fbe8..64cda5bb6c0 100644 --- a/forge-game/src/main/java/forge/game/zone/MagicStack.java +++ b/forge-game/src/main/java/forge/game/zone/MagicStack.java @@ -479,6 +479,11 @@ public class MagicStack /* extends MyObservable */ implements Iterable