From 72f2c8b2cb250176ff40a0c0b4d076f2104b9558 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Tue, 2 Feb 2021 17:06:19 +0000 Subject: [PATCH] ExchangeControl: add Optional and two Targets also TargetsWithSameCardType --- .../forge/ai/ability/ControlExchangeAi.java | 53 ++++++++++++++++++- .../forge/game/ability/AbilityFactory.java | 3 ++ .../effects/ControlExchangeEffect.java | 50 ++++++++++++----- .../forge/game/spellability/SpellAbility.java | 47 +++++++++------- .../game/spellability/TargetRestrictions.java | 16 ++++++ .../res/cardsfolder/c/cliffside_market.txt | 4 +- .../cardsfolder/c/confusion_in_the_ranks.txt | 1 - forge-gui/res/cardsfolder/d/daring_thief.txt | 3 +- .../res/cardsfolder/g/gauntlets_of_chaos.txt | 3 +- forge-gui/res/cardsfolder/j/juxtapose.txt | 5 +- .../cardsfolder/k/karona_false_god_avatar.txt | 2 +- forge-gui/res/cardsfolder/l/legerdemain.txt | 2 +- forge-gui/res/cardsfolder/r/role_reversal.txt | 3 +- .../res/cardsfolder/s/shifting_loyalties.txt | 4 +- .../upcoming/the_trickster_gods_heist.txt | 11 ++++ forge-gui/res/languages/de-DE.properties | 2 + forge-gui/res/languages/en-US.properties | 2 + forge-gui/res/languages/es-ES.properties | 2 + forge-gui/res/languages/it-IT.properties | 2 + forge-gui/res/languages/zh-CN.properties | 2 + 20 files changed, 163 insertions(+), 54 deletions(-) create mode 100644 forge-gui/res/cardsfolder/upcoming/the_trickster_gods_heist.txt diff --git a/forge-ai/src/main/java/forge/ai/ability/ControlExchangeAi.java b/forge-ai/src/main/java/forge/ai/ability/ControlExchangeAi.java index f21b327b3fd..e93f30ff733 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ControlExchangeAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ControlExchangeAi.java @@ -81,7 +81,12 @@ public class ControlExchangeAi extends SpellAbilityAi { final TargetRestrictions tgt = sa.getTargetRestrictions(); - CardCollection list = CardLists.getValidCards(aiPlayer.getGame().getCardsIn(ZoneType.Battlefield), + // for TrigTwoTargets logic, only get the opponents' cards for the first target + CardCollectionView unfilteredList = "TrigTwoTargets".equals(sa.getParam("AILogic")) ? + aiPlayer.getOpponents().getCardsIn(ZoneType.Battlefield) : + aiPlayer.getGame().getCardsIn(ZoneType.Battlefield); + + CardCollection list = CardLists.getValidCards(unfilteredList, tgt.getValidTgts(), aiPlayer, sa.getHostCard(), sa); // only select the cards that can be targeted @@ -106,7 +111,51 @@ public class ControlExchangeAi extends SpellAbilityAi { // add best Target sa.getTargets().add(best); + + // second target needed (the AI's own worst) + if ("TrigTwoTargets".equals(sa.getParam("AILogic"))) { + return doTrigTwoTargetsLogic(aiPlayer, sa, best); + } + return true; } - + + private boolean doTrigTwoTargetsLogic(Player ai, SpellAbility sa, Card bestFirstTgt) { + final TargetRestrictions tgt = sa.getTargetRestrictions(); + final int creatureThreshold = 100; // TODO: make this configurable from the AI profile + final int nonCreatureThreshold = 2; + + CardCollection list = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), + tgt.getValidTgts(), ai, sa.getHostCard(), sa); + + // only select the cards that can be targeted + list = CardLists.getTargetableCards(list, sa); + + if (list.isEmpty()) { + return false; + } + + Card aiWorst = ComputerUtilCard.getWorstAI(list); + if (aiWorst == null) { + return false; + } + + if (aiWorst != null && aiWorst != bestFirstTgt) { + if (bestFirstTgt.isCreature() && aiWorst.isCreature()) { + if ((ComputerUtilCard.evaluateCreature(bestFirstTgt) > ComputerUtilCard.evaluateCreature(aiWorst) + creatureThreshold) || sa.isMandatory()) { + sa.getTargets().add(aiWorst); + return true; + } + } else { + // TODO: compare non-creatures by CMC - can be improved, at least shouldn't give control of things like the Power Nine + if ((bestFirstTgt.getCMC() > aiWorst.getCMC() + nonCreatureThreshold) || sa.isMandatory()) { + sa.getTargets().add(aiWorst); + return true; + } + } + } + + sa.clearTargets(); + return false; + } } diff --git a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java index f45d73068dd..b64e55edf59 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java @@ -352,6 +352,9 @@ public final class AbilityFactory { if (mapParams.containsKey("TargetsWithSameCreatureType")) { abTgt.setWithSameCreatureType(true); } + if (mapParams.containsKey("TargetsWithSameCardType")) { + abTgt.setWithSameCardType(true); + } if (mapParams.containsKey("TargetsWithSameController")) { abTgt.setSameController(true); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ControlExchangeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ControlExchangeEffect.java index 0e2ea5a6ce6..7a2daa09be0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ControlExchangeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ControlExchangeEffect.java @@ -1,14 +1,16 @@ package forge.game.ability.effects; import com.google.common.collect.Lists; + +import forge.game.Game; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; +import forge.util.CardTranslation; +import forge.util.Localizer; -import java.util.ArrayList; import java.util.List; @@ -21,21 +23,27 @@ public class ControlExchangeEffect extends SpellAbilityEffect { protected String getStackDescription(SpellAbility sa) { Card object1 = null; Card object2 = null; - final TargetRestrictions tgt = sa.getTargetRestrictions(); - List tgts = tgt == null ? new ArrayList<>() : Lists.newArrayList(sa.getTargets().getTargetCards()); - if (tgts.size() > 0) { - object1 = tgts.get(0); + List tgts = null; + if (sa.usesTargeting()) { + tgts = Lists.newArrayList(sa.getTargets().getTargetCards()); + if (tgts.size() > 0) { + object1 = tgts.get(0); + } } if (sa.hasParam("Defined")) { List cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa); object2 = cards.isEmpty() ? null : cards.get(0); - if (cards.size() > 1 && sa.hasParam("BothDefined")) { + if (cards.size() > 1 && !sa.usesTargeting()) { object1 = cards.get(1); } } else if (tgts.size() > 1) { object2 = tgts.get(1); } + if (object1 == null || object2 == null) { + return ""; + } + return object1 + " exchanges controller with " + object2; } @@ -44,17 +52,22 @@ public class ControlExchangeEffect extends SpellAbilityEffect { */ @Override public void resolve(SpellAbility sa) { + Card host = sa.getHostCard(); + Game game = host.getGame(); Card object1 = null; Card object2 = null; - final TargetRestrictions tgt = sa.getTargetRestrictions(); - List tgts = tgt == null ? new ArrayList<>() : Lists.newArrayList(sa.getTargets().getTargetCards()); - if (tgts.size() > 0) { - object1 = tgts.get(0); + + List tgts = null; + if (sa.usesTargeting()) { + tgts = Lists.newArrayList(sa.getTargets().getTargetCards()); + if (tgts.size() > 0) { + object1 = tgts.get(0); + } } if (sa.hasParam("Defined")) { - final List cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa); + final List cards = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa); object2 = cards.isEmpty() ? null : cards.get(0); - if (cards.size() > 1 && sa.hasParam("BothDefined")) { + if (cards.size() > 1 && !sa.usesTargeting()) { object1 = cards.get(1); } } else if (tgts.size() > 1) { @@ -73,7 +86,16 @@ public class ControlExchangeEffect extends SpellAbilityEffect { return; } - final long tStamp = sa.getActivatingPlayer().getGame().getNextTimestamp(); + if (sa.hasParam("Optional")) { + if (!sa.getActivatingPlayer().getController().confirmAction(sa, null, + Localizer.getInstance().getMessage("lblExchangeControl", + CardTranslation.getTranslatedName(object1.getName()), + CardTranslation.getTranslatedName(object2.getName())))) { + return; + } + } + + final long tStamp = game.getNextTimestamp(); object2.setController(player1, tStamp); object1.setController(player2, tStamp); if (sa.hasParam("RememberExchanged")) { diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index bc63cb582f7..23d12ed06c5 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -1056,29 +1056,26 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit final Card c = (Card) entity; CardCollection pl = AbilityUtils.getDefinedCards(getHostCard(), getParam("TargetsWithSharedCardType"), this); for (final Card crd : pl) { - if (!c.sharesCardTypeWith(crd)) { - return false; + // one of those types + if (hasParam("TargetsWithSharedTypes")) { + boolean flag = false; + for (final String type : getParam("TargetsWithSharedTypes").split(",")) { + if (c.getType().hasStringType(type) && crd.getType().hasStringType(type)) { + flag = true; + break; + } + } + if (!flag) { + return false; + } + } else { + if (!c.sharesCardTypeWith(crd)) { + return false; + } } } } - if (hasParam("TargetsWithSharedTypes") && entity instanceof Card) { - final Card c = (Card) entity; - final SpellAbility parent = getParentTargetingCard(); - final Card parentTargeted = parent != null ? parent.getTargetCard() : null; - if (parentTargeted == null) { - return false; - } - boolean flag = false; - for (final String type : getParam("TargetsWithSharedTypes").split(",")) { - if (c.getType().hasStringType(type) && parentTargeted.getType().hasStringType(type)) { - flag = true; - break; - } - } - if (!flag) { - return false; - } - } + if (hasParam("TargetsWithControllerProperty") && entity instanceof Card) { final String prop = getParam("TargetsWithControllerProperty"); final Card c = (Card) entity; @@ -1172,6 +1169,16 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } } + if (tr.isWithSameCardType()) { + if (entity instanceof Card) { + for (final Card c : targetChosen.getTargetCards()) { + if (entity != c && !c.sharesCardTypeWith((Card) entity)) { + return false; + } + } + } + } + String[] validTgt = tr.getValidTgts(); if (entity instanceof GameEntity) { GameEntity e = (GameEntity)entity; diff --git a/forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java b/forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java index d917a4058f9..eef0d88b541 100644 --- a/forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java +++ b/forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java @@ -65,6 +65,7 @@ public class TargetRestrictions { private boolean sameController = false; private boolean withoutSameCreatureType = false; private boolean withSameCreatureType = false; + private boolean withSameCardType = false; private boolean singleTarget = false; private boolean randomTarget = false; @@ -108,6 +109,7 @@ public class TargetRestrictions { this.sameController = target.isSameController(); this.withoutSameCreatureType = target.isWithoutSameCreatureType(); this.withSameCreatureType = target.isWithSameCreatureType(); + this.withSameCardType = target.isWithSameCardType(); this.singleTarget = target.isSingleTarget(); this.randomTarget = target.isRandomTarget(); } @@ -622,6 +624,20 @@ public class TargetRestrictions { this.withSameCreatureType = b; } + /** + * @return the withSameCardType + */ + public boolean isWithSameCardType() { + return withSameCardType; + } + + /** + * @param b the withSameCardType to set + */ + public void setWithSameCardType(boolean b) { + this.withSameCardType = b; + } + /** *

* copy. diff --git a/forge-gui/res/cardsfolder/c/cliffside_market.txt b/forge-gui/res/cardsfolder/c/cliffside_market.txt index 00db712d6b2..c6a038093a8 100644 --- a/forge-gui/res/cardsfolder/c/cliffside_market.txt +++ b/forge-gui/res/cardsfolder/c/cliffside_market.txt @@ -5,9 +5,7 @@ T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ TrigLife | OptionalDeci T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigLife | TriggerZones$ Command | Secondary$ True | OptionalDecider$ You | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, you may exchange life totals with target player. SVar:TrigLife:DB$ ExchangeLife | Optional$ True | ValidTgts$ Player | TgtPrompt$ Select target player to exchange life totals with T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, exchange control of two target permanents that share a card type. -SVar:RolledChaos:DB$ Pump | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | SubAbility$ DBExchange -SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent | TgtPrompt$ Select target permanent card | TargetsWithSharedTypes$ Creature,Artifact,Enchantment,Planeswalker,Land,Tribal | TargetUnique$ True +SVar:RolledChaos:DB$ ExchangeControl | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Permanent | TgtPrompt$ Select target permanents that share a permanent type | TargetsWithSameCardType$ True AI:RemoveDeck:All AI:RemoveDeck:Random -SVar:Picture:http://www.wizards.com/global/images/magic/general/cliffside_market.jpg Oracle:When you planeswalk to Cliffside Market or at the beginning of your upkeep, you may exchange life totals with target player.\nWhenever you roll {CHAOS}, exchange control of two target permanents that share a card type. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/c/confusion_in_the_ranks.txt b/forge-gui/res/cardsfolder/c/confusion_in_the_ranks.txt index e6f94222468..7a025cdcf7c 100644 --- a/forge-gui/res/cardsfolder/c/confusion_in_the_ranks.txt +++ b/forge-gui/res/cardsfolder/c/confusion_in_the_ranks.txt @@ -4,5 +4,4 @@ Types:Enchantment T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Artifact,Creature,Enchantment | TriggerZones$ Battlefield | Execute$ TrigExchangeControl | TriggerDescription$ Whenever an artifact, creature, or enchantment enters the battlefield, its controller chooses target permanent another player controls that shares a card type with it. Exchange control of those permanents. SVar:TrigExchangeControl:DB$ ExchangeControl | Defined$ TriggeredCard | TargetingPlayer$ TriggeredCardController | TargetsWithDefinedController$ NonTriggeredCardController | ValidTgts$ Permanent | TargetsWithSharedCardType$ TriggeredCard AI:RemoveDeck:Random -SVar:Picture:http://www.wizards.com/global/images/magic/general/confusion_in_the_ranks.jpg Oracle:Whenever an artifact, creature, or enchantment enters the battlefield, its controller chooses target permanent another player controls that shares a card type with it. Exchange control of those permanents. diff --git a/forge-gui/res/cardsfolder/d/daring_thief.txt b/forge-gui/res/cardsfolder/d/daring_thief.txt index 875fae5c210..b76e71babdd 100644 --- a/forge-gui/res/cardsfolder/d/daring_thief.txt +++ b/forge-gui/res/cardsfolder/d/daring_thief.txt @@ -4,8 +4,7 @@ Types:Creature Human Rogue PT:2/3 T:Mode$ Untaps | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigExchangeControl | OptionalDecider$ You | TriggerDescription$ Inspired — Whenever CARDNAME becomes untapped, you may exchange control of target nonland permanent you control and target permanent an opponent controls that shares a card type with it. SVar:TrigExchangeControl:DB$ Pump | ValidTgts$ Permanent.YouCtrl+nonLand | TgtPrompt$ Select target nonland permanent you control | SubAbility$ DBExchange -SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent.OppCtrl | TgtPrompt$ Select target permanent an opponent controls that shares a card type with it | TargetsWithSharedTypes$ Creature,Artifact,Enchantment,Planeswalker,Tribal +SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent.OppCtrl | TgtPrompt$ Select target permanent an opponent controls that shares a card type with it | TargetsWithSharedCardType$ ParentTarget AI:RemoveDeck:All AI:RemoveDeck:Random -SVar:Picture:http://www.wizards.com/global/images/magic/general/daring_thief.jpg Oracle:Inspired — Whenever Daring Thief becomes untapped, you may exchange control of target nonland permanent you control and target permanent an opponent controls that shares a card type with it. diff --git a/forge-gui/res/cardsfolder/g/gauntlets_of_chaos.txt b/forge-gui/res/cardsfolder/g/gauntlets_of_chaos.txt index 6d5d5f43151..991a6868a74 100644 --- a/forge-gui/res/cardsfolder/g/gauntlets_of_chaos.txt +++ b/forge-gui/res/cardsfolder/g/gauntlets_of_chaos.txt @@ -2,9 +2,8 @@ Name:Gauntlets of Chaos ManaCost:5 Types:Artifact A:AB$ Pump | Cost$ 5 Sac<1/CARDNAME> | ValidTgts$ Artifact.YouCtrl,Creature.YouCtrl,Land.YouCtrl | TgtPrompt$ target artifact, creature, or land you control | StackDescription$ None | SubAbility$ DBExchange | SpellDescription$ Exchange control of target artifact, creature, or land you control and target permanent an opponent controls that shares one of those types with it. If those permanents are exchanged this way, destroy all Auras attached to them. -SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent.OppCtrl | TgtPrompt$ Select target permanent an opponent controls that shares one of those types | TargetsWithSharedTypes$ Artifact,Creature,Land | RememberExchanged$ True | SubAbility$ DBDestroyAll +SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent.OppCtrl | TgtPrompt$ Select target permanent an opponent controls that shares one of those types | TargetsWithSharedCardType$ ParentTarget | TargetsWithSharedTypes$ Artifact,Creature,Land | RememberExchanged$ True | SubAbility$ DBDestroyAll SVar:DBDestroyAll:DB$ DestroyAll | ValidCards$ Aura.AttachedTo Card.IsRemembered | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True AI:RemoveDeck:All -SVar:Picture:http://www.wizards.com/global/images/magic/general/gauntlets_of_chaos.jpg Oracle:{5}, Sacrifice Gauntlets of Chaos: Exchange control of target artifact, creature, or land you control and target permanent an opponent controls that shares one of those types with it. If those permanents are exchanged this way, destroy all Auras attached to them. diff --git a/forge-gui/res/cardsfolder/j/juxtapose.txt b/forge-gui/res/cardsfolder/j/juxtapose.txt index 07faf62f196..c71022b24c0 100644 --- a/forge-gui/res/cardsfolder/j/juxtapose.txt +++ b/forge-gui/res/cardsfolder/j/juxtapose.txt @@ -3,16 +3,15 @@ ManaCost:3 U Types:Sorcery A:SP$ ChooseCard | Cost$ 3 U | ValidTgts$ Player | Choices$ Creature.cmcEQY | TargetControls$ True | References$ Y | Mandatory$ True | AILogic$ WorstCard | RememberChosen$ True | SubAbility$ DBChooseCreatureYou | SpellDescription$ You and target player exchange control of the creature you each control with the highest converted mana cost. Then exchange control of artifacts the same way. If two or more permanents a player controls are tied for highest cost, their controller chooses one of them. SVar:DBChooseCreatureYou:DB$ ChooseCard | Choices$ Creature.YouCtrl+cmcEQX | References$ X | Mandatory$ True | RememberChosen$ True | SubAbility$ DBExchangeCreature -SVar:DBExchangeCreature:DB$ ExchangeControl | BothDefined$ True | Defined$ Remembered | SubAbility$ DBCleanCreature +SVar:DBExchangeCreature:DB$ ExchangeControl | Defined$ Remembered | SubAbility$ DBCleanCreature SVar:DBCleanCreature:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBChooseArtifactYou SVar:DBChooseArtifactYou:DB$ ChooseCard | Choices$ Artifact.YouCtrl+cmcEQZ | References$ Z | Mandatory$ True | RememberChosen$ True | SubAbility$ DBChooseArtifactOpp SVar:DBChooseArtifactOpp:DB$ ChooseCard | Defined$ ParentTarget | Choices$ Artifact.cmcEQW | TargetControls$ True | References$ W | Mandatory$ True | AILogic$ WorstCard | RememberChosen$ True | SubAbility$ DBExchangeArtifact -SVar:DBExchangeArtifact:DB$ ExchangeControl | BothDefined$ True | Defined$ Remembered | SubAbility$ DBCleanup +SVar:DBExchangeArtifact:DB$ ExchangeControl | Defined$ Remembered | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:X:Count$HighestCMC_Creature.YouCtrl+inZoneBattlefield SVar:Y:Count$HighestCMC_Creature.TargetedPlayerCtrl+inZoneBattlefield SVar:Z:Count$HighestCMC_Artifact.YouCtrl+inZoneBattlefield SVar:W:Count$HighestCMC_Artifact.TargetedPlayerCtrl+inZoneBattlefield AI:RemoveDeck:All -SVar:Picture:http://www.wizards.com/global/images/magic/general/juxtapose.jpg Oracle:You and target player exchange control of the creature you each control with the highest converted mana cost. Then exchange control of artifacts the same way. If two or more permanents a player controls are tied for highest cost, their controller chooses one of them. diff --git a/forge-gui/res/cardsfolder/k/karona_false_god_avatar.txt b/forge-gui/res/cardsfolder/k/karona_false_god_avatar.txt index 799a9dbe070..5bb48983663 100644 --- a/forge-gui/res/cardsfolder/k/karona_false_god_avatar.txt +++ b/forge-gui/res/cardsfolder/k/karona_false_god_avatar.txt @@ -5,7 +5,7 @@ HandLifeModifier:-1/+8 T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Command | Execute$ TrigExchangeChoose | TriggerDescription$ At the beginning of your upkeep, exchange control of a permanent you control chosen at random and a permanent target opponent controls chosen at random. SVar:TrigExchangeChoose:DB$ ChooseCard | ValidTgts$ Opponent | Choices$ Permanent.TargetedPlayerCtrl | AtRandom$ True | Amount$ 1 | RememberChosen$ True | SubAbility$ ChooseYou SVar:ChooseYou:DB$ ChooseCard | Choices$ Permanent.YouCtrl | Amount$ 1 | AtRandom$ True | RememberChosen$ True | SubAbility$ DBExchange -SVar:DBExchange:DB$ ExchangeControl | Defined$ Remembered | BothDefined$ True | SubAbility$ DBCleanup +SVar:DBExchange:DB$ ExchangeControl | Defined$ Remembered | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:Picture:https://downloads.cardforge.org/images/cards/VAN/Karona, False God Avatar.full.jpg Oracle:Hand -1, life +8\nAt the beginning of your upkeep, exchange control of a permanent you control chosen at random and a permanent target opponent controls chosen at random. diff --git a/forge-gui/res/cardsfolder/l/legerdemain.txt b/forge-gui/res/cardsfolder/l/legerdemain.txt index aac8a000596..273619bb132 100644 --- a/forge-gui/res/cardsfolder/l/legerdemain.txt +++ b/forge-gui/res/cardsfolder/l/legerdemain.txt @@ -2,7 +2,7 @@ Name:Legerdemain ManaCost:2 U U Types:Sorcery A:SP$ Pump | Cost$ 2 U U | ValidTgts$ Artifact,Creature | TgtPrompt$ target artifact or creature | StackDescription$ None | SubAbility$ DBExchange | SpellDescription$ Exchange control of target artifact or creature and another target permanent that shares one of those types with it. (This effect lasts indefinitely.) -SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent | TgtPrompt$ Select target permanent that shares one of those types | TargetsWithSharedTypes$ Artifact,Creature | TargetUnique$ True +SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent | TgtPrompt$ Select target permanent that shares one of those types | TargetsWithSharedCardType$ ParentTarget | TargetsWithSharedTypes$ Artifact,Creature | TargetUnique$ True AI:RemoveDeck:All SVar:Picture:http://www.wizards.com/global/images/magic/general/legerdemain.jpg Oracle:Exchange control of target artifact or creature and another target permanent that shares one of those types with it. (This effect lasts indefinitely.) diff --git a/forge-gui/res/cardsfolder/r/role_reversal.txt b/forge-gui/res/cardsfolder/r/role_reversal.txt index beb8f76cfa1..077734bd24a 100644 --- a/forge-gui/res/cardsfolder/r/role_reversal.txt +++ b/forge-gui/res/cardsfolder/r/role_reversal.txt @@ -1,8 +1,7 @@ Name:Role Reversal ManaCost:U U R Types:Sorcery -A:SP$ Pump | Cost$ U U R | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | SubAbility$ DBExchange | StackDescription$ None | SpellDescription$ Exchange control of two target permanents that share a permanent type. -SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent | TgtPrompt$ Select target permanent shares a card type with it | TargetsWithSharedTypes$ Creature,Artifact,Enchantment,Planeswalker,Land | TargetUnique$ True +A:SP$ ExchangeControl | Cost$ U U R | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Permanent | TgtPrompt$ Select target permanents that share a permanent type | TargetsWithSameCardType$ True | SpellDescription$ Exchange control of two target permanents that share a permanent type. AI:RemoveDeck:All AI:RemoveDeck:Random Oracle:Exchange control of two target permanents that share a permanent type. diff --git a/forge-gui/res/cardsfolder/s/shifting_loyalties.txt b/forge-gui/res/cardsfolder/s/shifting_loyalties.txt index 4e45b9a0191..ca71f08f973 100644 --- a/forge-gui/res/cardsfolder/s/shifting_loyalties.txt +++ b/forge-gui/res/cardsfolder/s/shifting_loyalties.txt @@ -1,9 +1,7 @@ Name:Shifting Loyalties ManaCost:5 U Types:Sorcery -A:SP$ Pump | Cost$ 5 U | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | SubAbility$ DBExchange | StackDescription$ None | SpellDescription$ Exchange control of two target permanents that share a card type. -SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent | TgtPrompt$ Select target permanent shares a card type with it | TargetsWithSharedTypes$ Creature,Artifact,Enchantment,Planeswalker,Land,Tribal | TargetUnique$ True +A:SP$ ExchangeControl | Cost$ 5 U | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Permanent | TgtPrompt$ Select target permanents that share a permanent type | TargetsWithSameCardType$ True | SpellDescription$ Exchange control of two target permanents that share a card type. AI:RemoveDeck:All AI:RemoveDeck:Random -SVar:Picture:http://www.wizards.com/global/images/magic/general/shifting_loyalties.jpg Oracle:Exchange control of two target permanents that share a card type. (Artifact, creature, enchantment, land, and planeswalker are card types.) diff --git a/forge-gui/res/cardsfolder/upcoming/the_trickster_gods_heist.txt b/forge-gui/res/cardsfolder/upcoming/the_trickster_gods_heist.txt new file mode 100644 index 00000000000..502fe6ae380 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/the_trickster_gods_heist.txt @@ -0,0 +1,11 @@ +Name:The Trickster-God's Heist +ManaCost:2 U B +Types:Enchantment Saga +K:Saga:3:DBCreature,DBNonCreature,DBDrain +SVar:DBCreature:DB$ ExchangeControl | ValidTgts$ Creature | TargetMin$ 2 | TargetMax$ 2 | TgtPrompt$ Choose two target creatures | Optional$ True | AILogic$ TrigTwoTargets | StackDescription$ SpellDescription | SpellDescription$ You may exchange control of two target creatures. +SVar:DBNonCreature:DB$ ExchangeControl | ValidTgts$ Card.nonBasic+nonCreature | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Permanent | TgtPrompt$ Choose two target nonbasic, noncreature permanent | TargetsWithSameCardType$ True | Optional$ True | AILogic$ TrigTwoTargets | StackDescription$ SpellDescription | SpellDescription$ You may exchange control of two target nonbasic, noncreature permanents that share a card type. +SVar:DBDrain:DB$ LoseLife | ValidTgts$ Player | TgtPrompt$ Select a player | LifeAmount$ 3 | SubAbility$ DBGainLife | SpellDescription$ Target player loses 3 life and you gain 3 life. +SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 3 +DeckHas:Ability$LifeGain +AI:RemoveDeck:Random +Oracle:I - You may exchange control of two target creatures.\nII - You may exchange control of two target nonbasic, noncreature permanents that share a card type.\nIII - Target player loses 3 life and you gain 3 life. diff --git a/forge-gui/res/languages/de-DE.properties b/forge-gui/res/languages/de-DE.properties index 3534dfe9fea..f8acfd3ebb0 100644 --- a/forge-gui/res/languages/de-DE.properties +++ b/forge-gui/res/languages/de-DE.properties @@ -1769,6 +1769,8 @@ lblWinsClash=gewinnt Fehde lblLosesClash=verliert Fehde #CloneEffect.java lblDoYouWantCopy=Möchtest du {0} kopieren? +#ControlExchangeEffect.java +lblExchangeControl=Do you want to exchange control of {0} and {1}? #ControlExchangeVariantEffect.java lblChooseCards=Wähle Karten #CopyPermanentEffect.java diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index c52e1f54380..99b38444ceb 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -1769,6 +1769,8 @@ lblWinsClash=wins clash lblLosesClash=loses clash #CloneEffect.java lblDoYouWantCopy=Do you want to copy {0}? +#ControlExchangeEffect.java +lblExchangeControl=Do you want to exchange control of {0} and {1}? #ControlExchangeVariantEffect.java lblChooseCards=Choose cards #CopyPermanentEffect.java diff --git a/forge-gui/res/languages/es-ES.properties b/forge-gui/res/languages/es-ES.properties index 2b90de1b841..820f6a45f28 100644 --- a/forge-gui/res/languages/es-ES.properties +++ b/forge-gui/res/languages/es-ES.properties @@ -1769,6 +1769,8 @@ lblWinsClash=gana el enfrentamiento lblLosesClash=pierde el enfrentamiento #CloneEffect.java lblDoYouWantCopy=¿Quieres copiar {0}? +#ControlExchangeEffect.java +lblExchangeControl=¿Quieres intercambiar de {0} y {1}? #ControlExchangeVariantEffect.java lblChooseCards=Elige las cartas #CopyPermanentEffect.java diff --git a/forge-gui/res/languages/it-IT.properties b/forge-gui/res/languages/it-IT.properties index a13e803e8db..13cd1cabab3 100644 --- a/forge-gui/res/languages/it-IT.properties +++ b/forge-gui/res/languages/it-IT.properties @@ -1769,6 +1769,8 @@ lblWinsClash=wins clash lblLosesClash=loses clash #CloneEffect.java lblDoYouWantCopy=Do you want to copy {0}? +#ControlExchangeEffect.java +lblExchangeControl=Do you want to exchange control of {0} and {1}? #ControlExchangeVariantEffect.java lblChooseCards=Choose cards #CopyPermanentEffect.java diff --git a/forge-gui/res/languages/zh-CN.properties b/forge-gui/res/languages/zh-CN.properties index 164f9732f66..6b2f709982b 100644 --- a/forge-gui/res/languages/zh-CN.properties +++ b/forge-gui/res/languages/zh-CN.properties @@ -1769,6 +1769,8 @@ lblWinsClash=比点赢了 lblLosesClash=比点输了 #CloneEffect.java lblDoYouWantCopy=你想要复制{0}吗? +#ControlExchangeEffect.java +lblExchangeControl=Do you want to exchange control of {0} and {1}? #ControlExchangeVariantEffect.java lblChooseCards=选择牌 #CopyPermanentEffect.java