From 73c457f46a382dce5c42b9662f222aa021454dcf Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 16:35:21 +0000 Subject: [PATCH 01/11] - The AI will no longer play Mind Twist with X=0. --- src/main/java/forge/card/ability/ai/DiscardAi.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/forge/card/ability/ai/DiscardAi.java b/src/main/java/forge/card/ability/ai/DiscardAi.java index bbe0db9e21b..2ef1e2bc2ea 100644 --- a/src/main/java/forge/card/ability/ai/DiscardAi.java +++ b/src/main/java/forge/card/ability/ai/DiscardAi.java @@ -81,6 +81,9 @@ public class DiscardAi extends SpellAbilityAi { // Set PayX here to maximum value. final int cardsToDiscard = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), ai.getOpponent() .getCardsIn(ZoneType.Hand).size()); + if (cardsToDiscard < 1) { + return false; + } source.setSVar("PayX", Integer.toString(cardsToDiscard)); } else { if (AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("NumCards"), sa) < 1) { From 8b30d41d177df8ee54e2699c06812e11181d5318 Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 16:38:15 +0000 Subject: [PATCH 02/11] - Improved AI using Shirei, Shizo's Caretaker. --- res/cardsfolder/s/shirei_shizos_caretaker.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/cardsfolder/s/shirei_shizos_caretaker.txt b/res/cardsfolder/s/shirei_shizos_caretaker.txt index f8709e710c4..89336025030 100644 --- a/res/cardsfolder/s/shirei_shizos_caretaker.txt +++ b/res/cardsfolder/s/shirei_shizos_caretaker.txt @@ -7,6 +7,8 @@ SVar:DelTrig:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ Player | Execute$ T SVar:TrigReturn:AB$ ChangeZone | Cost$ 0 | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredCard T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Static$ True | Execute$ DBCleanup SVar:DBCleanup:DB$Cleanup | ClearTriggered$ True +S:Mode$ Continuous | Affected$ Creature.YouCtrl+powerLE1+YouOwn | AddSVar$ Sac +SVar:Sac:SVar:SacMe:3 SVar:Picture:http://www.wizards.com/global/images/magic/general/shirei_shizos_caretaker.jpg Oracle:Whenever a creature with power 1 or less is put into your graveyard from the battlefield, you may return that card to the battlefield under your control at the beginning of the next end step if Shirei, Shizo's Caretaker is still on the battlefield. SetInfo:BOK Rare \ No newline at end of file From 4e8ce56e80fd2c27222065aec135f6acbd757e05 Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 16:39:51 +0000 Subject: [PATCH 03/11] - Added SVar:RemAIDeck:True to Timid Drake. --- res/cardsfolder/t/timid_drake.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/res/cardsfolder/t/timid_drake.txt b/res/cardsfolder/t/timid_drake.txt index ce95df11e67..6c087962373 100644 --- a/res/cardsfolder/t/timid_drake.txt +++ b/res/cardsfolder/t/timid_drake.txt @@ -6,6 +6,7 @@ K:Flying T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Other | TriggerZones$ Battlefield | Execute$ TrigReturn | TriggerDescription$ When another creature enters the battlefield, return CARDNAME to its owner's hand. SVar:TrigReturn:AB$ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Battlefield | Destination$ Hand SVar:AntiBuffedBy:Creature +SVar:RemAIDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/timid_drake.jpg Oracle:Flying\nWhen another creature enters the battlefield, return Timid Drake to its owner's hand. SetInfo:MMQ Uncommon From d20b5f02f91a81d62e8e7120bd106c2811e7410b Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 16:46:17 +0000 Subject: [PATCH 04/11] - Improved AI using Rending Vines. --- res/cardsfolder/r/rending_vines.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/cardsfolder/r/rending_vines.txt b/res/cardsfolder/r/rending_vines.txt index 2d3a5bbfbf0..cc43b5f982a 100644 --- a/res/cardsfolder/r/rending_vines.txt +++ b/res/cardsfolder/r/rending_vines.txt @@ -1,8 +1,8 @@ Name:Rending Vines ManaCost:1 G G Types:Instant Arcane -A:SP$ Destroy | Cost$ 1 G G | ValidTgts$ Artifact,Enchantment | TgtPrompt$ Select target artifact or enchantment | ConditionCheckSVar$ Y | ConditionSVarCompare$ LEX | References$ X,Y | SubAbility$ DBDraw | SpellDescription$ Destroy target artifact or enchantment if its converted mana cost is less than or equal to the number of cards in your hand. -SVar:DBDraw:DB$Draw | NumCards$ 1 | SpellDescription$ Draw a card. +A:SP$ Destroy | Cost$ 1 G G | ValidTgts$ Artifact,Enchantment | TgtPrompt$ Select target artifact or enchantment | ConditionCheckSVar$ Y | ConditionSVarCompare$ LEX | References$ X,Y | AITgts$ Card.cmcLTX | SubAbility$ DBDraw | SpellDescription$ Destroy target artifact or enchantment if its converted mana cost is less than or equal to the number of cards in your hand. +SVar:DBDraw:DB$ Draw | NumCards$ 1 | SpellDescription$ Draw a card. SVar:Y:Targeted$CardManaCost SVar:X:Count$InYourHand SVar:Picture:http://www.wizards.com/global/images/magic/general/rending_vines.jpg From 3b0a3e665871e011dea9ec3611924663bd2443d5 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 20:41:02 +0000 Subject: [PATCH 05/11] Code that chooses cards to be discarded moved to PlayerController and ComputerUtil --- src/main/java/forge/CardLists.java | 3 + .../card/ability/effects/DiscardEffect.java | 150 ++---------------- .../cardfactory/CardFactorySorceries.java | 6 +- src/main/java/forge/game/ai/AiController.java | 35 ++-- src/main/java/forge/game/ai/ComputerUtil.java | 74 +++++++-- src/main/java/forge/game/player/AIPlayer.java | 5 +- .../forge/game/player/PlayerController.java | 3 + .../forge/game/player/PlayerControllerAi.java | 12 ++ .../game/player/PlayerControllerHuman.java | 8 + 9 files changed, 121 insertions(+), 175 deletions(-) diff --git a/src/main/java/forge/CardLists.java b/src/main/java/forge/CardLists.java index 44363d2e8c6..4d8f445fba9 100644 --- a/src/main/java/forge/CardLists.java +++ b/src/main/java/forge/CardLists.java @@ -24,6 +24,7 @@ import java.util.List; import com.google.common.base.Predicate; import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -89,6 +90,8 @@ public class CardLists { return aLen - bLen; } }; + + public static final List emptyList = ImmutableList.of(); /** *

diff --git a/src/main/java/forge/card/ability/effects/DiscardEffect.java b/src/main/java/forge/card/ability/effects/DiscardEffect.java index 27aef564e8f..d15ab566e7f 100644 --- a/src/main/java/forge/card/ability/effects/DiscardEffect.java +++ b/src/main/java/forge/card/ability/effects/DiscardEffect.java @@ -1,26 +1,18 @@ package forge.card.ability.effects; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; -import com.google.common.base.Predicate; - import forge.Card; import forge.CardLists; import forge.card.ability.AbilityUtils; import forge.card.cardfactory.CardFactoryUtil; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; -import forge.game.ai.ComputerUtilCard; -import forge.game.player.AIPlayer; import forge.game.player.Player; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; -import forge.util.Aggregates; public class DiscardEffect extends RevealEffectBase { @Override @@ -87,44 +79,6 @@ public class DiscardEffect extends RevealEffectBase { return sb.toString(); } // discardStackDescription() - /** - * TODO: Write javadoc for this method. - * @param sa - * @param opponentHand - * @param list - */ - private Card chooseCardToDiscardFromOpponent(SpellAbility sa, List opponentHand) { - List goodChoices = CardLists.filter(opponentHand, new Predicate() { - @Override - public boolean apply(final Card c) { - if (!c.getSVar("DiscardMeByOpp").equals("") || !c.getSVar("DiscardMe").equals("")) { - return false; - } - return true; - } - }); - if (goodChoices.isEmpty()) { - goodChoices = opponentHand; - } - final List dChoices = new ArrayList(); - if (sa.hasParam("DiscardValid")) { - final String validString = sa.getParam("DiscardValid"); - if (validString.contains("Creature") && !validString.contains("nonCreature")) { - final Card c = ComputerUtilCard.getBestCreatureAI(goodChoices); - if (c != null) { - dChoices.add(ComputerUtilCard.getBestCreatureAI(goodChoices)); - } - } - } - - Collections.sort(goodChoices, CardLists.TextLenComparator); - - CardLists.sortByCmcDesc(goodChoices); - dChoices.add(goodChoices.get(0)); - - return Aggregates.random(goodChoices); - } - @Override public void resolve(SpellAbility sa) { final Card source = sa.getSourceCard(); @@ -194,11 +148,7 @@ public class DiscardEffect extends RevealEffectBase { // Reveal final List dPHand = p.getCardsIn(ZoneType.Hand); - if (p.isHuman()) { - // "reveal to computer" for information gathering - } else { - GuiChoose.oneOrNone("Revealed computer hand", dPHand); - } + p.getOpponent().getController().reveal("Reveal " + p + " hand" , dPHand, ZoneType.Hand, p); String valid = sa.hasParam("DiscardValid") ? sa.getParam("DiscardValid") : "Card"; @@ -215,7 +165,7 @@ public class DiscardEffect extends RevealEffectBase { } else if (mode.equals("RevealYouChoose") || mode.equals("RevealOppChoose") || mode.equals("TgtChoose")) { // Is Reveal you choose right? I think the wrong player is // being used? - List dPHand = new ArrayList(p.getCardsIn(ZoneType.Hand)); + List dPHand = p.getCardsIn(ZoneType.Hand); if (dPHand.isEmpty()) continue; // for loop over players @@ -224,11 +174,9 @@ public class DiscardEffect extends RevealEffectBase { int amount = StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) : CardFactoryUtil.xCount(source, source.getSVar(amountString)); dPHand = getRevealedList(p, dPHand, amount, false); } - List dPChHand = new ArrayList(dPHand); final String valid = sa.hasParam("DiscardValid") ? sa.getParam("DiscardValid") : "Card"; - String[] dValid = ArrayUtils.EMPTY_STRING_ARRAY; - dValid = valid.split(","); - dPChHand = CardLists.getValidCards(dPHand, dValid, source.getController(), source); + String[] dValid = valid.split(","); + List validCards = CardLists.getValidCards(dPHand, dValid, source.getController(), source); Player chooser = p; if (mode.equals("RevealYouChoose")) { @@ -237,60 +185,16 @@ public class DiscardEffect extends RevealEffectBase { chooser = source.getController().getOpponent(); } - List toBeDiscarded = new ArrayList(); - if (chooser.isComputer()) { - List dPChHand1 = new ArrayList(p.getCardsIn(ZoneType.Hand)); - dPChHand1 = CardLists.getValidCards(dPChHand1, dValid, source.getController(), source); - - int max = Math.min(dPChHand1.size(), numCards); - List list = new ArrayList(); - - if (!p.isOpponentOf(chooser) && p instanceof AIPlayer) { // discard AI cards - toBeDiscarded = ((AIPlayer) p).getAi().getCardsToDiscard(max, dValid, sa); - } else { - // discard hostile or human opponent - for (int i = 0; i < max; i++) { - Card dC = chooseCardToDiscardFromOpponent(sa, dPChHand1); - dPChHand1.remove(dC); - toBeDiscarded.add(dC); - } - } - - if (mode.startsWith("Reveal")) { - GuiChoose.oneOrNone("Computer has chosen", list); - } + if (mode.startsWith("Reveal") && p != chooser) + chooser.getController().reveal("Revealed " + p + " hand", dPHand, ZoneType.Hand, p); + + int minDiscardAmount = sa.hasParam("AnyNumber") || sa.hasParam("Optional") ? 0 : numCards; + int max = Math.min(validCards.size(), minDiscardAmount); - } else { - // human - if (mode.startsWith("Reveal")) { - GuiChoose.oneOrNone("Revealed " + p + " hand", dPHand); - } + List toBeDiscarded = validCards.isEmpty() ? CardLists.emptyList : chooser.getController().chooseCardsToDiscardFrom(p, sa, validCards, max); - if (sa.hasParam("AnyNumber")) { - List chosen = getDiscardedList(p, dPChHand); - - for (Card c : chosen) { - dPChHand.remove(c); - toBeDiscarded.add(c); - } - } else - for (int i = 0; i < numCards; i++) { - if (dPChHand.isEmpty()) { - break; - } - Card dC = null; - if (sa.hasParam("Optional")) { - dC = GuiChoose.oneOrNone("Choose a card to be discarded", dPChHand); - } else { - dC = GuiChoose.one("Choose a card to be discarded", dPChHand); - } - - if (dC != null) { - dPChHand.remove(dC); - toBeDiscarded.add(dC); - } - else break; - } + if (mode.startsWith("Reveal") ) { + p.getController().reveal(chooser + " has chosen", toBeDiscarded, ZoneType.Hand, p); } if (toBeDiscarded != null) { @@ -311,34 +215,4 @@ public class DiscardEffect extends RevealEffectBase { } } // discardResolve() - - public static List getDiscardedList(final Player player, final List valid) { - final List chosen = new ArrayList(); - final int validamount = Math.min(valid.size(), valid.size()); - - if (player.isHuman() && validamount > 0) { - final List selection = GuiChoose.order("Choose Which Cards to Discard", "Discarded", -1, valid, null, null); - for (final Object o : selection) { - if (o != null && o instanceof Card) { - chosen.add((Card) o); - } - } - } else { - for (int i = 0; i < validamount; i++) { - if (player.isHuman()) { - final Card o = GuiChoose.one("Choose card(s) to discard", valid); - if (o != null) { - chosen.add(o); - valid.remove(o); - } else { - break; - } - } else { // Computer - chosen.add(valid.get(0)); - valid.remove(valid.get(0)); - } - } - } - return chosen; - } } diff --git a/src/main/java/forge/card/cardfactory/CardFactorySorceries.java b/src/main/java/forge/card/cardfactory/CardFactorySorceries.java index 25ce2580b0f..c9f451986f7 100644 --- a/src/main/java/forge/card/cardfactory/CardFactorySorceries.java +++ b/src/main/java/forge/card/cardfactory/CardFactorySorceries.java @@ -473,11 +473,9 @@ public class CardFactorySorceries { int s = h.size(); min = Math.min(min, s); } - Iterator> hh = hands.iterator(); - for (Player p : Singletons.getModel().getGame().getPlayers()) { - List h = hh.next(); - int sac = h.size() - min; + for (Player p : Singletons.getModel().getGame().getPlayers()) { + int sac = p.getCardsIn(ZoneType.Hand).size() - min; if (sac == 0) { continue; } diff --git a/src/main/java/forge/game/ai/AiController.java b/src/main/java/forge/game/ai/AiController.java index 1d098e6e27a..fdbec57c938 100644 --- a/src/main/java/forge/game/ai/AiController.java +++ b/src/main/java/forge/game/ai/AiController.java @@ -525,16 +525,21 @@ public class AiController { */ public List getCardsToDiscard(final int numDiscard, final String[] uTypes, final SpellAbility sa) { List hand = new ArrayList(player.getCardsIn(ZoneType.Hand)); - Card sourceCard = null; + if ((uTypes != null) && (sa != null)) { hand = CardLists.getValidCards(hand, uTypes, sa.getActivatingPlayer(), sa.getSourceCard()); } + return getCardsToDiscard(numDiscard, hand, sa); + } - if (hand.size() < numDiscard) { + public List getCardsToDiscard(final int numDiscard, final List validCards, final SpellAbility sa) { + + if (validCards.size() < numDiscard) { return null; } - + + Card sourceCard = null; final List discardList = new ArrayList(); int count = 0; if (sa != null) { @@ -545,7 +550,7 @@ public class AiController { while (count < numDiscard) { Card prefCard = null; if (sa != null && sa.getActivatingPlayer() != null && sa.getActivatingPlayer().isOpponentOf(player)) { - for (Card c : hand) { + for (Card c : validCards) { if (c.hasKeyword("If a spell or ability an opponent controls causes you to discard CARDNAME," + " put it onto the battlefield instead of putting it into your graveyard.")) { prefCard = c; @@ -554,11 +559,11 @@ public class AiController { } } if (prefCard == null) { - prefCard = ComputerUtil.getCardPreference(player, sourceCard, "DiscardCost", hand); + prefCard = ComputerUtil.getCardPreference(player, sourceCard, "DiscardCost", validCards); } if (prefCard != null) { discardList.add(prefCard); - hand.remove(prefCard); + validCards.remove(prefCard); count++; } else { break; @@ -569,11 +574,11 @@ public class AiController { // choose rest for (int i = 0; i < discardsLeft; i++) { - if (hand.isEmpty()) { + if (validCards.isEmpty()) { continue; } final int numLandsInPlay = Iterables.size(Iterables.filter(player.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS)); - final List landsInHand = CardLists.filter(hand, CardPredicates.Presets.LANDS); + final List landsInHand = CardLists.filter(validCards, CardPredicates.Presets.LANDS); final int numLandsInHand = landsInHand.size(); // Discard a land @@ -582,21 +587,21 @@ public class AiController { if (canDiscardLands) { discardList.add(landsInHand.get(0)); - hand.remove(landsInHand.get(0)); + validCards.remove(landsInHand.get(0)); } else { // Discard other stuff - CardLists.sortByCmcDesc(hand); + CardLists.sortByCmcDesc(validCards); int numLandsAvailable = numLandsInPlay; if (numLandsInHand > 0) { numLandsAvailable++; } //Discard unplayable card - if (hand.get(0).getCMC() > numLandsAvailable) { - discardList.add(hand.get(0)); - hand.remove(hand.get(0)); + if (validCards.get(0).getCMC() > numLandsAvailable) { + discardList.add(validCards.get(0)); + validCards.remove(validCards.get(0)); } else { //Discard worst card - Card worst = ComputerUtilCard.getWorstAI(hand); + Card worst = ComputerUtilCard.getWorstAI(validCards); discardList.add(worst); - hand.remove(worst); + validCards.remove(worst); } } } diff --git a/src/main/java/forge/game/ai/ComputerUtil.java b/src/main/java/forge/game/ai/ComputerUtil.java index b8d8ad5b8cc..61194d3f465 100644 --- a/src/main/java/forge/game/ai/ComputerUtil.java +++ b/src/main/java/forge/game/ai/ComputerUtil.java @@ -1001,21 +1001,7 @@ public class ComputerUtil { for (final Card c : all) { for (final SpellAbility sa : c.getSpellAbility()) { - - if (sa.getApi() == null) { - continue; - } - - /// ???? - // if ( sa.isAbility() || sa.isSpell() && sa.getApi() != ApiType.Pump ) continue - if (sa.hasParam("AB") && !sa.getParam("AB").equals("Pump")) { - continue; - } - if (sa.hasParam("SP") && !sa.getParam("SP").equals("Pump")) { - continue; - } - - if (sa.hasParam("KW") && sa.getParam("KW").contains("Haste")) { + if (sa.getApi() == ApiType.Pump && sa.hasParam("KW") && sa.getParam("KW").contains("Haste")) { return true; } } @@ -1239,4 +1225,62 @@ public class ComputerUtil { } return bottom; } + + /** + * TODO: Write javadoc for this method. + * @param chooser + * @param discarder + * @param sa + * @param validCards + * @param min + * @return + */ + public static List getCardsToDiscardFromOpponent(AIPlayer chooser, Player discarder, SpellAbility sa, List validCards, int min) { + List goodChoices = CardLists.filter(validCards, new Predicate() { + @Override + public boolean apply(final Card c) { + if (!c.getSVar("DiscardMeByOpp").equals("") || !c.getSVar("DiscardMe").equals("")) { + return false; + } + return true; + } + }); + if (goodChoices.isEmpty()) { + goodChoices = validCards; + } + final List dChoices = new ArrayList(); + if (sa.hasParam("DiscardValid")) { + final String validString = sa.getParam("DiscardValid"); + if (validString.contains("Creature") && !validString.contains("nonCreature")) { + final Card c = ComputerUtilCard.getBestCreatureAI(goodChoices); + if (c != null) { + dChoices.add(ComputerUtilCard.getBestCreatureAI(goodChoices)); + } + } + } + + Collections.sort(goodChoices, CardLists.TextLenComparator); + + CardLists.sortByCmcDesc(goodChoices); + dChoices.add(goodChoices.get(0)); + + return Aggregates.random(goodChoices, min); + } + + /** + * TODO: Write javadoc for this method. + * @param aiChoser + * @param p + * @param sa + * @param validCards + * @param min + * @return + */ + public static List getCardsToDiscardFromFriend(AIPlayer aiChooser, Player p, SpellAbility sa, List validCards, int min) { + if (p instanceof AIPlayer) { // ask that ai player what he would like to discard + return ((AIPlayer) p).getAi().getCardsToDiscard(min, validCards, sa); + } + // no special options for human or remote friends + return getCardsToDiscardFromOpponent(aiChooser, p, sa, validCards, min); + } } diff --git a/src/main/java/forge/game/player/AIPlayer.java b/src/main/java/forge/game/player/AIPlayer.java index fa3ede2880a..4853430e809 100644 --- a/src/main/java/forge/game/player/AIPlayer.java +++ b/src/main/java/forge/game/player/AIPlayer.java @@ -111,7 +111,7 @@ public class AIPlayer extends Player { public final void discard(final int num, final SpellAbility sa) { int max = this.getCardsIn(ZoneType.Hand).size(); max = Math.min(max, num); - final List toDiscard = this.getAi().getCardsToDiscard(max, null, sa); + final List toDiscard = this.getAi().getCardsToDiscard(max, (String[])null, sa); for (int i = 0; i < toDiscard.size(); i++) { this.doDiscard(toDiscard.get(i), sa); } @@ -125,8 +125,7 @@ public class AIPlayer extends Player { if (tHand.size() > 0) { Card toDiscard = Aggregates.itemWithMin(tHand, CardPredicates.Accessors.fnGetCmc); - toDiscard.getController().discard(toDiscard, sa); // this got changed - // to doDiscard basically + discard(toDiscard, sa); // this got changed to doDiscard basically return; } this.discard(num, sa); diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index 49500052dc2..9b42de5ff33 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -108,4 +108,7 @@ public abstract class PlayerController { public abstract void reveal(String string, List cards, ZoneType zone, Player owner); public abstract ImmutablePair, List> arrangeForScry(List topN); public abstract boolean willPutCardOnTop(Card c); + + /** p = target player, validCards - possible discards, min cards to discard */ + public abstract List chooseCardsToDiscardFrom(Player p, SpellAbility sa, List validCards, int min); } diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index d6814d8076e..95757f81f43 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -255,4 +255,16 @@ public class PlayerControllerAi extends PlayerController { return true; // AI does not know what will happen next (another clash or that would become his topdeck) } + /* (non-Javadoc) + * @see forge.game.player.PlayerController#chooseCardsToDiscardFrom(forge.game.player.Player, java.util.List, int) + */ + @Override + public List chooseCardsToDiscardFrom(Player p, SpellAbility sa, List validCards, int min) { + boolean isTargetFriendly = !p.isOpponentOf(getPlayer()); + + return isTargetFriendly + ? ComputerUtil.getCardsToDiscardFromFriend(player, p, sa, validCards, min) + : ComputerUtil.getCardsToDiscardFromOpponent(player, p, sa, validCards, min); + } + } diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index 27ad4cc512d..c3d29124351 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -318,4 +318,12 @@ public class PlayerControllerHuman extends PlayerController { public boolean willPutCardOnTop(Card c) { return GuiDialog.confirm(c, "Where will you put " + c.getName() + " in your library", new String[]{"Top", "Bottom"} ); } + + /* (non-Javadoc) + * @see forge.game.player.PlayerController#chooseCardsToDiscardFrom(forge.game.player.Player, java.util.List, int) + */ + @Override + public List chooseCardsToDiscardFrom(Player p, SpellAbility sa, List valid, int minDiscard) { + return GuiChoose.order("Choose cards to Discard", "Discarded", minDiscard == 0 ? -1 : minDiscard, valid, null, null); + } } From 742cec28a797e453296484e7c0a7a5a73d8ed298 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 21:39:18 +0000 Subject: [PATCH 06/11] dredge action from player* moved: decisions are taken at PlayerController, execution remains in Player --- res/cardsfolder/l/land_equilibrium.txt | 1 + src/main/java/forge/game/ai/AiController.java | 14 ++++ src/main/java/forge/game/player/AIPlayer.java | 51 --------------- .../java/forge/game/player/HumanPlayer.java | 64 +------------------ src/main/java/forge/game/player/Player.java | 36 ++++------- .../forge/game/player/PlayerController.java | 1 + .../forge/game/player/PlayerControllerAi.java | 8 ++- .../game/player/PlayerControllerHuman.java | 11 ++++ .../java/forge/game/player/PlayerType.java | 7 +- .../java/forge/game/player/PlayerUtil.java | 16 ----- .../game/zone/PlayerZoneBattlefield.java | 32 +++------- 11 files changed, 56 insertions(+), 185 deletions(-) diff --git a/res/cardsfolder/l/land_equilibrium.txt b/res/cardsfolder/l/land_equilibrium.txt index 38a7a7c4073..be2bc1c410b 100644 --- a/res/cardsfolder/l/land_equilibrium.txt +++ b/res/cardsfolder/l/land_equilibrium.txt @@ -2,6 +2,7 @@ Name:Land Equilibrium ManaCost:2 U U Types:Enchantment Text:If an opponent who controls at least as many lands as you do would put a land onto the battlefield, that player instead puts that land onto the battlefield then sacrifices a land. +SVar:SacLand:DB$ Sacrifice | SacValid$ Land | Defined$ Opponent | SpellDescription$ Opponent sacrifices a land SVar:Picture:http://www.wizards.com/global/images/magic/general/land_equilibrium.jpg Oracle:If an opponent who controls at least as many lands as you do would put a land onto the battlefield, that player instead puts that land onto the battlefield then sacrifices a land. SetInfo:LEG Rare \ No newline at end of file diff --git a/src/main/java/forge/game/ai/AiController.java b/src/main/java/forge/game/ai/AiController.java index fdbec57c938..e9cdcd46446 100644 --- a/src/main/java/forge/game/ai/AiController.java +++ b/src/main/java/forge/game/ai/AiController.java @@ -48,6 +48,7 @@ import forge.game.player.Player; import forge.game.zone.ZoneType; import forge.util.Aggregates; import forge.util.Expressions; +import forge.util.MyRandom; /** *

@@ -706,5 +707,18 @@ public class AiController { } return true; } + + /** + * AI decides if he wants to use dredge ability and which one if many available + * @param dredgers - contains at least single element + * @return + */ + public Card chooseCardToDredge(List dredgers) { + // use dredge if there are more than one of them in your graveyard + if (dredgers.size() > 1 || MyRandom.getRandom().nextBoolean()) { + return Aggregates.random(dredgers); + } + return null; + } } diff --git a/src/main/java/forge/game/player/AIPlayer.java b/src/main/java/forge/game/player/AIPlayer.java index 4853430e809..e0df75f88f4 100644 --- a/src/main/java/forge/game/player/AIPlayer.java +++ b/src/main/java/forge/game/player/AIPlayer.java @@ -18,8 +18,6 @@ package forge.game.player; import java.util.List; -import java.util.Random; - import forge.Card; import forge.CardLists; @@ -27,10 +25,8 @@ import forge.CardPredicates; import forge.card.spellability.SpellAbility; import forge.game.GameState; import forge.game.ai.AiController; -import forge.game.ai.ComputerUtilCard; import forge.game.zone.ZoneType; import forge.util.Aggregates; -import forge.util.MyRandom; /** *

@@ -63,43 +59,6 @@ public class AIPlayer extends Player { - // ////////////////////////////// - // / - // / replaces Singletons.getModel().getGameAction().draw* methods - // / - // ////////////////////////////// - - /** - *

- * dredge. - *

- * - * @return a boolean. - */ - @Override - public final boolean dredge() { - final List dredgers = this.getDredge(); - final Random random = MyRandom.getRandom(); - - // use dredge if there are more than one of them in your graveyard - if ((dredgers.size() > 1) || ((dredgers.size() == 1) && random.nextBoolean())) { - CardLists.shuffle(dredgers); - final Card c = dredgers.get(0); - // rule 702.49a - if (this.getDredgeNumber(c) <= this.getCardsIn(ZoneType.Library).size()) { - // dredge library, put card in hand - game.getAction().moveToHand(c); - // put dredge number in graveyard - for (int i = 0; i < this.getDredgeNumber(c); i++) { - final Card c2 = this.getCardsIn(ZoneType.Library).get(0); - game.getAction().moveToGraveyard(c2); - } - return true; - } - } - return false; - } - // ////////////////////////////// // / // / replaces Singletons.getModel().getGameAction().discard* methods @@ -133,16 +92,6 @@ public class AIPlayer extends Player { // ///////////////////////// - /** {@inheritDoc} */ - @Override - public final void sacrificePermanent(final String prompt, final List choices) { - if (choices.size() > 0) { - // TODO - this could probably use better AI - final Card c = ComputerUtilCard.getWorstPermanentAI(choices, false, false, false, false); - game.getAction().sacrificeDestroy(c); - } - } - /* (non-Javadoc) * @see forge.game.player.Player#getType() */ diff --git a/src/main/java/forge/game/player/HumanPlayer.java b/src/main/java/forge/game/player/HumanPlayer.java index a752e6ceebb..951df64fefd 100644 --- a/src/main/java/forge/game/player/HumanPlayer.java +++ b/src/main/java/forge/game/player/HumanPlayer.java @@ -17,17 +17,10 @@ */ package forge.game.player; -import java.util.List; - -import forge.Card; - import forge.Singletons; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; import forge.game.GameState; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; -import forge.gui.GuiDialog; /** *

@@ -40,52 +33,11 @@ import forge.gui.GuiDialog; public class HumanPlayer extends Player { private PlayerControllerHuman controller; - /** - *

- * Constructor for HumanPlayer. - *

- * - * @param myName - * a {@link java.lang.String} object. - */ public HumanPlayer(final LobbyPlayer player, GameState game) { super(player, game); - controller = new PlayerControllerHuman(game, this); } - /** - *

- * dredge. - *

- * - * @return a boolean. - */ - @Override - public final boolean dredge() { - boolean dredged = false; - final boolean wantDredge = GuiDialog.confirm(null, "Do you want to dredge?"); - if (wantDredge) { - final Card c = GuiChoose.one("Select card to dredge", this.getDredge()); - // rule 702.49a - if (this.getDredgeNumber(c) <= getZone(ZoneType.Library).size()) { - - // might have to make this more sophisticated - // dredge library, put card in hand - game.getAction().moveToHand(c); - - for (int i = 0; i < this.getDredgeNumber(c); i++) { - final Card c2 = getZone(ZoneType.Library).get(0); - game.getAction().moveToGraveyard(c2); - } - dredged = true; - } else { - dredged = false; - } - } - return dredged; - } - /** {@inheritDoc} */ @Override public final void discard(final int num, final SpellAbility sa) { @@ -100,26 +52,12 @@ public class HumanPlayer extends Player { } } - /** {@inheritDoc} */ - @Override - public final void sacrificePermanent(final String prompt, final List choices) { - final Input in = PlayerUtil.inputSacrificePermanent(choices, prompt); - Singletons.getModel().getMatch().getInput().setInput(in); - } - - /* (non-Javadoc) - * @see forge.game.player.Player#getType() - */ @Override public PlayerType getType() { return PlayerType.HUMAN; } - - /* (non-Javadoc) - * @see forge.game.player.Player#getController() - */ - @Override public PlayerController getController() { return controller; } + } // end HumanPlayer class diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index 9ee074e8d5d..db24f27d9ef 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -1234,16 +1234,6 @@ public abstract class Player extends GameEntity implements Comparable { return this.drawCards(1); } - - /** - *

- * dredge. - *

- * - * @return a boolean. - */ - public abstract boolean dredge(); - /** * * TODO Write javadoc for this method. @@ -1288,8 +1278,17 @@ public abstract class Player extends GameEntity implements Comparable { for (int i = 0; i < n; i++) { // TODO: multiple replacements need to be selected by the controller - if (!this.getDredge().isEmpty()) { - if (this.dredge()) { + List dredgers = this.getDredge(); + if (!dredgers.isEmpty()) { + Card toDredge = getController().chooseCardToDredge(dredgers); + int dredgeNumber = toDredge == null ? Integer.MAX_VALUE : getDredgeNumber(toDredge); + if ( dredgeNumber <= getZone(ZoneType.Library).size()) { + game.getAction().moveToHand(toDredge); + + for (int iD = 0; iD < dredgeNumber; iD++) { + final Card c2 = getZone(ZoneType.Library).get(0); + game.getAction().moveToGraveyard(c2); + } continue; } } @@ -2141,19 +2140,6 @@ public abstract class Player extends GameEntity implements Comparable { this.attackersDeclaredThisTurn = 0; } - // ////////////////////////////// - /** - *

- * sacrificePermanent. - *

- * - * @param prompt - * a {@link java.lang.String} object. - * @param choices - * a {@link forge.CardList} object. - */ - public abstract void sacrificePermanent(String prompt, List choices); - // Game win/loss /** diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index 9b42de5ff33..083cebe6e3b 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -111,4 +111,5 @@ public abstract class PlayerController { /** p = target player, validCards - possible discards, min cards to discard */ public abstract List chooseCardsToDiscardFrom(Player p, SpellAbility sa, List validCards, int min); + public abstract Card chooseCardToDredge(List dredgers); } diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index 95757f81f43..44a99c9d880 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -255,9 +255,6 @@ public class PlayerControllerAi extends PlayerController { return true; // AI does not know what will happen next (another clash or that would become his topdeck) } - /* (non-Javadoc) - * @see forge.game.player.PlayerController#chooseCardsToDiscardFrom(forge.game.player.Player, java.util.List, int) - */ @Override public List chooseCardsToDiscardFrom(Player p, SpellAbility sa, List validCards, int min) { boolean isTargetFriendly = !p.isOpponentOf(getPlayer()); @@ -267,4 +264,9 @@ public class PlayerControllerAi extends PlayerController { : ComputerUtil.getCardsToDiscardFromOpponent(player, p, sa, validCards, min); } + @Override + public Card chooseCardToDredge(List dredgers) { + return brains.chooseCardToDredge(dredgers); + } + } diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index c3d29124351..be2b14e5737 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -326,4 +326,15 @@ public class PlayerControllerHuman extends PlayerController { public List chooseCardsToDiscardFrom(Player p, SpellAbility sa, List valid, int minDiscard) { return GuiChoose.order("Choose cards to Discard", "Discarded", minDiscard == 0 ? -1 : minDiscard, valid, null, null); } + + /* (non-Javadoc) + * @see forge.game.player.PlayerController#chooseCardToDredge(java.util.List) + */ + @Override + public Card chooseCardToDredge(List dredgers) { + if (GuiDialog.confirm(null, "Do you want to dredge?", false)) { + return GuiChoose.oneOrNone("Select card to dredge", dredgers); + } + return null; + } } diff --git a/src/main/java/forge/game/player/PlayerType.java b/src/main/java/forge/game/player/PlayerType.java index cd5498463aa..c91db8ec738 100644 --- a/src/main/java/forge/game/player/PlayerType.java +++ b/src/main/java/forge/game/player/PlayerType.java @@ -23,10 +23,7 @@ package forge.game.player; * @author jendave */ public enum PlayerType { - - /** The HUMAN. */ HUMAN, - - /** The COMPUTER. */ - COMPUTER + COMPUTER, + REMOTE } diff --git a/src/main/java/forge/game/player/PlayerUtil.java b/src/main/java/forge/game/player/PlayerUtil.java index 39d852e5785..c4919af1248 100644 --- a/src/main/java/forge/game/player/PlayerUtil.java +++ b/src/main/java/forge/game/player/PlayerUtil.java @@ -154,22 +154,6 @@ public final class PlayerUtil { return target; } // input_discard() - /** - *

- * input_sacrificePermanent. - *

- * - * @param choices - * a {@link forge.CardList} object. - * @param message - * a {@link java.lang.String} object. - * @return a {@link forge.control.input.Input} object. - * @since 1.0.15 - */ - public static Input inputSacrificePermanent(final List choices, final String message) { - return PlayerUtil.inputSacrificePermanentsFromList(1, choices, message); - } // input_sacrifice() - /** *

* input_sacrificePermanents. diff --git a/src/main/java/forge/game/zone/PlayerZoneBattlefield.java b/src/main/java/forge/game/zone/PlayerZoneBattlefield.java index b863a8bd342..dda09757746 100644 --- a/src/main/java/forge/game/zone/PlayerZoneBattlefield.java +++ b/src/main/java/forge/game/zone/PlayerZoneBattlefield.java @@ -30,6 +30,7 @@ import forge.CardPredicates; import forge.CardPredicates.Presets; import forge.Command; import forge.Singletons; +import forge.card.ability.AbilityFactory; import forge.card.mana.ManaCost; import forge.card.spellability.Ability; import forge.card.spellability.SpellAbility; @@ -143,30 +144,17 @@ public class PlayerZoneBattlefield extends PlayerZone { } - final List les = c.getOwner().getOpponent().getCardsIn(ZoneType.Battlefield, "Land Equilibrium"); - final Card lesLand = c; - if (les.size() > 0) { - final Card source = les.get(0); - final SpellAbility ability = new Ability(source, ManaCost.NO_COST) { - @Override - public void resolve() { - final List lands = lesLand.getOwner().getLandsInPlay(); - lesLand.getOwner().sacrificePermanent(source.getName() + " - Select a land to sacrifice", - lands); + + for( Player opp : c.getOwner().getOpponents()) + for( Card le : opp.getCardsIn(ZoneType.Battlefield, "Land Equilibrium") ) { + final List pLands = c.getOwner().getLandsInPlay(); + final List oLands = opp.getLandsInPlay(); + + if (oLands.size() <= (pLands.size() - 1)) { + SpellAbility abSac = AbilityFactory.getAbility(le.getSVar("SacLand"), le); + Singletons.getModel().getGame().getStack().addSimultaneousStackEntry(abSac); } - }; - final StringBuilder sb = new StringBuilder(); - sb.append(source).append(" - "); - sb.append(tisLand.getController()).append(" sacrifices a land."); - ability.setStackDescription(sb.toString()); - final List pLands = lesLand.getOwner().getLandsInPlay(); - final List oLands = lesLand.getOwner().getOpponent().getLandsInPlay(); - // (pLands - 1) because this land is in play, and the - // ability is before it is in play - if (oLands.size() <= (pLands.size() - 1)) { - Singletons.getModel().getGame().getStack().addSimultaneousStackEntry(ability); } - } } // isLand() } From 7c334b51b18b7c9cf5d760989865ed223bb8f36a Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 22:05:59 +0000 Subject: [PATCH 07/11] minor - remove unused methods, quest controllers uses lobbyPlayer to detect human playing quest one discard(int, sa) call less (inlined in AiInputCommon) --- src/main/java/forge/game/ai/AiInputCommon.java | 8 ++++++-- src/main/java/forge/game/player/Player.java | 16 +--------------- src/main/java/forge/quest/QuestController.java | 4 ++-- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/main/java/forge/game/ai/AiInputCommon.java b/src/main/java/forge/game/ai/AiInputCommon.java index 5276194e4f0..9a2c80a5880 100644 --- a/src/main/java/forge/game/ai/AiInputCommon.java +++ b/src/main/java/forge/game/ai/AiInputCommon.java @@ -87,8 +87,12 @@ public class AiInputCommon extends Input { final int size = player.getCardsIn(ZoneType.Hand).size(); if (!player.isUnlimitedHandSize()) { - final int numDiscards = size - player.getMaxHandSize(); - player.discard(numDiscards, null); + + int max = Math.min(player.getZone(ZoneType.Hand).size(), size - player.getMaxHandSize()); + final List toDiscard = player.getAi().getCardsToDiscard(max, (String[])null, null); + for (int i = 0; i < toDiscard.size(); i++) { + player.discard(toDiscard.get(i), null); + } } } break; diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index db24f27d9ef..e126dda9cb9 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -1687,18 +1687,6 @@ public abstract class Player extends GameEntity implements Comparable { return list; } - /** - *

- * discardRandom. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @return a List of cards discarded - */ - public final List discardRandom(final SpellAbility sa) { - return this.discardRandom(1, sa); - } /** *

@@ -1910,9 +1898,7 @@ public abstract class Player extends GameEntity implements Comparable { } // Dev Mode - if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.DEV_UNLIMITED_LAND) - && this.isHuman() - && Preferences.DEV_MODE) { + if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.DEV_UNLIMITED_LAND) && this.getType() == PlayerType.HUMAN && Preferences.DEV_MODE) { return true; } diff --git a/src/main/java/forge/quest/QuestController.java b/src/main/java/forge/quest/QuestController.java index 67a07172f62..0b07bb1f0d4 100644 --- a/src/main/java/forge/quest/QuestController.java +++ b/src/main/java/forge/quest/QuestController.java @@ -29,7 +29,6 @@ import forge.quest.data.GameFormatQuest; import forge.game.GameFormat; import forge.game.event.Event; import forge.game.event.MulliganEvent; -import forge.game.player.PlayerType; import forge.item.CardPrinted; import forge.item.PreconDeck; import forge.properties.ForgeProps; @@ -446,7 +445,8 @@ public class QuestController { if ( ev instanceof MulliganEvent ) { MulliganEvent mev = (MulliganEvent)ev; // First mulligan is free - if (mev.player.getType() == PlayerType.HUMAN && getAssets().hasItem(QuestItemType.SLEIGHT) && mev.player.getStats().getMulliganCount() == 0) { + if (mev.player.getLobbyPlayer() == Singletons.getControl().getLobby().getQuestPlayer() + && getAssets().hasItem(QuestItemType.SLEIGHT) && mev.player.getStats().getMulliganCount() == 0) { mev.player.drawCard(); } } From c195e944daec36ada3080e0daa51b10f7ea45ec2 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 22:16:26 +0000 Subject: [PATCH 08/11] balance hands written in less lines --- .../forge/card/cardfactory/CardFactorySorceries.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/main/java/forge/card/cardfactory/CardFactorySorceries.java b/src/main/java/forge/card/cardfactory/CardFactorySorceries.java index c9f451986f7..ac3930ad7cc 100644 --- a/src/main/java/forge/card/cardfactory/CardFactorySorceries.java +++ b/src/main/java/forge/card/cardfactory/CardFactorySorceries.java @@ -462,16 +462,9 @@ public class CardFactorySorceries { } private static final void balanceHands(Spell card) { - - List> hands = new ArrayList>(); - for (Player p : Singletons.getModel().getGame().getPlayers()) { - - hands.add(p.getCardsIn(ZoneType.Hand)); - } int min = Integer.MAX_VALUE; - for (List h : hands) { - int s = h.size(); - min = Math.min(min, s); + for (Player p : Singletons.getModel().getGame().getPlayers()) { + min = Math.min(min, p.getZone(ZoneType.Hand).size()); } for (Player p : Singletons.getModel().getGame().getPlayers()) { From 74b2d09f4192e777d5324ca750df68bbadb80c1d Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 22:22:27 +0000 Subject: [PATCH 09/11] made a base class for quest/QuestRewardCardChooser and quest/QuestRewardCardFiltered to dump duplicate code there. --- .gitattributes | 1 + .../java/forge/quest/QuestRewardCard.java | 134 ++++++++++++++++++ .../forge/quest/QuestRewardCardChooser.java | 119 +--------------- .../forge/quest/QuestRewardCardFiltered.java | 110 +------------- 4 files changed, 138 insertions(+), 226 deletions(-) create mode 100644 src/main/java/forge/quest/QuestRewardCard.java diff --git a/.gitattributes b/.gitattributes index 54fd9d0805e..bb73b93041f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14508,6 +14508,7 @@ src/main/java/forge/quest/QuestEventDifficulty.java -text src/main/java/forge/quest/QuestEventDuel.java svneol=native#text/plain src/main/java/forge/quest/QuestEventManager.java svneol=native#text/plain src/main/java/forge/quest/QuestMode.java -text +src/main/java/forge/quest/QuestRewardCard.java -text src/main/java/forge/quest/QuestRewardCardDuplicate.java -text src/main/java/forge/quest/QuestRewardCardFiltered.java -text src/main/java/forge/quest/QuestUtil.java svneol=native#text/plain diff --git a/src/main/java/forge/quest/QuestRewardCard.java b/src/main/java/forge/quest/QuestRewardCard.java new file mode 100644 index 00000000000..3d08865acc1 --- /dev/null +++ b/src/main/java/forge/quest/QuestRewardCard.java @@ -0,0 +1,134 @@ +package forge.quest; + +import java.util.ArrayList; +import java.util.List; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; + +import forge.Singletons; +import forge.card.CardRules; +import forge.item.CardPrinted; +import forge.item.IPaperCard; +import forge.item.InventoryItem; + +/** + * TODO: Write javadoc for this type. + * + */ +public abstract class QuestRewardCard implements InventoryItem { + + protected String buildDescription(final String [] input) { + final String defaultDescription = "a card"; + if (input == null || input.length < 1) { + return defaultDescription; + } + + String buildDesc = null; + + for (String s : input) { + if (s.startsWith("desc:") || s.startsWith("Desc:")) { + String[] tmp = s.split(":"); + if (tmp.length > 1) { + buildDesc = new String(tmp[1]); + } else { + buildDesc = new String(); + } + } else if (buildDesc != null) { + if (s.contains(":")) { + return buildDesc; + } else { + buildDesc = buildDesc + " " + s; + } + } + } + + if (buildDesc != null) { + return buildDesc; + } + return defaultDescription; + } + + protected Predicate buildPredicates(final String [] input) { + if (input == null || input.length < 1) { + return null; + } + + Predicate filters = Singletons.getModel().getQuest().getFormat().getFilterPrinted(); + Predicate filterRules = null; + Predicate filterRarity = null; + + for (String s : input) { + if (s.startsWith("sets:") || s.startsWith("Sets:")) { + final String[] tmp = s.split(":"); + if (tmp.length > 1) { + String [] setcodes = tmp[1].split(","); + if (setcodes.length > 0) { + List sets = new ArrayList(); + for (String code : setcodes) { + if (Singletons.getModel().getEditions().contains(code)) { + // System.out.println("Set " + code + " was found!"); + sets.add(code); + } + // else { System.out.println("Unknown set code " + code); } + } + if (sets.size() > 0) { + filters = IPaperCard.Predicates.printedInSets(sets, true); + } + } + } + } else if (s.startsWith("rules:") || s.startsWith("Rules:")) { + final String[] tmp = s.split(":"); + if (tmp.length > 1) { + String [] ruleCodes = tmp[1].split(","); + if (ruleCodes.length > 0) { + for (String rule : ruleCodes) { + final Predicate newRule = BoosterUtils.parseRulesLimitation(rule); + if (newRule != null) { + filterRules = (filterRules == null ? newRule : Predicates.and(filterRules, newRule)); + } + } + } + } + } else if (s.startsWith("rarity:") || s.startsWith("Rarity:")) { + final String[] tmp = s.split(":"); + if (tmp.length > 1) { + String [] rarityCodes = tmp[1].split(","); + if (rarityCodes.length > 0) { + for (String rarity : rarityCodes) { + if (rarity.startsWith("C") || rarity.startsWith("c")) { + filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_COMMON : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_COMMON)); + } else if (rarity.startsWith("U") || rarity.startsWith("u")) { + filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_UNCOMMON : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_UNCOMMON)); + } else if (rarity.startsWith("R") || rarity.startsWith("r")) { + filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_RARE : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_RARE)); + } else if (rarity.startsWith("M") || rarity.startsWith("m")) { + filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_MYTHIC_RARE : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_MYTHIC_RARE)); + } + } + } + } + } + } + + if (filterRules != null) { + final Predicate rulesPrinted = Predicates.compose(filterRules, CardPrinted.FN_GET_RULES); + filters = Predicates.and(filters, rulesPrinted); + } + if (filterRarity != null) { + filters = Predicates.and(filters, filterRarity); + } + return filters; + } + + /** + * A QuestRewardCardChooser ought to always be resolved to an actual card, hence no images. + * + * @return an empty string + */ + @Override + public String getImageFilename() { + return ""; + } + +} \ No newline at end of file diff --git a/src/main/java/forge/quest/QuestRewardCardChooser.java b/src/main/java/forge/quest/QuestRewardCardChooser.java index 2f31e4cf0ba..8f787742daa 100644 --- a/src/main/java/forge/quest/QuestRewardCardChooser.java +++ b/src/main/java/forge/quest/QuestRewardCardChooser.java @@ -1,7 +1,6 @@ package forge.quest; import com.google.common.base.Predicate; -import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import java.util.ArrayList; @@ -12,8 +11,6 @@ import java.util.Map; import forge.Singletons; import forge.item.CardDb; import forge.item.CardPrinted; -import forge.item.IPaperCard; -import forge.card.CardRules; import forge.item.InventoryItem; import forge.item.ItemPool; @@ -22,7 +19,7 @@ import forge.item.ItemPool; * The initial version includes "duplicate", other type may be added later. * */ -public class QuestRewardCardChooser implements InventoryItem { +public class QuestRewardCardChooser extends QuestRewardCard implements InventoryItem { /** * Possible types for this object. @@ -55,110 +52,6 @@ public class QuestRewardCardChooser implements InventoryItem { } } - private String buildDescription(final String [] input) { - final String defaultDescription = "a card"; - if (input == null || input.length < 1) { - return defaultDescription; - } - - String buildDesc = null; - - for (String s : input) { - if (s.startsWith("desc:") || s.startsWith("Desc:")) { - String[] tmp = s.split(":"); - if (tmp.length > 1) { - buildDesc = new String(tmp[1]); - } else { - buildDesc = new String(); - } - } else if (buildDesc != null) { - if (s.contains(":")) { - return buildDesc; - } else { - buildDesc = buildDesc + " " + s; - } - } - } - - if (buildDesc != null) { - return buildDesc; - } - return defaultDescription; - } - - - private Predicate buildPredicates(final String [] input) { - if (input == null || input.length < 1) { - return null; - } - - Predicate filters = Singletons.getModel().getQuest().getFormat().getFilterPrinted(); - Predicate filterRules = null; - Predicate filterRarity = null; - - for (String s : input) { - if (s.startsWith("sets:") || s.startsWith("Sets:")) { - final String[] tmp = s.split(":"); - if (tmp.length > 1) { - String [] setcodes = tmp[1].split(","); - if (setcodes.length > 0) { - List sets = new ArrayList(); - for (String code : setcodes) { - if (Singletons.getModel().getEditions().contains(code)) { - // System.out.println("Set " + code + " was found!"); - sets.add(code); - } - // else { System.out.println("Unknown set code " + code); } - } - if (sets.size() > 0) { - filters = IPaperCard.Predicates.printedInSets(sets, true); - } - } - } - } else if (s.startsWith("rules:") || s.startsWith("Rules:")) { - final String[] tmp = s.split(":"); - if (tmp.length > 1) { - String [] ruleCodes = tmp[1].split(","); - if (ruleCodes.length > 0) { - for (String rule : ruleCodes) { - final Predicate newRule = BoosterUtils.parseRulesLimitation(rule); - if (newRule != null) { - filterRules = (filterRules == null ? newRule : Predicates.and(filterRules, newRule)); - } - } - } - } - } else if (s.startsWith("rarity:") || s.startsWith("Rarity:")) { - final String[] tmp = s.split(":"); - if (tmp.length > 1) { - String [] rarityCodes = tmp[1].split(","); - if (rarityCodes.length > 0) { - for (String rarity : rarityCodes) { - if (rarity.startsWith("C") || rarity.startsWith("c")) { - filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_COMMON : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_COMMON)); - } else if (rarity.startsWith("U") || rarity.startsWith("u")) { - filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_UNCOMMON : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_UNCOMMON)); - } else if (rarity.startsWith("R") || rarity.startsWith("r")) { - filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_RARE : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_RARE)); - } else if (rarity.startsWith("M") || rarity.startsWith("m")) { - filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_MYTHIC_RARE : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_MYTHIC_RARE)); - } - } - } - } - } - } - - if (filterRules != null) { - final Predicate rulesPrinted = Predicates.compose(filterRules, CardPrinted.FN_GET_RULES); - filters = Predicates.and(filters, rulesPrinted); - } - if (filterRarity != null) { - filters = Predicates.and(filters, filterRarity); - } - return filters; - } - /** * The name. * @@ -169,16 +62,6 @@ public class QuestRewardCardChooser implements InventoryItem { return description; } - /** - * A QuestRewardCardChooser ought to always be resolved to an actual card, hence no images. - * - * @return an empty string - */ - @Override - public String getImageFilename() { - return ""; - } - /** * The item type. * diff --git a/src/main/java/forge/quest/QuestRewardCardFiltered.java b/src/main/java/forge/quest/QuestRewardCardFiltered.java index b80b18bbfb8..5cee1b0ca3b 100644 --- a/src/main/java/forge/quest/QuestRewardCardFiltered.java +++ b/src/main/java/forge/quest/QuestRewardCardFiltered.java @@ -5,20 +5,16 @@ import java.util.Collections; import java.util.List; import com.google.common.base.Predicate; -import com.google.common.base.Predicates; import com.google.common.collect.Iterables; -import forge.Singletons; -import forge.card.CardRules; import forge.item.CardDb; import forge.item.CardPrinted; -import forge.item.IPaperCard; /** * Allows the player to choose a card from a predicate-filtered list of cards. * */ -public class QuestRewardCardFiltered implements IQuestRewardCard { +public class QuestRewardCardFiltered extends QuestRewardCard implements IQuestRewardCard { private final String description; private final Predicate predicates; @@ -33,109 +29,7 @@ public class QuestRewardCardFiltered implements IQuestRewardCard { } - private String buildDescription(final String [] input) { - final String defaultDescription = "a card"; - if (input == null || input.length < 1) { - return defaultDescription; - } - - String buildDesc = null; - - for (String s : input) { - if (s.startsWith("desc:") || s.startsWith("Desc:")) { - String[] tmp = s.split(":"); - if (tmp.length > 1) { - buildDesc = new String(tmp[1]); - } else { - buildDesc = new String(); - } - } else if (buildDesc != null) { - if (s.contains(":")) { - return buildDesc; - } else { - buildDesc = buildDesc + " " + s; - } - } - } - if (buildDesc != null) { - return buildDesc; - } - return defaultDescription; - } - - private Predicate buildPredicates(final String [] input) { - if (input == null || input.length < 1) { - return null; - } - - Predicate filters = Singletons.getModel().getQuest().getFormat().getFilterPrinted(); - Predicate filterRules = null; - Predicate filterRarity = null; - - for (String s : input) { - if (s.startsWith("sets:") || s.startsWith("Sets:")) { - final String[] tmp = s.split(":"); - if (tmp.length > 1) { - String [] setcodes = tmp[1].split(","); - if (setcodes.length > 0) { - List sets = new ArrayList(); - for (String code : setcodes) { - if (Singletons.getModel().getEditions().contains(code)) { - // System.out.println("Set " + code + " was found!"); - sets.add(code); - } - // else { System.out.println("Unknown set code " + code); } - } - if (sets.size() > 0) { - filters = IPaperCard.Predicates.printedInSets(sets, true); - } - } - } - } else if (s.startsWith("rules:") || s.startsWith("Rules:")) { - final String[] tmp = s.split(":"); - if (tmp.length > 1) { - String [] ruleCodes = tmp[1].split(","); - if (ruleCodes.length > 0) { - for (String rule : ruleCodes) { - final Predicate newRule = BoosterUtils.parseRulesLimitation(rule); - if (newRule != null) { - filterRules = (filterRules == null ? newRule : Predicates.and(filterRules, newRule)); - } - } - } - } - } else if (s.startsWith("rarity:") || s.startsWith("Rarity:")) { - final String[] tmp = s.split(":"); - if (tmp.length > 1) { - String [] rarityCodes = tmp[1].split(","); - if (rarityCodes.length > 0) { - for (String rarity : rarityCodes) { - if (rarity.startsWith("C") || rarity.startsWith("c")) { - filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_COMMON : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_COMMON)); - } else if (rarity.startsWith("U") || rarity.startsWith("u")) { - filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_UNCOMMON : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_UNCOMMON)); - } else if (rarity.startsWith("R") || rarity.startsWith("r")) { - filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_RARE : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_RARE)); - } else if (rarity.startsWith("M") || rarity.startsWith("m")) { - filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_MYTHIC_RARE : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_MYTHIC_RARE)); - } - } - } - } - } - } - - if (filterRules != null) { - final Predicate rulesPrinted = Predicates.compose(filterRules, CardPrinted.FN_GET_RULES); - filters = Predicates.and(filters, rulesPrinted); - } - if (filterRarity != null) { - filters = Predicates.and(filters, filterRarity); - } - return filters; - } - - /** + /** * The name. * * @return the name From 8dc331ed145b47573ed8dffbac3444b20eccaa95 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 22:25:34 +0000 Subject: [PATCH 10/11] little polishing on QuestRewardCard classes --- src/main/java/forge/quest/QuestRewardCard.java | 4 +++- src/main/java/forge/quest/QuestRewardCardChooser.java | 1 + src/main/java/forge/quest/QuestRewardCardFiltered.java | 10 +--------- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/main/java/forge/quest/QuestRewardCard.java b/src/main/java/forge/quest/QuestRewardCard.java index 3d08865acc1..1ce9004ea0b 100644 --- a/src/main/java/forge/quest/QuestRewardCard.java +++ b/src/main/java/forge/quest/QuestRewardCard.java @@ -16,7 +16,7 @@ import forge.item.InventoryItem; * TODO: Write javadoc for this type. * */ -public abstract class QuestRewardCard implements InventoryItem { +public abstract class QuestRewardCard implements InventoryItem, IQuestRewardCard { protected String buildDescription(final String [] input) { final String defaultDescription = "a card"; @@ -130,5 +130,7 @@ public abstract class QuestRewardCard implements InventoryItem { public String getImageFilename() { return ""; } + + public abstract List getChoices(); } \ No newline at end of file diff --git a/src/main/java/forge/quest/QuestRewardCardChooser.java b/src/main/java/forge/quest/QuestRewardCardChooser.java index 8f787742daa..67aefcd8307 100644 --- a/src/main/java/forge/quest/QuestRewardCardChooser.java +++ b/src/main/java/forge/quest/QuestRewardCardChooser.java @@ -90,6 +90,7 @@ public class QuestRewardCardChooser extends QuestRewardCard implements Inventory * * @return a List or null if could not create a list. */ + @Override public final List getChoices() { if (type == poolType.playerCards) { final ItemPool playerCards = Singletons.getModel().getQuest().getAssets().getCardPool(); diff --git a/src/main/java/forge/quest/QuestRewardCardFiltered.java b/src/main/java/forge/quest/QuestRewardCardFiltered.java index 5cee1b0ca3b..ba34b573027 100644 --- a/src/main/java/forge/quest/QuestRewardCardFiltered.java +++ b/src/main/java/forge/quest/QuestRewardCardFiltered.java @@ -39,15 +39,6 @@ public class QuestRewardCardFiltered extends QuestRewardCard implements IQuestRe return description; } - /** - * This class is a dynamic list of cards, hence no images. - * - * @return an empty string - */ - @Override - public String getImageFilename() { - return ""; - } /** * The item type. @@ -64,6 +55,7 @@ public class QuestRewardCardFiltered extends QuestRewardCard implements IQuestRe * * @return a List or null if could not create a list. */ + @Override public final List getChoices() { List cardChoices = new ArrayList(); for (final CardPrinted card : Iterables.filter(CardDb.instance().getAllCards(), predicates)) { From f22b7aee0f68eae025b5b6eab4fa2ba6333b19e4 Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 23:28:18 +0000 Subject: [PATCH 11/11] - Rewrote the card controller system. - Converted Control Magic auras to continuous static abilities. --- res/cardsfolder/a/abduction.txt | 4 +- res/cardsfolder/a/annex.txt | 2 +- res/cardsfolder/b/biting_tether.txt | 3 +- res/cardsfolder/c/confiscate.txt | 3 +- res/cardsfolder/c/conquer.txt | 2 +- res/cardsfolder/c/control_magic.txt | 2 +- res/cardsfolder/c/corrupted_conscience.txt | 2 +- res/cardsfolder/d/domestication.txt | 3 +- res/cardsfolder/d/dominating_licid.txt | 2 +- res/cardsfolder/d/domineer.txt | 2 +- res/cardsfolder/e/enslave.txt | 2 +- res/cardsfolder/k/krovikan_whispers.txt | 2 +- res/cardsfolder/m/mark_of_the_oni.txt | 2 +- res/cardsfolder/m/mind_control.txt | 3 +- res/cardsfolder/m/mind_harness.txt | 3 +- res/cardsfolder/p/persuasion.txt | 3 +- res/cardsfolder/s/soul_ransom.txt | 2 +- .../s/soul_seizer_ghastly_haunting.txt | 2 +- res/cardsfolder/s/spirit_away.txt | 2 +- res/cardsfolder/s/steal_artifact.txt | 2 +- res/cardsfolder/s/steal_enchantment.txt | 2 +- res/cardsfolder/t/take_possession.txt | 2 +- res/cardsfolder/t/treachery.txt | 2 +- res/cardsfolder/v/vapor_snare.txt | 2 +- res/cardsfolder/v/volition_reins.txt | 6 +- res/cardsfolder/y/yavimayas_embrace.txt | 3 +- src/main/java/forge/Card.java | 131 +++++------------- src/main/java/forge/CardUtil.java | 2 +- src/main/java/forge/StaticEffects.java | 5 + .../forge/card/ability/ai/ChangeZoneAi.java | 2 +- .../card/ability/effects/AttachEffect.java | 85 ++---------- .../ability/effects/ChangeZoneAllEffect.java | 2 +- .../ability/effects/ChangeZoneEffect.java | 4 +- .../effects/ControlExchangeEffect.java | 8 +- .../ability/effects/ControlGainEffect.java | 71 ++++------ .../ability/effects/CopyPermanentEffect.java | 4 +- .../card/ability/effects/CounterEffect.java | 4 +- .../card/ability/effects/DigUntilEffect.java | 2 +- .../effects/PermanentCreatureEfect.java | 2 +- .../effects/PermanentNoncreatureEffect.java | 2 +- .../card/spellability/SpellPermanent.java | 2 +- .../card/staticability/StaticAbility.java | 4 + .../StaticAbilityContinuous.java | 5 + src/main/java/forge/game/GameAction.java | 14 +- .../java/forge/game/phase/PhaseHandler.java | 3 +- src/main/java/forge/game/player/Player.java | 2 +- 46 files changed, 146 insertions(+), 273 deletions(-) diff --git a/res/cardsfolder/a/abduction.txt b/res/cardsfolder/a/abduction.txt index 6382acbb62a..9aedd975c2a 100644 --- a/res/cardsfolder/a/abduction.txt +++ b/res/cardsfolder/a/abduction.txt @@ -1,11 +1,11 @@ Name:Abduction ManaCost:2 U U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 2 U U | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigUntap | TriggerDescription$ When CARDNAME enters the battlefield, untap enchanted creature. -SVar:TrigUntap:AB$Untap | Cost$ 0 | Defined$ Enchanted +SVar:TrigUntap:AB$ Untap | Cost$ 0 | Defined$ Enchanted T:Mode$ ChangesZone | ValidCard$ Card.AttachedBy | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigReturnOwner | TriggerDescription$ When enchanted creature dies, return that card to the battlefield under its owner's control. SVar:TrigReturnOwner:AB$ ChangeZone | Cost$ 0 | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Battlefield SVar:PlayMain1:TRUE diff --git a/res/cardsfolder/a/annex.txt b/res/cardsfolder/a/annex.txt index 85dbbc1b2ac..c44bb944ac3 100644 --- a/res/cardsfolder/a/annex.txt +++ b/res/cardsfolder/a/annex.txt @@ -1,9 +1,9 @@ Name:Annex ManaCost:2 U U Types:Enchantment Aura -Text:You control enchanted land. K:Enchant land A:SP$ Attach | Cost$ 2 U U | ValidTgts$ Land | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Land.EnchantedBy | GainControl$ You | Description$ You control enchanted land. SVar:Picture:http://www.wizards.com/global/images/magic/general/annex.jpg Oracle:Enchant land (Target a land as you cast this. This card enters the battlefield attached to that land.)\nYou control enchanted land. SetInfo:ONS Uncommon diff --git a/res/cardsfolder/b/biting_tether.txt b/res/cardsfolder/b/biting_tether.txt index 287c365c56d..21513329dbe 100644 --- a/res/cardsfolder/b/biting_tether.txt +++ b/res/cardsfolder/b/biting_tether.txt @@ -1,12 +1,11 @@ Name:Biting Tether ManaCost:4 U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 4 U | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, put a -1/-1 counter on enchanted creature. SVar:TrigPutCounter:AB$ PutCounter | Cost$ 0 | Defined$ Enchanted | CounterType$ M1M1 | CounterNum$ 1 -SVar:PlayMain1:TRUE SVar:Picture:http://www.wizards.com/global/images/magic/general/biting_tether.jpg Oracle:Enchant creature\nYou control enchanted creature.\nAt the beginning of your upkeep, put a -1/-1 counter on enchanted creature. SetInfo:SHM Uncommon \ No newline at end of file diff --git a/res/cardsfolder/c/confiscate.txt b/res/cardsfolder/c/confiscate.txt index e763e914be2..e5924f3e69e 100644 --- a/res/cardsfolder/c/confiscate.txt +++ b/res/cardsfolder/c/confiscate.txt @@ -1,10 +1,9 @@ Name:Confiscate ManaCost:4 U U Types:Enchantment Aura -Text:You control enchanted permanent. K:Enchant permanent A:SP$ Attach | Cost$ 4 U U | ValidTgts$ Permanent | AILogic$ GainControl -SVar:PlayMain1:TRUE +S:Mode$ Continuous | Affected$ Permanent.EnchantedBy | GainControl$ You | Description$ You control enchanted permanent. SVar:Picture:http://www.wizards.com/global/images/magic/general/confiscate.jpg Oracle:Enchant permanent (Target a permanent as you cast this. This card enters the battlefield attached to that permanent.)\nYou control enchanted permanent. SetInfo:8ED Uncommon diff --git a/res/cardsfolder/c/conquer.txt b/res/cardsfolder/c/conquer.txt index d680aabc0f0..3bc1d118131 100644 --- a/res/cardsfolder/c/conquer.txt +++ b/res/cardsfolder/c/conquer.txt @@ -1,9 +1,9 @@ Name:Conquer ManaCost:3 R R Types:Enchantment Aura -Text:You control enchanted land. K:Enchant land A:SP$ Attach | Cost$ 3 R R | ValidTgts$ Land | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Land.EnchantedBy | GainControl$ You | Description$ You control enchanted land. SVar:Picture:http://www.wizards.com/global/images/magic/general/conquer.jpg Oracle:Enchant land\nYou control enchanted land. SetInfo:ICE Uncommon diff --git a/res/cardsfolder/c/control_magic.txt b/res/cardsfolder/c/control_magic.txt index de68fa7c885..995a3ffeb70 100644 --- a/res/cardsfolder/c/control_magic.txt +++ b/res/cardsfolder/c/control_magic.txt @@ -1,9 +1,9 @@ Name:Control Magic ManaCost:2 U U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 2 U U | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. SVar:Picture:http://www.wizards.com/global/images/magic/general/control_magic.jpg Oracle:Enchant creature\nYou control enchanted creature. SetInfo:2ED Uncommon diff --git a/res/cardsfolder/c/corrupted_conscience.txt b/res/cardsfolder/c/corrupted_conscience.txt index 890269de26c..5337d528fff 100644 --- a/res/cardsfolder/c/corrupted_conscience.txt +++ b/res/cardsfolder/c/corrupted_conscience.txt @@ -1,9 +1,9 @@ Name:Corrupted Conscience ManaCost:3 U U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 3 U U | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddKeyword$ Infect | Description$ Enchanted creature has infect. (It deals damage to creatures in the form of -1/-1 counters and to players in the form of poison counters.) SVar:Picture:http://www.wizards.com/global/images/magic/general/corrupted_conscience.jpg Oracle:Enchant creature\nYou control enchanted creature.\nEnchanted creature has infect. (It deals damage to creatures in the form of -1/-1 counters and to players in the form of poison counters.) diff --git a/res/cardsfolder/d/domestication.txt b/res/cardsfolder/d/domestication.txt index 3083929b897..ff27d436c60 100644 --- a/res/cardsfolder/d/domestication.txt +++ b/res/cardsfolder/d/domestication.txt @@ -1,12 +1,11 @@ Name:Domestication ManaCost:2 U U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 2 U U | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | IsPresent$ Card.AttachedBy+powerGE4 | Execute$ TrigSac | TriggerDescription$ At the beginning of your end step, if enchanted creature's power is 4 or greater, sacrifice CARDNAME. SVar:TrigSac:AB$Sacrifice | Cost$ 0 | Defined$ Self -SVar:PlayMain1:TRUE SVar:Picture:http://www.wizards.com/global/images/magic/general/domestication.jpg Oracle:Enchant creature\nYou control enchanted creature.\nAt the beginning of your end step, if enchanted creature's power is 4 or greater, sacrifice Domestication. SetInfo:ROE Uncommon \ No newline at end of file diff --git a/res/cardsfolder/d/dominating_licid.txt b/res/cardsfolder/d/dominating_licid.txt index e3f7afde804..45f6378c5de 100644 --- a/res/cardsfolder/d/dominating_licid.txt +++ b/res/cardsfolder/d/dominating_licid.txt @@ -16,9 +16,9 @@ Name:Dominating Licid ManaCost:1 U U Colors:blue Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 0 | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. A:AB$ SetState | Cost$ U | Defined$ Self | NewState$ Original | SpellDescription$ End this effect. SVar:RemAIDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/dominating_licid.jpg diff --git a/res/cardsfolder/d/domineer.txt b/res/cardsfolder/d/domineer.txt index 6d4b740e247..052484f0b8a 100644 --- a/res/cardsfolder/d/domineer.txt +++ b/res/cardsfolder/d/domineer.txt @@ -3,7 +3,7 @@ ManaCost:1 U U Types:Enchantment Aura K:Enchant artifact creature A:SP$ Attach | Cost$ 1 U U | ValidTgts$ Creature.Artifact | AILogic$ GainControl | SpellDescription$ You control enchanted artifact creature. -SVar:PlayMain1:TRUE +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You SVar:Picture:http://www.wizards.com/global/images/magic/general/domineer.jpg Oracle:Enchant artifact creature\nYou control enchanted artifact creature. SetInfo:MRD Uncommon \ No newline at end of file diff --git a/res/cardsfolder/e/enslave.txt b/res/cardsfolder/e/enslave.txt index 35f4bb50deb..90417d78c37 100644 --- a/res/cardsfolder/e/enslave.txt +++ b/res/cardsfolder/e/enslave.txt @@ -1,9 +1,9 @@ Name:Enslave ManaCost:4 B B Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 4 B B | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDamage | TriggerDescription$ At the beginning of your upkeep, enchanted creature deals 1 damage to its owner. SVar:TrigDamage:AB$DealDamage | Cost$ 0 | Defined$ EnchantedOwner | DamageSource$ Enchanted | NumDmg$ 1 SVar:PlayMain1:TRUE diff --git a/res/cardsfolder/k/krovikan_whispers.txt b/res/cardsfolder/k/krovikan_whispers.txt index 86f0da149b0..e4296e7ca64 100644 --- a/res/cardsfolder/k/krovikan_whispers.txt +++ b/res/cardsfolder/k/krovikan_whispers.txt @@ -1,10 +1,10 @@ Name:Krovikan Whispers ManaCost:3 U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature K:Cumulative upkeep:UB A:SP$ Attach | Cost$ 3 U | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigLoseLife | ValidCard$ Card.Self | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME is put into a graveyard from the battlefield, you lose 2 life for each age counter on it. SVar:TrigLoseLife:AB$ LoseLife | Cost$ 0 | LifeAmount$ X | References$ X SVar:X:TriggeredCard$CardCounters.AGE/Times.2 diff --git a/res/cardsfolder/m/mark_of_the_oni.txt b/res/cardsfolder/m/mark_of_the_oni.txt index 1d4cbdbc6ea..57bbff928d8 100644 --- a/res/cardsfolder/m/mark_of_the_oni.txt +++ b/res/cardsfolder/m/mark_of_the_oni.txt @@ -1,9 +1,9 @@ Name:Mark of the Oni ManaCost:2 B Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 2 B | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | IsPresent$ Demon.YouCtrl | PresentCompare$ EQ0 | Execute$ TrigSac | TriggerDescription$ At the beginning of the end step, if you control no Demons, sacrifice CARDNAME. SVar:TrigSac:AB$Sacrifice | Cost$ 0 | Defined$ Self SVar:PlayMain1:TRUE diff --git a/res/cardsfolder/m/mind_control.txt b/res/cardsfolder/m/mind_control.txt index a5dc85bd311..62f6651d515 100644 --- a/res/cardsfolder/m/mind_control.txt +++ b/res/cardsfolder/m/mind_control.txt @@ -1,10 +1,9 @@ Name:Mind Control ManaCost:3 U U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 3 U U | ValidTgts$ Creature | AILogic$ GainControl -SVar:PlayMain1:TRUE +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. SVar:Picture:http://www.wizards.com/global/images/magic/general/mind_control.jpg Oracle:Enchant creature\nYou control enchanted creature. SetInfo:M11 Uncommon diff --git a/res/cardsfolder/m/mind_harness.txt b/res/cardsfolder/m/mind_harness.txt index 950ac65903b..3665d1552ea 100644 --- a/res/cardsfolder/m/mind_harness.txt +++ b/res/cardsfolder/m/mind_harness.txt @@ -1,11 +1,10 @@ Name:Mind Harness ManaCost:U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant red or green creature K:Cumulative upkeep:1 A:SP$ Attach | Cost$ U | ValidTgts$ Creature.Red,Creature.Green | TgtPrompt$ Select target Red or Green Creature | AILogic$ GainControl -SVar:PlayMain1:TRUE +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. SVar:RemRandomDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/mind_harness.jpg Oracle:Enchant red or green creature\nCumulative upkeep {1} (At the beginning of your upkeep, put an age counter on this permanent, then sacrifice it unless you pay its upkeep cost for each age counter on it.)\nYou control enchanted creature. diff --git a/res/cardsfolder/p/persuasion.txt b/res/cardsfolder/p/persuasion.txt index 435ddca5b63..fc1f5842112 100644 --- a/res/cardsfolder/p/persuasion.txt +++ b/res/cardsfolder/p/persuasion.txt @@ -1,10 +1,9 @@ Name:Persuasion ManaCost:3 U U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 3 U U | ValidTgts$ Creature | AILogic$ GainControl -SVar:PlayMain1:TRUE +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. SVar:Picture:http://www.wizards.com/global/images/magic/general/persuasion.jpg Oracle:Enchant creature (Target a creature as you cast this. This card enters the battlefield attached to that creature.)\nYou control enchanted creature. SetInfo:ODY Rare diff --git a/res/cardsfolder/s/soul_ransom.txt b/res/cardsfolder/s/soul_ransom.txt index 5b69421367f..5bd62685d34 100644 --- a/res/cardsfolder/s/soul_ransom.txt +++ b/res/cardsfolder/s/soul_ransom.txt @@ -1,9 +1,9 @@ Name:Soul Ransom ManaCost:2 U B Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 2 U B | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. A:AB$ Sacrifice | Cost$ Discard<2/Card> | AnyOpponent$ True | SacValid$ Self | SubAbility$ DBDraw | SpellDescription$ CARDNAME's controller sacrifices it, then draws two cards. Only any opponent may activate this ability. SVar:DBDraw:DB$ Draw | NumCards$ 2 | Defined$ SourceController SVar:Picture:http://www.wizards.com/global/images/magic/general/soul_ransom.jpg diff --git a/res/cardsfolder/s/soul_seizer_ghastly_haunting.txt b/res/cardsfolder/s/soul_seizer_ghastly_haunting.txt index a7988a64ccb..451e5bcee1c 100644 --- a/res/cardsfolder/s/soul_seizer_ghastly_haunting.txt +++ b/res/cardsfolder/s/soul_seizer_ghastly_haunting.txt @@ -16,9 +16,9 @@ Name:Ghastly Haunting ManaCost:no cost Colors:blue Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 0 | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. SVar:RemAIDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/ghastly_haunting.jpg Oracle:Enchant creature\nYou control enchanted creature. diff --git a/res/cardsfolder/s/spirit_away.txt b/res/cardsfolder/s/spirit_away.txt index bb5c40d8b5b..3e5eec862e6 100644 --- a/res/cardsfolder/s/spirit_away.txt +++ b/res/cardsfolder/s/spirit_away.txt @@ -1,9 +1,9 @@ Name:Spirit Away ManaCost:5 U U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 5 U U | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddToughness$ 2 | AddKeyword$ Flying | Description$ Enchanted creature gets +2/+2 and has flying. SVar:Picture:http://www.wizards.com/global/images/magic/general/spirit_away.jpg Oracle:Enchant creature\nYou control enchanted creature.\nEnchanted creature gets +2/+2 and has flying. diff --git a/res/cardsfolder/s/steal_artifact.txt b/res/cardsfolder/s/steal_artifact.txt index 87fd74970a7..c6b3fbc56a9 100644 --- a/res/cardsfolder/s/steal_artifact.txt +++ b/res/cardsfolder/s/steal_artifact.txt @@ -1,9 +1,9 @@ Name:Steal Artifact ManaCost:2 U U Types:Enchantment Aura -Text:You control enchanted artifact. K:Enchant artifact A:SP$ Attach | Cost$ 2 U U | ValidTgts$ Artifact | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Artifact.EnchantedBy | GainControl$ You | Description$ You control enchanted artifact. SVar:Picture:http://www.wizards.com/global/images/magic/general/steal_artifact.jpg Oracle:Enchant artifact\nYou control enchanted artifact. SetInfo:8ED Uncommon diff --git a/res/cardsfolder/s/steal_enchantment.txt b/res/cardsfolder/s/steal_enchantment.txt index 15b7151fe63..9d2b0740927 100644 --- a/res/cardsfolder/s/steal_enchantment.txt +++ b/res/cardsfolder/s/steal_enchantment.txt @@ -1,9 +1,9 @@ Name:Steal Enchantment ManaCost:U U Types:Enchantment Aura -Text:You control enchanted enchantment. K:Enchant enchantment A:SP$ Attach | Cost$ U U | ValidTgts$ Enchantment | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Enchantment.EnchantedBy | GainControl$ You | Description$ You control enchanted enchantment. SVar:RemAIDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/steal_enchantment.jpg Oracle:Enchant enchantment\nYou control enchanted enchantment. diff --git a/res/cardsfolder/t/take_possession.txt b/res/cardsfolder/t/take_possession.txt index 25bfbea6087..40218e8f5d7 100644 --- a/res/cardsfolder/t/take_possession.txt +++ b/res/cardsfolder/t/take_possession.txt @@ -1,9 +1,9 @@ Name:Take Possession ManaCost:5 U U Types:Enchantment Aura -Text:You control enchanted permanent. K:Split second K:Enchant permanent +S:Mode$ Continuous | Affected$ Permanent.EnchantedBy | GainControl$ You | Description$ You control enchanted permanent. A:SP$ Attach | Cost$ 5 U U | ValidTgts$ Permanent | AILogic$ GainControl SVar:PlayMain1:TRUE SVar:Picture:http://resources.wizards.com/magic/cards/fut/en-us/card136199.jpg diff --git a/res/cardsfolder/t/treachery.txt b/res/cardsfolder/t/treachery.txt index bdc2c68e321..7c38ebe4bc9 100644 --- a/res/cardsfolder/t/treachery.txt +++ b/res/cardsfolder/t/treachery.txt @@ -1,9 +1,9 @@ Name:Treachery ManaCost:3 U U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 3 U U | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigUntap | TriggerDescription$ When CARDNAME enters the battlefield, untap up to five lands. SVar:TrigUntap:AB$ Untap | Cost$ 0 | UntapUpTo$ True | UntapType$ Land | Amount$ 5 SVar:PlayMain1:TRUE diff --git a/res/cardsfolder/v/vapor_snare.txt b/res/cardsfolder/v/vapor_snare.txt index 6096ea606f9..fabe998c0e5 100644 --- a/res/cardsfolder/v/vapor_snare.txt +++ b/res/cardsfolder/v/vapor_snare.txt @@ -1,9 +1,9 @@ Name:Vapor Snare ManaCost:4 U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 4 U | ValidTgts$ Creature | AILogic$ GainControl +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigChangeZone | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, sacrifice CARDNAME unless you return a land you control to its owner's hand. SVar:TrigChangeZone:AB$ ChangeZone | Cost$ 0 | Origin$ Battlefield | Destination$ Hand | Hidden$ True | ChangeType$ Land.YouCtrl | RememberChanged$ True | SubAbility$ DBSac SVar:DBSac:DB$ Sacrifice | Cost$ 0 | Defined$ Self | SubAbility$ DBCleanup | ConditionCheckSVar$ X | ConditionSVarCompare$ LT1 diff --git a/res/cardsfolder/v/volition_reins.txt b/res/cardsfolder/v/volition_reins.txt index 899a1da1bf3..5afad2f168e 100644 --- a/res/cardsfolder/v/volition_reins.txt +++ b/res/cardsfolder/v/volition_reins.txt @@ -1,11 +1,11 @@ Name:Volition Reins ManaCost:3 U U U Types:Enchantment Aura -Text:You control enchanted permanent. K:Enchant permanent A:SP$ Attach | Cost$ 3 U U U | ValidTgts$ Permanent | AILogic$ GainControl -T:Mode$ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigUntap | TriggerDescription$ When CARDNAME enters the battlefield, if enchanted permanent is tapped, untap it. -SVar:TrigUntap:AB$Untap | Cost$ 0 | Defined$ Enchanted +S:Mode$ Continuous | Affected$ Permanent.EnchantedBy | GainControl$ You | Description$ You control enchanted permanent. +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigUntap | TriggerDescription$ When CARDNAME enters the battlefield, if enchanted permanent is tapped, untap it. +SVar:TrigUntap:AB$ Untap | Cost$ 0 | Defined$ Enchanted SVar:PlayMain1:TRUE SVar:Picture:http://www.wizards.com/global/images/magic/general/volition_reins.jpg Oracle:Enchant permanent\nWhen Volition Reins enters the battlefield, if enchanted permanent is tapped, untap it.\nYou control enchanted permanent. diff --git a/res/cardsfolder/y/yavimayas_embrace.txt b/res/cardsfolder/y/yavimayas_embrace.txt index f5954fa094a..919210ca5f4 100644 --- a/res/cardsfolder/y/yavimayas_embrace.txt +++ b/res/cardsfolder/y/yavimayas_embrace.txt @@ -1,11 +1,10 @@ Name:Yavimaya's Embrace ManaCost:5 G U U Types:Enchantment Aura -Text:You control enchanted creature. K:Enchant creature A:SP$ Attach | Cost$ 5 G U U | ValidTgts$ Creature | AILogic$ GainControl S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddToughness$ 2 | AddKeyword$ Trample | Description$ Enchanted creature gets +2/+2 and has trample. -SVar:PlayMain1:TRUE +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | GainControl$ You | Description$ You control enchanted creature. SVar:Picture:http://www.wizards.com/global/images/magic/general/yavimayas_embrace.jpg Oracle:Enchant creature\nYou control enchanted creature.\nEnchanted creature gets +2/+2 and has trample. SetInfo:APC Rare \ No newline at end of file diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index ba81d6d9644..d290434bcb1 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -199,7 +199,9 @@ public class Card extends GameEntity implements Comparable { private String colorsPaid = ""; private Player owner = null; - private ArrayList controllerObjects = new ArrayList(); + private Player controller = null; + private long controllerTimestamp = 0; + private TreeMap tempControllers = new TreeMap(); // private String rarity = ""; private String text = ""; @@ -3522,6 +3524,13 @@ public class Card extends GameEntity implements Comparable { public final void addChangeControllerCommand(final Command c) { this.changeControllerCommandList.add(c); } + + + public final void runChangeControllerCommands() { + for (final Command c : this.changeControllerCommandList) { + c.execute(); + } + } /** *

@@ -3578,98 +3587,41 @@ public class Card extends GameEntity implements Comparable { * @return a {@link forge.game.player.Player} object. */ public final Player getController() { - if (this.controllerObjects.size() == 0) { - return this.owner; + + if (!this.tempControllers.isEmpty()) { + final long lastTimestamp = this.tempControllers.lastKey(); + if (lastTimestamp > this.controllerTimestamp) { + return this.tempControllers.lastEntry().getValue(); + } } - final Object topController = this.controllerObjects.get(this.controllerObjects.size() - 1); - if (topController instanceof Player) { - return (Player) topController; + if (this.controller != null) { + return this.controller; } - return ((Card) topController).getController(); + return this.owner; } - /** - * - * TODO Write javadoc for this method. - * - * @param controllerObject - * an Object - */ - public final void addController(final Object controllerObject) { - final Object prevController = this.controllerObjects.size() == 0 ? this.owner : this.controllerObjects - .get(this.controllerObjects.size() - 1); - if (!controllerObject.equals(prevController)) { - if (controllerObject instanceof Player) { - for (int i = 0; i < this.controllerObjects.size(); i++) { - if (this.controllerObjects.get(i) instanceof Player) { - this.controllerObjects.remove(i); - } - } - } - this.controllerObjects.add(controllerObject); - if ((Singletons.getModel().getGame().getAction() != null) && (prevController != null)) { - Singletons.getModel().getGame().getAction().controllerChangeZoneCorrection(this); - } - - if (prevController != null) { - for (final Command c : this.changeControllerCommandList) { - c.execute(); - } - } - - this.updateObservers(); - } + public final void addTempController(final Player player, final long tstamp) { + this.tempControllers.put(tstamp, player); } - /** - * - * TODO Write javadoc for this method. - * - * @param controllerObject - * a Object - */ - public final void removeController(final Object controllerObject) { - final Object currentController = this.getController(); - this.controllerObjects.remove(controllerObject); - - if (!currentController.equals(this.getController())) { - Singletons.getModel().getGame().getAction().controllerChangeZoneCorrection(this); - - for (final Command c : this.changeControllerCommandList) { - c.execute(); - } - - this.updateObservers(); - } + public final void removeTempController(final long tstamp) { + this.tempControllers.remove(tstamp); + } + + public final void clearTempControllers() { + this.tempControllers.clear(); + } - /** - * - * TODO Write javadoc for this method. - */ public final void clearControllers() { - this.controllerObjects.clear(); + clearTempControllers(); + this.controller = null; } - /** - * - * TODO Write javadoc for this method. - * - * @return an ArrayList - */ - public final ArrayList getControllerObjects() { - return this.controllerObjects; - } - - /** - * - * TODO Write javadoc for this method. - * - * @param in - * an Object - */ - public final void setControllerObjects(final ArrayList in) { - this.controllerObjects = in; + public final void setController(final Player player, final long tstamp) { + clearTempControllers(); + this.controller = player; + this.controllerTimestamp = tstamp; } /** @@ -3682,23 +3634,8 @@ public class Card extends GameEntity implements Comparable { */ public final void setOwner(final Player player) { this.owner = player; - //this.updateObservers(); } - /** - *

- * Setter for the field controller. - *

- * - * @return the equipped by - */ - /* - * public void setController(Player player) { boolean sameController = - * controller == null ? false : controller.isPlayer(player); controller = - * player; if (null != controller && !sameController) { for (Command var : - * changeControllerCommandList) var.execute(); } this.updateObservers(); } - */ - /** *

* Getter for the field equippedBy. diff --git a/src/main/java/forge/CardUtil.java b/src/main/java/forge/CardUtil.java index e75017d4a14..bc56b34e3df 100644 --- a/src/main/java/forge/CardUtil.java +++ b/src/main/java/forge/CardUtil.java @@ -330,6 +330,7 @@ public final class CardUtil { newCopy.setUniqueNumber(in.getUniqueNumber()); newCopy.setCurSetCode(in.getCurSetCode()); newCopy.setOwner(in.getOwner()); + newCopy.setController(in.getController(), 0); newCopy.setFlipCard(in.isFlipCard()); newCopy.setDoubleFaced(in.isDoubleFaced()); newCopy.getCharacteristics().copy(in.getState(in.getCurState())); @@ -342,7 +343,6 @@ public final class CardUtil { sa.setSourceCard(in); } - newCopy.setControllerObjects(in.getControllerObjects()); newCopy.setCounters(in.getCounters()); newCopy.setExtrinsicKeyword(in.getExtrinsicKeyword()); newCopy.setColor(in.getColor()); diff --git a/src/main/java/forge/StaticEffects.java b/src/main/java/forge/StaticEffects.java index 3f54e1effdb..327e9ad6ac1 100644 --- a/src/main/java/forge/StaticEffects.java +++ b/src/main/java/forge/StaticEffects.java @@ -184,6 +184,11 @@ public class StaticEffects { // modify the affected card for (int i = 0; i < affectedCards.size(); i++) { final Card affectedCard = affectedCards.get(i); + + // Gain control + if (params.containsKey("GainControl")) { + affectedCard.removeTempController(se.getTimestamp()); + } // remove set P/T if (!params.containsKey("CharacteristicDefining") && setPT) { diff --git a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java index 6bc32e6f9a5..f48bfe18a51 100644 --- a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java +++ b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java @@ -1254,7 +1254,7 @@ public class ChangeZoneAi extends SpellAbilityAi { c.setTapped(true); } if (sa.hasParam("GainControl")) { - c.addController(sa.getSourceCard()); + c.setController(sa.getActivatingPlayer(), Singletons.getModel().getGame().getNextTimestamp()); } if (sa.hasParam("AttachedTo")) { diff --git a/src/main/java/forge/card/ability/effects/AttachEffect.java b/src/main/java/forge/card/ability/effects/AttachEffect.java index 0373b04112a..81a77cc7c6c 100644 --- a/src/main/java/forge/card/ability/effects/AttachEffect.java +++ b/src/main/java/forge/card/ability/effects/AttachEffect.java @@ -29,7 +29,7 @@ public class AttachEffect extends SpellAbilityEffect { // The Spell_Permanent (Auras) version of this AF needs to // move the card into play before Attaching - sa.getSourceCard().addController(sa.getActivatingPlayer()); + sa.getSourceCard().setController(sa.getActivatingPlayer(), 0); final Card c = Singletons.getModel().getGame().getAction().moveTo(sa.getActivatingPlayer().getZone(ZoneType.Battlefield), sa.getSourceCard()); sa.setSourceCard(c); } @@ -92,8 +92,7 @@ public class AttachEffect extends SpellAbilityEffect { // Spellweaver Volute, Dance of the Dead, Animate Dead // Although honestly, I'm not sure if the three of those could // handle being scripted - final boolean gainControl = "GainControl".equals(sa.getParam("AILogic")); - handleAura(card, c, gainControl); + handleAura(card, c); } else if (card.isEquipment()) { card.equipCard(c); // else if (card.isFortification()) @@ -105,7 +104,7 @@ public class AttachEffect extends SpellAbilityEffect { // Curse cards final Player p = (Player) o; if (card.isAura()) { - handleAura(card, p, false); + handleAura(card, p); } } } @@ -120,7 +119,7 @@ public class AttachEffect extends SpellAbilityEffect { * @param gainControl * the gain control */ - public static void handleAura(final Card card, final GameEntity tgt, final boolean gainControl) { + public static void handleAura(final Card card, final GameEntity tgt) { if (card.isEnchanting()) { // If this Card is already Enchanting something // Need to unenchant it, then clear out the commands @@ -132,72 +131,6 @@ public class AttachEffect extends SpellAbilityEffect { card.clearTriggers(); // not sure if cleartriggers is needed? } - if (gainControl) { - // Handle GainControl Auras - final Player[] pl = new Player[1]; - - if (tgt instanceof Card) { - pl[0] = ((Card) tgt).getController(); - } else { - pl[0] = (Player) tgt; - } - - final Command onEnchant = new Command() { - private static final long serialVersionUID = -2519887209491512000L; - - @Override - public void execute() { - final Card crd = card.getEnchantingCard(); - if (crd == null) { - return; - } - - pl[0] = crd.getController(); - - crd.addController(card); - - } // execute() - }; // Command - - final Command onUnEnchant = new Command() { - private static final long serialVersionUID = 3426441132121179288L; - - @Override - public void execute() { - final Card crd = card.getEnchantingCard(); - if (crd == null) { - return; - } - - if (crd.isInPlay()) { - crd.removeController(card); - } - - } // execute() - }; // Command - - final Command onChangesControl = new Command() { - /** automatically generated serialVersionUID. */ - private static final long serialVersionUID = -65903786170234039L; - - @Override - public void execute() { - final Card crd = card.getEnchantingCard(); - if (crd == null) { - return; - } - crd.removeController(card); // This looks odd, but will - // simply refresh controller - crd.addController(card); - } // execute() - }; // Command - - // Add Enchant Commands for Control changers - card.addEnchantCommand(onEnchant); - card.addUnEnchantCommand(onUnEnchant); - card.addChangeControllerCommand(onChangesControl); - } - final Command onLeavesPlay = new Command() { private static final long serialVersionUID = -639204333673364477L; @@ -251,7 +184,6 @@ public class AttachEffect extends SpellAbilityEffect { } aura.setActivatingPlayer(source.getController()); final Target tgt = aura.getTarget(); - final boolean gainControl = "GainControl".equals(aura.getParam("AILogic")); if (source.getController().isHuman()) { if (tgt.canTgtPlayer()) { @@ -265,8 +197,7 @@ public class AttachEffect extends SpellAbilityEffect { final Player p = GuiChoose.one(source + " - Select a player to attach to.", players); if (p != null) { - handleAura(source, p, false); - //source.enchantEntity((Player) o); + handleAura(source, p); return true; } } else { @@ -278,7 +209,7 @@ public class AttachEffect extends SpellAbilityEffect { final Object o = GuiChoose.one(source + " - Select a card to attach to.", list); if (o instanceof Card) { - handleAura(source, (Card) o, gainControl); + handleAura(source, (Card) o); //source.enchantEntity((Card) o); return true; } @@ -289,11 +220,11 @@ public class AttachEffect extends SpellAbilityEffect { final Object o = aura.getTarget().getTargets().get(0); if (o instanceof Card) { //source.enchantEntity((Card) o); - handleAura(source, (Card) o, gainControl); + handleAura(source, (Card) o); return true; } else if (o instanceof Player) { //source.enchantEntity((Player) o); - handleAura(source, (Player) o, false); + handleAura(source, (Player) o); return true; } } diff --git a/src/main/java/forge/card/ability/effects/ChangeZoneAllEffect.java b/src/main/java/forge/card/ability/effects/ChangeZoneAllEffect.java index 2c806fc5412..00deb458af9 100644 --- a/src/main/java/forge/card/ability/effects/ChangeZoneAllEffect.java +++ b/src/main/java/forge/card/ability/effects/ChangeZoneAllEffect.java @@ -81,7 +81,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect { } if (sa.hasParam("GainControl")) { - c.addController(sa.getSourceCard()); + c.setController(sa.getActivatingPlayer(), Singletons.getModel().getGame().getNextTimestamp()); Singletons.getModel().getGame().getAction().moveToPlay(c, sa.getActivatingPlayer()); } else { final Card movedCard = Singletons.getModel().getGame().getAction().moveTo(destination, c, libraryPos); diff --git a/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java b/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java index 8ac6861aad1..615aad4ce3f 100644 --- a/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java +++ b/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java @@ -385,7 +385,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { tgtC.setTapped(true); } if (sa.hasParam("GainControl")) { - tgtC.addController(sa.getSourceCard()); + tgtC.setController(sa.getActivatingPlayer(), Singletons.getModel().getGame().getNextTimestamp()); } if (sa.hasParam("AttachedTo")) { List list = AbilityUtils.getDefinedCards(hostCard, @@ -669,7 +669,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { c.setTapped(true); } if (sa.hasParam("GainControl")) { - c.addController(sa.getSourceCard()); + c.setController(sa.getActivatingPlayer(), Singletons.getModel().getGame().getNextTimestamp()); } if (sa.hasParam("AttachedTo")) { diff --git a/src/main/java/forge/card/ability/effects/ControlExchangeEffect.java b/src/main/java/forge/card/ability/effects/ControlExchangeEffect.java index 7889bea88e1..5f63b87e90b 100644 --- a/src/main/java/forge/card/ability/effects/ControlExchangeEffect.java +++ b/src/main/java/forge/card/ability/effects/ControlExchangeEffect.java @@ -3,6 +3,7 @@ package forge.card.ability.effects; import java.util.ArrayList; import forge.Card; +import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.ability.SpellAbilityEffect; import forge.card.spellability.SpellAbility; @@ -56,9 +57,10 @@ public class ControlExchangeEffect extends SpellAbilityEffect { return; } - Player player2 = object2.getController(); - object2.addController(object1.getController()); - object1.addController(player2); + final Player player2 = object2.getController(); + final long tStamp = Singletons.getModel().getGame().getNextTimestamp(); + object2.setController(object1.getController(), tStamp); + object1.setController(player2, tStamp); } } diff --git a/src/main/java/forge/card/ability/effects/ControlGainEffect.java b/src/main/java/forge/card/ability/effects/ControlGainEffect.java index ba4d374d462..e0263bd79d3 100644 --- a/src/main/java/forge/card/ability/effects/ControlGainEffect.java +++ b/src/main/java/forge/card/ability/effects/ControlGainEffect.java @@ -6,7 +6,6 @@ import java.util.List; import forge.Card; import forge.Command; -import forge.GameEntity; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.ability.SpellAbilityEffect; @@ -52,14 +51,12 @@ public class ControlGainEffect extends SpellAbilityEffect { } private void doLoseControl(final Card c, final Card host, final boolean tapOnLose, - final List addedKeywords, final GameEntity newController) { + final List addedKeywords, final long tStamp) { if (null == c) { return; } if (c.isInPlay()) { - c.removeController(newController); - // Singletons.getModel().getGameAction().changeController(new ArrayList(c), - // c.getController(), originalController); + c.removeTempController(tStamp); if (tapOnLose) { c.tap(); @@ -105,14 +102,10 @@ public class ControlGainEffect extends SpellAbilityEffect { controllers = tgt.getTargetPlayers(); } - GameEntity newController; + Player newController; if (controllers.size() == 0) { - if (sa.isSpell()) { - newController = sa.getActivatingPlayer(); - } else { - newController = source; - } + newController = sa.getActivatingPlayer(); } else { newController = controllers.get(0); } @@ -124,66 +117,62 @@ public class ControlGainEffect extends SpellAbilityEffect { return; } - final int size = tgtCards.size(); - for (int j = 0; j < size; j++) { - final Card tgtC = tgtCards.get(j); - final Player originalController = tgtC.getController(); + for (Card tgtC : tgtCards) { if (!tgtC.equals(sa.getSourceCard()) && !sa.getSourceCard().getGainControlTargets().contains(tgtC)) { sa.getSourceCard().addGainControlTarget(tgtC); } - if (tgtC.isInPlay()) { + if (!tgtC.isInPlay()) { + return; + } - if (!tgtC.equals(newController)) { - tgtC.addController(newController); - } - // Singletons.getModel().getGameAction().changeController(new ArrayList(tgtC), - // tgtC.getController(), newController.get(0)); + long tStamp = Singletons.getModel().getGame().getNextTimestamp(); + if (lose != null) { + tgtC.addTempController(newController, tStamp); + } else { + tgtC.setController(newController, tStamp); + } - if (bUntap) { - tgtC.untap(); - } + if (bUntap) { + tgtC.untap(); + } - if (null != kws) { - for (final String kw : kws) { - tgtC.addExtrinsicKeyword(kw); - } + if (null != kws) { + for (final String kw : kws) { + tgtC.addExtrinsicKeyword(kw); } } - // end copied - - final Card hostCard = sa.getSourceCard(); if (lose != null) { if (lose.contains("LeavesPlay")) { - sa.getSourceCard().addLeavesPlayCommand(this.getLoseControlCommand(tgtC, originalController, newController, bTapOnLose, hostCard, kws)); + sa.getSourceCard().addLeavesPlayCommand(this.getLoseControlCommand(tgtC, tStamp, bTapOnLose, source, kws)); } if (lose.contains("Untap")) { - sa.getSourceCard().addUntapCommand(this.getLoseControlCommand(tgtC, originalController, newController, bTapOnLose, hostCard, kws)); + sa.getSourceCard().addUntapCommand(this.getLoseControlCommand(tgtC, tStamp, bTapOnLose, source, kws)); } if (lose.contains("LoseControl")) { - sa.getSourceCard().addChangeControllerCommand(this.getLoseControlCommand(tgtC, originalController, newController, bTapOnLose, hostCard, kws)); + sa.getSourceCard().addChangeControllerCommand(this.getLoseControlCommand(tgtC, tStamp, bTapOnLose, source, kws)); } if (lose.contains("EOT")) { - Singletons.getModel().getGame().getEndOfTurn().addAt(this.getLoseControlCommand(tgtC, originalController, newController, bTapOnLose, hostCard, kws)); + Singletons.getModel().getGame().getEndOfTurn().addAt(this.getLoseControlCommand(tgtC, tStamp, bTapOnLose, source, kws)); } } if (destroyOn != null) { if (destroyOn.contains("LeavesPlay")) { - sa.getSourceCard().addLeavesPlayCommand(this.getDestroyCommand(tgtC, hostCard, bNoRegen)); + sa.getSourceCard().addLeavesPlayCommand(this.getDestroyCommand(tgtC, source, bNoRegen)); } if (destroyOn.contains("Untap")) { - sa.getSourceCard().addUntapCommand(this.getDestroyCommand(tgtC, hostCard, bNoRegen)); + sa.getSourceCard().addUntapCommand(this.getDestroyCommand(tgtC, source, bNoRegen)); } if (destroyOn.contains("LoseControl")) { - sa.getSourceCard().addChangeControllerCommand(this.getDestroyCommand(tgtC, hostCard, bNoRegen)); + sa.getSourceCard().addChangeControllerCommand(this.getDestroyCommand(tgtC, source, bNoRegen)); } } sa.getSourceCard().clearGainControlReleaseCommands(); - sa.getSourceCard().addGainControlReleaseCommand(this.getLoseControlCommand(tgtC, originalController, newController, bTapOnLose, hostCard, kws)); + sa.getSourceCard().addGainControlReleaseCommand(this.getLoseControlCommand(tgtC, tStamp, bTapOnLose, source, kws)); } // end foreach target } @@ -239,13 +228,13 @@ public class ControlGainEffect extends SpellAbilityEffect { * a {@link forge.game.player.Player} object. * @return a {@link forge.Command} object. */ - private Command getLoseControlCommand(final Card c, final Player originalController, final GameEntity newController, + private Command getLoseControlCommand(final Card c, final long tStamp, final boolean bTapOnLose, final Card hostCard, final List kws) { final Command loseControl = new Command() { private static final long serialVersionUID = 878543373519872418L; @Override - public void execute() { doLoseControl(c, hostCard, bTapOnLose, kws, newController); } // execute() + public void execute() { doLoseControl(c, hostCard, bTapOnLose, kws, tStamp); } }; return loseControl; diff --git a/src/main/java/forge/card/ability/effects/CopyPermanentEffect.java b/src/main/java/forge/card/ability/effects/CopyPermanentEffect.java index 1bd5ff47928..f3537d6ce0c 100644 --- a/src/main/java/forge/card/ability/effects/CopyPermanentEffect.java +++ b/src/main/java/forge/card/ability/effects/CopyPermanentEffect.java @@ -86,7 +86,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect { copy = CardFactory.getCard(CardDb.getCard(c), sa.getActivatingPlayer()); // when copying something stolen: - copy.addController(controller); + copy.setController(controller, 0); copy.setToken(true); copy.setCopiedToken(true); @@ -97,7 +97,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect { copy.setImageFilename(c.getImageFilename()); copy.setOwner(controller); - copy.addController(controller); + copy.setController(controller, 0); copy.setManaCost(c.getManaCost()); copy.setColor(c.getColor()); diff --git a/src/main/java/forge/card/ability/effects/CounterEffect.java b/src/main/java/forge/card/ability/effects/CounterEffect.java index 04706603ce0..3a28e70a12e 100644 --- a/src/main/java/forge/card/ability/effects/CounterEffect.java +++ b/src/main/java/forge/card/ability/effects/CounterEffect.java @@ -148,11 +148,11 @@ public class CounterEffect extends SpellAbilityEffect { if (tgtSA instanceof SpellPermanent) { Card c = tgtSA.getSourceCard(); System.out.println(c + " is SpellPermanent"); - c.addController(srcSA.getActivatingPlayer()); + c.setController(srcSA.getActivatingPlayer(), 0); Singletons.getModel().getGame().getAction().moveToPlay(c, srcSA.getActivatingPlayer()); } else { Card c = Singletons.getModel().getGame().getAction().moveToPlay(tgtSA.getSourceCard(), srcSA.getActivatingPlayer()); - c.addController(srcSA.getActivatingPlayer()); + c.setController(srcSA.getActivatingPlayer(), 0); } } else if (destination.equals("BottomOfLibrary")) { Singletons.getModel().getGame().getAction().moveToBottomOfLibrary(tgtSA.getSourceCard()); diff --git a/src/main/java/forge/card/ability/effects/DigUntilEffect.java b/src/main/java/forge/card/ability/effects/DigUntilEffect.java index d268a278bc3..f7eb4b0cede 100644 --- a/src/main/java/forge/card/ability/effects/DigUntilEffect.java +++ b/src/main/java/forge/card/ability/effects/DigUntilEffect.java @@ -131,7 +131,7 @@ public class DigUntilEffect extends SpellAbilityEffect { while (itr.hasNext()) { final Card c = itr.next(); if (sa.hasParam("GainControl") && foundDest.equals(ZoneType.Battlefield)) { - c.addController(sa.getSourceCard()); + c.setController(sa.getActivatingPlayer(), Singletons.getModel().getGame().getNextTimestamp()); Singletons.getModel().getGame().getAction().moveTo(c.getController().getZone(foundDest), c); } else if (sa.hasParam("NoMoveFound") && foundDest.equals(ZoneType.Library)) { //Don't do anything diff --git a/src/main/java/forge/card/ability/effects/PermanentCreatureEfect.java b/src/main/java/forge/card/ability/effects/PermanentCreatureEfect.java index 26e5498d842..c57e130bc0f 100644 --- a/src/main/java/forge/card/ability/effects/PermanentCreatureEfect.java +++ b/src/main/java/forge/card/ability/effects/PermanentCreatureEfect.java @@ -17,7 +17,7 @@ public class PermanentCreatureEfect extends SpellAbilityEffect { */ @Override public void resolve(SpellAbility sa) { - sa.getSourceCard().addController(sa.getActivatingPlayer()); + sa.getSourceCard().setController(sa.getActivatingPlayer(), 0); final Card c = Singletons.getModel().getGame().getAction().moveTo(sa.getActivatingPlayer().getZone(ZoneType.Battlefield), sa.getSourceCard()); sa.setSourceCard(c); } diff --git a/src/main/java/forge/card/ability/effects/PermanentNoncreatureEffect.java b/src/main/java/forge/card/ability/effects/PermanentNoncreatureEffect.java index b63c943f46c..c9e07795090 100644 --- a/src/main/java/forge/card/ability/effects/PermanentNoncreatureEffect.java +++ b/src/main/java/forge/card/ability/effects/PermanentNoncreatureEffect.java @@ -17,7 +17,7 @@ public class PermanentNoncreatureEffect extends SpellAbilityEffect { */ @Override public void resolve(SpellAbility sa) { - sa.getSourceCard().addController(sa.getActivatingPlayer()); + sa.getSourceCard().setController(sa.getActivatingPlayer(), 0); final Card c = Singletons.getModel().getGame().getAction().moveTo(sa.getActivatingPlayer().getZone(ZoneType.Battlefield), sa.getSourceCard()); sa.setSourceCard(c); } diff --git a/src/main/java/forge/card/spellability/SpellPermanent.java b/src/main/java/forge/card/spellability/SpellPermanent.java index 9a2d11a541e..dd08623e380 100644 --- a/src/main/java/forge/card/spellability/SpellPermanent.java +++ b/src/main/java/forge/card/spellability/SpellPermanent.java @@ -575,7 +575,7 @@ public class SpellPermanent extends Spell { @Override public void resolve() { Card c = this.getSourceCard(); - c.addController(this.getActivatingPlayer()); + c.setController(this.getActivatingPlayer(), 0); Singletons.getModel().getGame().getAction().moveTo(ZoneType.Battlefield, c); } } diff --git a/src/main/java/forge/card/staticability/StaticAbility.java b/src/main/java/forge/card/staticability/StaticAbility.java index acf003ee87e..3ca3ff50eba 100644 --- a/src/main/java/forge/card/staticability/StaticAbility.java +++ b/src/main/java/forge/card/staticability/StaticAbility.java @@ -137,6 +137,10 @@ public class StaticAbility { return 0; } + if (this.params.containsKey("GainControl")) { + return 2; + } + if (this.params.containsKey("AddType") || this.params.containsKey("RemoveType") || this.params.containsKey("RemoveCardType") || this.params.containsKey("RemoveSubType") || this.params.containsKey("RemoveSuperType")) { diff --git a/src/main/java/forge/card/staticability/StaticAbilityContinuous.java b/src/main/java/forge/card/staticability/StaticAbilityContinuous.java index 77971bc032f..12e516d5e4f 100644 --- a/src/main/java/forge/card/staticability/StaticAbilityContinuous.java +++ b/src/main/java/forge/card/staticability/StaticAbilityContinuous.java @@ -318,6 +318,11 @@ public class StaticAbilityContinuous { // start modifying the cards for (int i = 0; i < affectedCards.size(); i++) { final Card affectedCard = affectedCards.get(i); + + // Gain control + if (params.containsKey("GainControl")) { + affectedCard.addTempController(hostCard.getController(), hostCard.getTimestamp()); + } // set P/T if (params.containsKey("CharacteristicDefining")) { diff --git a/src/main/java/forge/game/GameAction.java b/src/main/java/forge/game/GameAction.java index d2be61d4351..02442025407 100644 --- a/src/main/java/forge/game/GameAction.java +++ b/src/main/java/forge/game/GameAction.java @@ -975,9 +975,18 @@ public class GameAction { final HashMap runParams = new HashMap(); game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false); + + for (Player p : game.getPlayers()) { + for (Card c : p.getCardsIn(ZoneType.Battlefield)) { + if (!c.getController().equals(p)) { + controllerChangeZoneCorrection(c); + c.runChangeControllerCommands(); + checkAgain = true; + } + } + } for (Card c : game.getCardsIn(ZoneType.Battlefield)) { - if (c.isEquipped()) { final List equipments = new ArrayList(c.getEquippedBy()); for (final Card equipment : equipments) { @@ -988,7 +997,6 @@ public class GameAction { } } // if isEquipped() - if (c.isEquipping()) { final Card equippedCreature = c.getEquipping().get(0); if (!equippedCreature.isCreature() || !equippedCreature.isInPlay()) { @@ -1089,7 +1097,7 @@ public class GameAction { checkAgain = true; } - } // while it.hasNext() + } if (game.getTriggerHandler().runWaitingTriggers(true)) { checkAgain = true; diff --git a/src/main/java/forge/game/phase/PhaseHandler.java b/src/main/java/forge/game/phase/PhaseHandler.java index 9893b613a98..a871a13feec 100644 --- a/src/main/java/forge/game/phase/PhaseHandler.java +++ b/src/main/java/forge/game/phase/PhaseHandler.java @@ -550,8 +550,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { if (game.getType() == GameType.Planechase) { Card p = game.getActivePlane(); if (p != null) { - p.clearControllers(); - p.addController(next); + p.setController(next, 0); game.getAction().controllerChangeZoneCorrection(p); } } diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index e126dda9cb9..8f8b9410d5b 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -1850,7 +1850,7 @@ public abstract class Player extends GameEntity implements Comparable { */ public final void playLand(final Card land) { if (this.canPlayLand(land)) { - land.addController(this); + land.setController(this, 0); game.getAction().moveTo(this.getZone(ZoneType.Battlefield), land); CardFactoryUtil.playLandEffects(land); this.numLandsPlayed++;