From 08d984a699796747503df37bb87b3d418e5bef93 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Sun, 5 Aug 2018 22:08:15 +0200 Subject: [PATCH 1/3] TokenEffect: Add AttachTo effect for Estrid --- .../src/main/java/forge/game/GameEntity.java | 12 +++ .../game/ability/effects/TokenEffect.java | 77 +++++++++++++++++++ .../src/main/java/forge/game/card/Card.java | 10 --- .../upcoming/estrid_the_masked.txt | 14 ++++ forge-gui/res/lists/TypeLists.txt | 3 + 5 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 forge-gui/res/cardsfolder/upcoming/estrid_the_masked.txt diff --git a/forge-game/src/main/java/forge/game/GameEntity.java b/forge-game/src/main/java/forge/game/GameEntity.java index 62689b9af54..37a27344866 100644 --- a/forge-game/src/main/java/forge/game/GameEntity.java +++ b/forge-game/src/main/java/forge/game/GameEntity.java @@ -27,6 +27,7 @@ import forge.game.event.GameEventCardAttachment.AttachMethod; import forge.game.keyword.Keyword; import forge.game.player.Player; import forge.game.spellability.SpellAbility; +import forge.game.spellability.TargetRestrictions; import forge.game.trigger.TriggerType; import forge.util.collect.FCollection; @@ -338,6 +339,17 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { } } + public boolean canBeEnchantedBy(final Card aura) { + SpellAbility sa = aura.getFirstAttachSpell(); + TargetRestrictions tgt = null; + if (sa != null) { + tgt = sa.getTargetRestrictions(); + } + + return !(hasProtectionFrom(aura) + || ((tgt != null) && !isValid(tgt.getValidTgts(), aura.getController(), aura, sa))); + } + public abstract boolean hasProtectionFrom(final Card source); // Counters! diff --git a/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java index d4850c928eb..8f6cc9b9d5d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java @@ -27,14 +27,18 @@ import org.apache.commons.lang3.StringUtils; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import forge.card.CardType; import forge.game.Game; import forge.game.GameEntity; +import forge.game.GameObject; import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; +import forge.game.card.CardCollection; +import forge.game.card.CardUtil; import forge.game.combat.Combat; import forge.game.event.GameEventCombatChanged; import forge.game.event.GameEventTokenCreated; @@ -42,6 +46,7 @@ import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.trigger.Trigger; import forge.game.trigger.TriggerHandler; +import forge.game.zone.ZoneType; import forge.item.PaperToken; import forge.util.collect.FCollectionView; import forge.util.MyRandom; @@ -291,6 +296,78 @@ public class TokenEffect extends SpellAbilityEffect { if (this.tokenTapped) { tok.setTapped(true); } + + if (sa.hasParam("AttachedTo")) { + GameObject aTo = Iterables.getFirst( + AbilityUtils.getDefinedObjects(host, sa.getParam("AttachedTo"), sa), null); + + if (aTo instanceof GameEntity) { + GameEntity ge = (GameEntity)aTo; + // check what the token would be on the battlefield + Card lki = CardUtil.getLKICopy(tok); + + lki.setLastKnownZone(tok.getController().getZone(ZoneType.Battlefield)); + + CardCollection preList = new CardCollection(lki); + game.getAction().checkStaticAbilities(false, Sets.newHashSet(lki), preList); + + // TODO update when doing Attach Update + boolean canAttach = lki.isAura() || lki.isEquipment() || lki.isFortification(); + + if (lki.isAura()) { + if (!ge.canBeEnchantedBy(lki)) { + canAttach = false; + } + } + if (lki.isEquipment()) { + if (ge instanceof Card) { + Card gc = (Card) ge; + if (!gc.canBeEquippedBy(lki)) { + canAttach = false; + } + } else { + canAttach = false; + } + } + if (lki.isFortification()) { + if (ge instanceof Card) { + Card gc = (Card) ge; + if (!gc.isLand()) { + canAttach = false; + } + } else { + canAttach = false; + } + } + + // reset static abilities + game.getAction().checkStaticAbilities(false); + + if (!canAttach) { + // Token can't attach it + continue; + } + + // TODO update when doing Attach Update + if (lki.isAura()) { + tok.enchantEntity(ge); + } else if (lki.isEquipment()) { + if (ge instanceof Card) { + Card gc = (Card) ge; + tok.equipCard(gc); + } + } else if (lki.isFortification()) { + if (ge instanceof Card) { + Card gc = (Card) ge; + tok.fortifyCard(gc); + } + } + } else { + // not a GameEntity, cant be attach + continue; + } + } + // Should this be catching the Card that's returned? Card c = game.getAction().moveToPlay(tok, sa); 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 0c091a5266c..84f0b25bc1b 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -5151,16 +5151,6 @@ public class Card extends GameEntity implements Comparable { result.setFalse(); } break; - case "CARDNAME can't be enchanted.": - if (source.isAura()) { - result.setFalse(); - } - break; - case "CARDNAME can't be equipped.": - if (source.isEquipment()) { - result.setFalse(); - } - break; case "CARDNAME can't be the target of spells.": if (sa.isSpell()) { result.setFalse(); diff --git a/forge-gui/res/cardsfolder/upcoming/estrid_the_masked.txt b/forge-gui/res/cardsfolder/upcoming/estrid_the_masked.txt new file mode 100644 index 00000000000..592315eed37 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/estrid_the_masked.txt @@ -0,0 +1,14 @@ +Name:Estrid, the Masked +ManaCost:1 G W U +Types:Legendary Planeswalker Estrid +Loyalty:3 +A:AB$ Untap | Cost$ AddCounter<2/LOYALTY> | Planeswalker$ True | Defined$ Permanent.enchanted+YouCtrl | SpellDescription$ Untap each enchanted permanent you control. +SVar:BuffedBy:Permanent.enchanted +A:AB$ Token | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenName$ Mask | TokenImage$ mask | TokenTypes$ Enchantment,Aura | TokenOwner$ You | TokenColors$ White | TokenKeywords$ Enchant permanent<>Totem armor | TokenAbilities$ DBFirstAttach | AttachedTo$ Targeted | ValidTgts$ Permanent.Other | TgtPrompt$ Select target permanent to attach Mask Token | SpellDescription$ Create a white Aura enchantment token named Mask attached to another target permanent. The token has enchant permanent and totem armor. +SVar:DBFirstAttach:SP$ Attach | Cost$ 0 | ValidTgts$ Permanent | AILogic$ Pump +A:AB$ Mill | Cost$ SubCounter<7/LOYALTY> | Planeswalker$ True | Ultimate$ True | NumCards$ 7 | Defined$ You | SubAbility$ DBChangeZone | SpellDescription$ Put the top seven cards of your library into your graveyard. Return all non-Aura enchantment cards from your graveyard to the battlefield, then do the same for Aura cards. +SVar:DBChangeZone:DB$ ChangeZoneAll | ChangeType$ Enchantment.nonAura+YouCtrl | Origin$ Graveyard | Destination$ Battlefield | SubAbility$ DBChangeZone2 +SVar:DBChangeZone2:DB$ ChangeZoneAll | ChangeType$ Enchantment.Aura+YouCtrl | Origin$ Graveyard | Destination$ Battlefield +K:CARDNAME can be your commander. +Oracle:[+2]: Untap each enchanted permanent you control.\n[-1]: Create a white Aura enchantment token named Mask attached to another target permanent. The token has enchant permanent and totem armor.\n[-7]: Put the top seven cards of your library into your graveyard. Return all non-Aura enchantment cards from your graveyard to the battlefield, then do the same for Aura cards.\nEstrid, the Masked can be your commander. + diff --git a/forge-gui/res/lists/TypeLists.txt b/forge-gui/res/lists/TypeLists.txt index dad33cd2671..34b360d6246 100644 --- a/forge-gui/res/lists/TypeLists.txt +++ b/forge-gui/res/lists/TypeLists.txt @@ -272,6 +272,7 @@ Treasure Vehicle [WalkerTypes] Ajani +Aminatou Angrath Arlinn Ashiok @@ -281,6 +282,7 @@ Dack Daretti Domri Dovin +Estrid Elspeth Freyalise Garruk @@ -311,6 +313,7 @@ Ugin Venser Vraska Will +Windgrace Xenagos Yanggu Yanling From 197880dda42da39746135ebe77a267e62dbfda18 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Mon, 6 Aug 2018 07:20:30 +0200 Subject: [PATCH 2/3] TokenEffect: refactor attachTo --- .../game/ability/effects/TokenEffect.java | 146 +++++++++--------- 1 file changed, 77 insertions(+), 69 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java index 8f6cc9b9d5d..6a3ee323531 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java @@ -297,75 +297,8 @@ public class TokenEffect extends SpellAbilityEffect { tok.setTapped(true); } - if (sa.hasParam("AttachedTo")) { - GameObject aTo = Iterables.getFirst( - AbilityUtils.getDefinedObjects(host, sa.getParam("AttachedTo"), sa), null); - - if (aTo instanceof GameEntity) { - GameEntity ge = (GameEntity)aTo; - // check what the token would be on the battlefield - Card lki = CardUtil.getLKICopy(tok); - - lki.setLastKnownZone(tok.getController().getZone(ZoneType.Battlefield)); - - CardCollection preList = new CardCollection(lki); - game.getAction().checkStaticAbilities(false, Sets.newHashSet(lki), preList); - - // TODO update when doing Attach Update - boolean canAttach = lki.isAura() || lki.isEquipment() || lki.isFortification(); - - if (lki.isAura()) { - if (!ge.canBeEnchantedBy(lki)) { - canAttach = false; - } - } - if (lki.isEquipment()) { - if (ge instanceof Card) { - Card gc = (Card) ge; - if (!gc.canBeEquippedBy(lki)) { - canAttach = false; - } - } else { - canAttach = false; - } - } - if (lki.isFortification()) { - if (ge instanceof Card) { - Card gc = (Card) ge; - if (!gc.isLand()) { - canAttach = false; - } - } else { - canAttach = false; - } - } - - // reset static abilities - game.getAction().checkStaticAbilities(false); - - if (!canAttach) { - // Token can't attach it - continue; - } - - // TODO update when doing Attach Update - if (lki.isAura()) { - tok.enchantEntity(ge); - } else if (lki.isEquipment()) { - if (ge instanceof Card) { - Card gc = (Card) ge; - tok.equipCard(gc); - } - } else if (lki.isFortification()) { - if (ge instanceof Card) { - Card gc = (Card) ge; - tok.fortifyCard(gc); - } - } - } else { - // not a GameEntity, cant be attach - continue; - } + if (sa.hasParam("AttachedTo") && !attachTokenTo(tok, sa)) { + continue; } // Should this be catching the Card that's returned? @@ -546,4 +479,79 @@ public class TokenEffect extends SpellAbilityEffect { } return combatChanged; } + + private boolean attachTokenTo(Card tok, SpellAbility sa) { + final Card host = sa.getHostCard(); + final Game game = host.getGame(); + + GameObject aTo = Iterables.getFirst( + AbilityUtils.getDefinedObjects(host, sa.getParam("AttachedTo"), sa), null); + + if (aTo instanceof GameEntity) { + GameEntity ge = (GameEntity)aTo; + // check what the token would be on the battlefield + Card lki = CardUtil.getLKICopy(tok); + + lki.setLastKnownZone(tok.getController().getZone(ZoneType.Battlefield)); + + CardCollection preList = new CardCollection(lki); + game.getAction().checkStaticAbilities(false, Sets.newHashSet(lki), preList); + + // TODO update when doing Attach Update + boolean canAttach = lki.isAura() || lki.isEquipment() || lki.isFortification(); + + if (lki.isAura()) { + if (!ge.canBeEnchantedBy(lki)) { + canAttach = false; + } + } + if (lki.isEquipment()) { + if (ge instanceof Card) { + Card gc = (Card) ge; + if (!gc.canBeEquippedBy(lki)) { + canAttach = false; + } + } else { + canAttach = false; + } + } + if (lki.isFortification()) { + if (ge instanceof Card) { + Card gc = (Card) ge; + if (!gc.isLand()) { + canAttach = false; + } + } else { + canAttach = false; + } + } + + // reset static abilities + game.getAction().checkStaticAbilities(false); + + if (!canAttach) { + // Token can't attach it + return false; + } + + // TODO update when doing Attach Update + if (lki.isAura()) { + tok.enchantEntity(ge); + } else if (lki.isEquipment()) { + if (ge instanceof Card) { + Card gc = (Card) ge; + tok.equipCard(gc); + } + } else if (lki.isFortification()) { + if (ge instanceof Card) { + Card gc = (Card) ge; + tok.fortifyCard(gc); + } + } + return true; + } else { + // not a GameEntity, cant be attach + return false; + } + } } From a98fdff570f59bdc616c380f94e209d7fc476ded Mon Sep 17 00:00:00 2001 From: Hanmac Date: Mon, 6 Aug 2018 13:25:09 +0200 Subject: [PATCH 3/3] Estrid the Masked: fixed Untap Effect --- forge-gui/res/cardsfolder/upcoming/estrid_the_masked.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/upcoming/estrid_the_masked.txt b/forge-gui/res/cardsfolder/upcoming/estrid_the_masked.txt index 592315eed37..ffa95c6666f 100644 --- a/forge-gui/res/cardsfolder/upcoming/estrid_the_masked.txt +++ b/forge-gui/res/cardsfolder/upcoming/estrid_the_masked.txt @@ -2,7 +2,7 @@ Name:Estrid, the Masked ManaCost:1 G W U Types:Legendary Planeswalker Estrid Loyalty:3 -A:AB$ Untap | Cost$ AddCounter<2/LOYALTY> | Planeswalker$ True | Defined$ Permanent.enchanted+YouCtrl | SpellDescription$ Untap each enchanted permanent you control. +A:AB$ UntapAll | Cost$ AddCounter<2/LOYALTY> | Planeswalker$ True | ValidCards$ Permanent.enchanted+YouCtrl | SpellDescription$ Untap each enchanted permanent you control. SVar:BuffedBy:Permanent.enchanted A:AB$ Token | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenName$ Mask | TokenImage$ mask | TokenTypes$ Enchantment,Aura | TokenOwner$ You | TokenColors$ White | TokenKeywords$ Enchant permanent<>Totem armor | TokenAbilities$ DBFirstAttach | AttachedTo$ Targeted | ValidTgts$ Permanent.Other | TgtPrompt$ Select target permanent to attach Mask Token | SpellDescription$ Create a white Aura enchantment token named Mask attached to another target permanent. The token has enchant permanent and totem armor. SVar:DBFirstAttach:SP$ Attach | Cost$ 0 | ValidTgts$ Permanent | AILogic$ Pump