diff --git a/Mage.Sets/src/mage/cards/g/GoldbugHumanitysAlly.java b/Mage.Sets/src/mage/cards/g/GoldbugHumanitysAlly.java new file mode 100644 index 0000000000..7f00881bbe --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoldbugHumanitysAlly.java @@ -0,0 +1,63 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.CastSecondSpellTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.PreventAllDamageToAllEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.MoreThanMeetsTheEyeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.AttackingPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoldbugHumanitysAlly extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent(SubType.HUMAN, "attacking Humans you control"); + + static { + filter.add(AttackingPredicate.instance); + } + + public GoldbugHumanitysAlly(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{W}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ROBOT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + this.secondSideCardClazz = mage.cards.g.GoldbugScrappyScout.class; + + // More Than Meets the Eye {W}{U} + this.addAbility(new MoreThanMeetsTheEyeAbility(this, "{W}{U}")); + + // Prevent all combat damage that would be dealt to attacking Humans you control. + this.addAbility(new SimpleStaticAbility(new PreventAllDamageToAllEffect( + Duration.WhileOnBattlefield, filter, true + ))); + + // Whenever you cast your second spell each turn, convert Goldbug. + this.addAbility(new CastSecondSpellTriggeredAbility(new TransformSourceEffect().setText("convert {this}"))); + } + + private GoldbugHumanitysAlly(final GoldbugHumanitysAlly card) { + super(card); + } + + @Override + public GoldbugHumanitysAlly copy() { + return new GoldbugHumanitysAlly(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoldbugScrappyScout.java b/Mage.Sets/src/mage/cards/g/GoldbugScrappyScout.java new file mode 100644 index 0000000000..59348487c9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoldbugScrappyScout.java @@ -0,0 +1,104 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CantBeCounteredControlledEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.LivingMetalAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterSpell; +import mage.game.Game; +import mage.game.events.GameEvent; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoldbugScrappyScout extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("Human spells"); + + static { + filter.add(SubType.HUMAN.getPredicate()); + } + + public GoldbugScrappyScout(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, ""); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + this.color.setWhite(true); + this.color.setBlue(true); + this.nightCard = true; + + // Living metal + this.addAbility(new LivingMetalAbility()); + + // Human spells you control can't be countered. + this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect( + filter, Duration.WhileOnBattlefield + ))); + + // Whenever Goldbug and at least one Human attack, draw a card and convert Goldbug. + this.addAbility(new GoldbugScrappyScoutTriggeredAbility()); + } + + private GoldbugScrappyScout(final GoldbugScrappyScout card) { + super(card); + } + + @Override + public GoldbugScrappyScout copy() { + return new GoldbugScrappyScout(this); + } +} + +class GoldbugScrappyScoutTriggeredAbility extends TriggeredAbilityImpl { + + GoldbugScrappyScoutTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1)); + this.addEffect(new TransformSourceEffect()); + } + + private GoldbugScrappyScoutTriggeredAbility(final GoldbugScrappyScoutTriggeredAbility ability) { + super(ability); + } + + @Override + public GoldbugScrappyScoutTriggeredAbility copy() { + return new GoldbugScrappyScoutTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return game + .getCombat() + .getAttackers() + .contains(getSourceId()) + && game + .getCombat() + .getAttackers() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> permanent.hasSubtype(SubType.HUMAN, game)); + } + + @Override + public String getRule() { + return "Whenever {this} and at least one Human attack, draw a card and convert {this}."; + } +} diff --git a/Mage.Sets/src/mage/sets/Transformers.java b/Mage.Sets/src/mage/sets/Transformers.java index f2fd1730fe..482b00b78c 100644 --- a/Mage.Sets/src/mage/sets/Transformers.java +++ b/Mage.Sets/src/mage/sets/Transformers.java @@ -1,6 +1,7 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.constants.Rarity; import mage.constants.SetType; /** @@ -17,5 +18,8 @@ public final class Transformers extends ExpansionSet { private Transformers() { super("Transformers", "BOT", ExpansionSet.buildDate(2022, 11, 18), SetType.SUPPLEMENTAL); this.hasBasicLands = false; + + cards.add(new SetCardInfo("Goldbug, Humanity's Ally", 11, Rarity.MYTHIC, mage.cards.g.GoldbugHumanitysAlly.class)); + cards.add(new SetCardInfo("Goldbug, Scrappy Scout", 11, Rarity.MYTHIC, mage.cards.g.GoldbugScrappyScout.class)); } } diff --git a/Mage/src/main/java/mage/abilities/keyword/LivingMetalAbility.java b/Mage/src/main/java/mage/abilities/keyword/LivingMetalAbility.java new file mode 100644 index 0000000000..9504e5b3c0 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/LivingMetalAbility.java @@ -0,0 +1,36 @@ +package mage.abilities.keyword; + +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.AddCardTypeSourceEffect; +import mage.abilities.hint.common.MyTurnHint; +import mage.constants.CardType; +import mage.constants.Duration; + +/** + * @author TheElk801 + */ +public class LivingMetalAbility extends EntersBattlefieldTriggeredAbility { + + public LivingMetalAbility() { + super(new ConditionalContinuousEffect(new AddCardTypeSourceEffect( + Duration.WhileOnBattlefield, CardType.ARTIFACT, CardType.CREATURE + ), MyTurnCondition.instance, "")); + this.addHint(MyTurnHint.instance); + } + + public LivingMetalAbility(final LivingMetalAbility ability) { + super(ability); + } + + @Override + public LivingMetalAbility copy() { + return new LivingMetalAbility(this); + } + + @Override + public String getRule() { + return "Living metal (As long as it's your turn, this Vehicle is also a creature.)"; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/MoreThanMeetsTheEyeAbility.java b/Mage/src/main/java/mage/abilities/keyword/MoreThanMeetsTheEyeAbility.java new file mode 100644 index 0000000000..fac02e3cf4 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/MoreThanMeetsTheEyeAbility.java @@ -0,0 +1,96 @@ +package mage.abilities.keyword; + +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.cards.Card; +import mage.constants.*; +import mage.game.Game; +import mage.game.stack.Spell; + +/** + * @author weirddan455, JayDi85, TheElk801 (based heavily on disturb) + */ +public class MoreThanMeetsTheEyeAbility extends SpellAbility { + + private final String manaCost; + private SpellAbility spellAbilityToResolve; + + public MoreThanMeetsTheEyeAbility(Card card, String manaCost) { + super(card.getSecondFaceSpellAbility()); + this.newId(); + + // getSecondFaceSpellAbility() already verified that second face exists + this.setCardName(card.getSecondCardFace().getName() + " with Disturb"); + this.spellAbilityType = SpellAbilityType.BASE_ALTERNATE; + this.spellAbilityCastMode = SpellAbilityCastMode.DISTURB; + + this.manaCost = manaCost; + this.getManaCosts().clear(); + this.getManaCostsToPay().clear(); + this.addManaCost(new ManaCostsImpl<>(manaCost)); + this.addSubAbility(new TransformAbility()); + } + + private MoreThanMeetsTheEyeAbility(final MoreThanMeetsTheEyeAbility ability) { + super(ability); + this.manaCost = ability.manaCost; + this.spellAbilityToResolve = ability.spellAbilityToResolve; + } + + @Override + public MoreThanMeetsTheEyeAbility copy() { + return new MoreThanMeetsTheEyeAbility(this); + } + + @Override + public boolean activate(Game game, boolean noMana) { + if (super.activate(game, noMana)) { + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getSourceId(), Boolean.TRUE); + // TODO: must be removed after transform cards (one side) migrated to MDF engine (multiple sides) + game.addEffect(new MoreThanMeetsTheEyeEffect(), this); + return true; + } + return false; + } + + @Override + public String getRule(boolean all) { + return this.getRule(); + } + + @Override + public String getRule() { + return "More Than Meets The Eye " + this.manaCost + + " (You may cast this card converted for" + this.manaCost + ".)"; + } +} + +class MoreThanMeetsTheEyeEffect extends ContinuousEffectImpl { + + public MoreThanMeetsTheEyeEffect() { + super(Duration.WhileOnStack, Layer.CopyEffects_1, SubLayer.CopyEffects_1a, Outcome.BecomeCreature); + staticText = ""; + } + + private MoreThanMeetsTheEyeEffect(final MoreThanMeetsTheEyeEffect effect) { + super(effect); + } + + @Override + public MoreThanMeetsTheEyeEffect copy() { + return new MoreThanMeetsTheEyeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = game.getSpell(source.getSourceId()); + if (spell == null || spell.getCard().getSecondCardFace() == null) { + return false; + } + // simulate another side as new card (another code part in spell constructor) + TransformAbility.transformCardSpellDynamic(spell, spell.getCard().getSecondCardFace(), game); + return true; + } +} diff --git a/Utils/keywords.txt b/Utils/keywords.txt index de11461294..e82af89e24 100644 --- a/Utils/keywords.txt +++ b/Utils/keywords.txt @@ -68,6 +68,7 @@ Jump-start|card| Kicker|manaString| Level up|cost| Lifelink|instance| +Living metal|new| Living weapon|new| Madness|cost| Melee|new| @@ -77,6 +78,7 @@ Miracle|manaString| Modular|card, number| Mountaincycling|cost| Mountainwalk|new| +More Than Meets The Eye|card, manaString| Morph|cost| Mutate|card, manaString| Myriad|new| diff --git a/Utils/known-sets.txt b/Utils/known-sets.txt index 1a881b1a31..67243862d7 100644 --- a/Utils/known-sets.txt +++ b/Utils/known-sets.txt @@ -222,6 +222,7 @@ Throne of Eldraine|ThroneOfEldraine| Time Spiral|TimeSpiral| Time Spiral "Timeshifted"|TimeSpiralTimeshifted| Torment|Torment| +Transformers|Transformers| Ugin's Fate|UginsFate| Unglued|Unglued| Unhinged|Unhinged| diff --git a/Utils/mtg-sets-data.txt b/Utils/mtg-sets-data.txt index 4efab5a564..e9631293a3 100644 --- a/Utils/mtg-sets-data.txt +++ b/Utils/mtg-sets-data.txt @@ -218,6 +218,7 @@ Torment|TOR| Tempest Remastered|TPR| Time Spiral "Timeshifted"|TSB| Time Spiral|TSP| +Transformers|BOT| Urza's Destiny|UDS| Ugin's Fate|UGIN| Unglued|UGL|