From dc41364d95b334a170af23c7870eeb080cfd7fc5 Mon Sep 17 00:00:00 2001 From: swordshine Date: Sun, 7 Oct 2018 15:56:59 +0800 Subject: [PATCH 1/3] - Added Lazav, the Multifarious --- forge-gui/res/cardsfolder/l/lazav_the_multifarious.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 forge-gui/res/cardsfolder/l/lazav_the_multifarious.txt diff --git a/forge-gui/res/cardsfolder/l/lazav_the_multifarious.txt b/forge-gui/res/cardsfolder/l/lazav_the_multifarious.txt new file mode 100644 index 00000000000..e8f35ff5bfa --- /dev/null +++ b/forge-gui/res/cardsfolder/l/lazav_the_multifarious.txt @@ -0,0 +1,10 @@ +Name:Lazav, the Multifarious +ManaCost:U B +Types:Legendary Creature Shapeshifter +PT:1/3 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSurveil | TriggerDescription$ When CARDNAME enters the battlefield, scry 1. (To scry 1, look at the top card of your library. You may put that card into your graveyard.) +SVar:TrigSurveil:DB$ Surveil | Amount$ 1 +A:AB$ Clone | Cost$ X | ValidTgts$ Creature.YouOwn | References$ X | TgtZone$ Graveyard | TgtPrompt$ Select target creature card in your graveyard | AddTypes$ Legendary | KeepName$ True | AddSVars$ X,LazavTrig | AddAbilities$ LazavTrig | SpellDescription$ CARDNAME becomes a copy of target creature card in your graveyard with converted mana cost X, except its name is CARDNAME, it's legendary in addition to it's other types, and it has this ability. +SVar:LazavTrig:AB$ Clone | Cost$ X | ValidTgts$ Creature.YouOwn | References$ X | TgtZone$ Graveyard | TgtPrompt$ Select target creature card in your graveyard | AddTypes$ Legendary | KeepName$ True | AddSVars$ X,LazavTrig | AddAbilities$ LazavTrig | SpellDescription$ CARDNAME becomes a copy of target creature card in your graveyard with converted mana cost X, except its name is CARDNAME, it's legendary in addition to it's other types, and it has this ability. +SVar:X:Targeted$CardManaCost +Oracle:When Lazav, the Multifarious enters the battlefield, surveil 1. (Look at the top card of your library. You may put that card into your graveyard.)\n{X}: Lazav, the Multifarious becomes a copy of target creature card in your graveyard with converted mana cost X, except its name is Lazav, the Multifarious, it's legendary in addition to its other types, and it has this ability. From 6f0871724fd9fe56c9365b9215fd884b1cfc603f Mon Sep 17 00:00:00 2001 From: swordshine Date: Sun, 7 Oct 2018 18:24:23 +0800 Subject: [PATCH 2/3] - Added Beamsplitter Mage --- .../effects/CopySpellAbilityEffect.java | 14 +++++++--- .../game/trigger/TriggerSpellAbilityCast.java | 27 +++++++++++++++++++ .../res/cardsfolder/b/beamsplitter_mage.txt | 7 +++++ 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 forge-gui/res/cardsfolder/b/beamsplitter_mage.txt diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java index 116b5aae57b..6d7b25e97d0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java @@ -8,6 +8,7 @@ import forge.game.GameEntity; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; +import forge.game.card.CardCollection; import forge.game.card.CardFactory; import forge.game.card.CardLists; import forge.game.player.Player; @@ -123,7 +124,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect { } } else {// Precursor Golem, Ink-Treader Nephilim final String type = sa.getParam("CopyForEachCanTarget"); - List valid = Lists.newArrayList(); + CardCollection valid = new CardCollection(); List players = Lists.newArrayList(); Player originalTargetPlayer = Iterables.getFirst(getTargetPlayers(chosenSA), null); for (final GameEntity o : candidates) { @@ -142,10 +143,17 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect { Card originalTarget = Iterables.getFirst(getTargetCards(chosenSA), null); valid.remove(originalTarget); mayChooseNewTargets = false; - for (final Card c : valid) { + if (sa.hasParam("ChooseOnlyOne")) { + Card choice = controller.getController().chooseSingleEntityForEffect(valid, sa, "Choose one"); SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true); - resetFirstTargetOnCopy(copy, c, targetedSA); + resetFirstTargetOnCopy(copy, choice, targetedSA); copies.add(copy); + } else { + for (final Card c : valid) { + SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true); + resetFirstTargetOnCopy(copy, c, targetedSA); + copies.add(copy); + } } for (final Player p : players) { SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true); diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCast.java b/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCast.java index 74646a8b98b..cd6ceb7ceab 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCast.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCast.java @@ -24,8 +24,10 @@ import java.util.Set; import com.google.common.collect.Sets; import forge.game.Game; +import forge.game.GameEntity; import forge.game.GameObject; import forge.game.card.Card; +import forge.game.card.CardCollection; import forge.game.card.CardLists; import forge.game.card.CardUtil; import forge.game.cost.Cost; @@ -158,6 +160,31 @@ public class TriggerSpellAbilityCast extends Trigger { } } + if (hasParam("CanTargetOtherCondition")) { + final CardCollection candidates = new CardCollection(); + SpellAbility targetedSA = spellAbility; + while (targetedSA != null) { + if (targetedSA.usesTargeting() && targetedSA.getTargets().getNumTargeted() != 0) { + break; + } + targetedSA = targetedSA.getSubAbility(); + } + if (targetedSA == null) { + return false; + } + final List candidateTargets = targetedSA.getTargetRestrictions().getAllCandidates(targetedSA, true); + for (GameEntity card : candidateTargets) { + if (card instanceof Card) { + candidates.add((Card) card); + } + } + candidates.removeAll(targetedSA.getTargets().getTargetCards()); + String valid = this.mapParams.get("CanTargetOtherCondition"); + if (CardLists.getValidCards(candidates, valid, spellAbility.getActivatingPlayer(), spellAbility.getHostCard()).isEmpty()) { + return false; + } + } + if (hasParam("NonTapCost")) { final Cost cost = (Cost) (runParams2.get("Cost")); if (cost.hasTapCost()) { diff --git a/forge-gui/res/cardsfolder/b/beamsplitter_mage.txt b/forge-gui/res/cardsfolder/b/beamsplitter_mage.txt new file mode 100644 index 00000000000..66f2bc8fe77 --- /dev/null +++ b/forge-gui/res/cardsfolder/b/beamsplitter_mage.txt @@ -0,0 +1,7 @@ +Name:Beamsplitter Mage +ManaCost:U R +Types:Creature Vedalken Wizard +PT:2/2 +T:Mode$ SpellCast | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | IsSingleTarget$ True | TargetsValid$ Card.Self | CanTargetOtherCondition$ Creature.YouCtrl | Execute$ TrigCopy | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast an instant or sorcery that targets only CARDNAME, if you control one or more other creatures that spell could target, choose one of those creatures. Copy that spell. The copy targets the chosen creature. +SVar:TrigCopy:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | Controller$ You | CopyForEachCanTarget$ Creature.YouCtrl | ChooseOnlyOne$ True +Oracle:Whenever you cast an instant or sorcery spell that targets only Beamsplitter Mage, if you control one or more other creatures that spell could target, choose one of those creatures. Copy that spell. The copy targets the chosen creature. From c2d0648655c3a59322347f1188eb2c2407575fd0 Mon Sep 17 00:00:00 2001 From: swordshine Date: Sun, 7 Oct 2018 19:29:27 +0800 Subject: [PATCH 3/3] - Added Venerated Loxodon --- .../src/main/java/forge/game/GameAction.java | 1 + .../src/main/java/forge/game/card/Card.java | 16 +++++++++++++++- .../main/java/forge/game/card/CardProperty.java | 4 ++++ .../java/forge/game/cost/CostAdjustment.java | 4 ++++ .../res/cardsfolder/v/venerated_loxodon.txt | 8 ++++++++ 5 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 forge-gui/res/cardsfolder/v/venerated_loxodon.txt diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index be3959b2ac7..12d19649791 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -428,6 +428,7 @@ public class GameAction { if (!c.isToken() && !toBattlefield) { copied.clearDevoured(); copied.clearDelved(); + copied.clearConvoked(); } // rule 504.6: reveal a face-down card leaving the stack diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index f56315a4a29..1a9ed880a56 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -98,7 +98,7 @@ public class Card extends GameEntity implements Comparable { private final KeywordCollection hiddenExtrinsicKeyword = new KeywordCollection(); // cards attached or otherwise linked to this card - private CardCollection equippedBy, fortifiedBy, hauntedBy, devouredCards, delvedCards, imprintedCards, encodedCards; + private CardCollection equippedBy, fortifiedBy, hauntedBy, devouredCards, delvedCards, convokedCards, imprintedCards, encodedCards; private CardCollection mustBlockCards, clones, gainControlTargets, chosenCards, blockedThisTurn, blockedByThisTurn; // if this card is attached or linked to something, what card is it currently attached to @@ -725,6 +725,20 @@ public class Card extends GameEntity implements Comparable { delvedCards = null; } + + public final CardCollectionView getConvoked() { + return CardCollection.getView(convokedCards); + } + public final void addConvoked(final Card c) { + if (convokedCards == null) { + convokedCards = new CardCollection(); + } + convokedCards.add(c); + } + public final void clearConvoked() { + convokedCards = null; + } + public MapOfLists getRememberMap() { return rememberMap; } diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 4a480acb239..d2be541192c 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -1378,6 +1378,10 @@ public class CardProperty { if (!source.getDelved().contains(card)) { return false; } + } else if (property.startsWith("convoked")) { + if (!source.getConvoked().contains(card)) { + return false; + } } else if (property.startsWith("unequalPT")) { if (card.getNetPower() == card.getNetToughness()) { return false; diff --git a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java index 70d6c91083d..455949c58d5 100644 --- a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java +++ b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java @@ -237,6 +237,7 @@ public class CostAdjustment { } } if (sa.getHostCard().hasKeyword(Keyword.CONVOKE)) { + sa.getHostCard().clearConvoked(); adjustCostByConvokeOrImprovise(cost, sa, false, test); } if (sa.getHostCard().hasKeyword(Keyword.IMPROVISE)) { @@ -270,6 +271,9 @@ public class CostAdjustment { cost.decreaseShard(conv.getValue(), 1); if (!test) { conv.getKey().tap(); + if (!improvise) { + sa.getHostCard().addConvoked(conv.getKey()); + } } } } diff --git a/forge-gui/res/cardsfolder/v/venerated_loxodon.txt b/forge-gui/res/cardsfolder/v/venerated_loxodon.txt new file mode 100644 index 00000000000..f5add8bf2ce --- /dev/null +++ b/forge-gui/res/cardsfolder/v/venerated_loxodon.txt @@ -0,0 +1,8 @@ +Name:Venerated Loxodon +ManaCost:4 W +Types:Creature Elephant Cleric +PT:4/4 +K:Convoke +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPutCounterAll | TriggerDescription$ When CARDNAME enters the battlefield, put a +1/+1 counter on each creature that convoked it. +SVar:TrigPutCounterAll:DB$ PutCounterAll | ValidCards$ Creature.convoked | CounterType$ P1P1 | CounterNum$ 1 +Oracle:Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of the creature's color.)\nWhen Venerated Loxodon enters the battlefield, put a +1/+1 counter on each creature that convoked it.