Merge branch 'tokenAttach' into 'master'

TokenEffect: Add AttachTo effect for Estrid

See merge request core-developers/forge!839
This commit is contained in:
Michael Kamensky
2018-08-06 12:20:07 +00:00
5 changed files with 114 additions and 10 deletions

View File

@@ -27,6 +27,7 @@ import forge.game.event.GameEventCardAttachment.AttachMethod;
import forge.game.keyword.Keyword; import forge.game.keyword.Keyword;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.trigger.TriggerType; import forge.game.trigger.TriggerType;
import forge.util.collect.FCollection; 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); public abstract boolean hasProtectionFrom(final Card source);
// Counters! // Counters!

View File

@@ -27,14 +27,18 @@ import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import forge.card.CardType; import forge.card.CardType;
import forge.game.Game; import forge.game.Game;
import forge.game.GameEntity; import forge.game.GameEntity;
import forge.game.GameObject;
import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect; import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardUtil;
import forge.game.combat.Combat; import forge.game.combat.Combat;
import forge.game.event.GameEventCombatChanged; import forge.game.event.GameEventCombatChanged;
import forge.game.event.GameEventTokenCreated; import forge.game.event.GameEventTokenCreated;
@@ -42,6 +46,7 @@ import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler; import forge.game.trigger.TriggerHandler;
import forge.game.zone.ZoneType;
import forge.item.PaperToken; import forge.item.PaperToken;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
import forge.util.MyRandom; import forge.util.MyRandom;
@@ -291,6 +296,11 @@ public class TokenEffect extends SpellAbilityEffect {
if (this.tokenTapped) { if (this.tokenTapped) {
tok.setTapped(true); tok.setTapped(true);
} }
if (sa.hasParam("AttachedTo") && !attachTokenTo(tok, sa)) {
continue;
}
// Should this be catching the Card that's returned? // Should this be catching the Card that's returned?
Card c = game.getAction().moveToPlay(tok, sa); Card c = game.getAction().moveToPlay(tok, sa);
@@ -469,4 +479,79 @@ public class TokenEffect extends SpellAbilityEffect {
} }
return combatChanged; 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;
}
}
} }

View File

@@ -5151,16 +5151,6 @@ public class Card extends GameEntity implements Comparable<Card> {
result.setFalse(); result.setFalse();
} }
break; 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.": case "CARDNAME can't be the target of spells.":
if (sa.isSpell()) { if (sa.isSpell()) {
result.setFalse(); result.setFalse();

View File

@@ -0,0 +1,14 @@
Name:Estrid, the Masked
ManaCost:1 G W U
Types:Legendary Planeswalker Estrid
Loyalty:3
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
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.

View File

@@ -272,6 +272,7 @@ Treasure
Vehicle Vehicle
[WalkerTypes] [WalkerTypes]
Ajani Ajani
Aminatou
Angrath Angrath
Arlinn Arlinn
Ashiok Ashiok
@@ -281,6 +282,7 @@ Dack
Daretti Daretti
Domri Domri
Dovin Dovin
Estrid
Elspeth Elspeth
Freyalise Freyalise
Garruk Garruk
@@ -311,6 +313,7 @@ Ugin
Venser Venser
Vraska Vraska
Will Will
Windgrace
Xenagos Xenagos
Yanggu Yanggu
Yanling Yanling