diff --git a/Mage.Sets/src/mage/cards/a/AchHansRun.java b/Mage.Sets/src/mage/cards/a/AchHansRun.java new file mode 100644 index 0000000000..14a0b6d200 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AchHansRun.java @@ -0,0 +1,107 @@ + +package mage.cards.a; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.repository.CardRepository; +import mage.choices.ChoiceImpl; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author L_J + */ +public final class AchHansRun extends CardImpl { + + public AchHansRun(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}{R}{G}{G}"); + + // At the beginning of your upkeep, you may say "Ach! Hans, run! It’s the …" and the name of a creature card. If you do, search your library for a card with that name, put it onto the battlefield, then shuffle your library. That creature gains haste. Exile it at the beginning of the next end step. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new AchHansRunEffect(), TargetController.YOU, true)); + } + + public AchHansRun(final AchHansRun card) { + super(card); + } + + @Override + public AchHansRun copy() { + return new AchHansRun(this); + } +} + +class AchHansRunEffect extends OneShotEffect { + + public AchHansRunEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "you may say \"Ach! Hans, run! It’s the …\" and the name of a creature card. If you do, search your library for a card with that name, put it onto the battlefield, then shuffle your library. That creature gains haste. Exile it at the beginning of the next end step"; + } + + public AchHansRunEffect(final AchHansRunEffect effect) { + super(effect); + } + + @Override + public AchHansRunEffect copy() { + return new AchHansRunEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + ChoiceImpl cardChoice = new ChoiceImpl(true); + cardChoice.setChoices(CardRepository.instance.getCreatureNames()); + cardChoice.setMessage("Choose a creature card name"); + if (controller.choose(Outcome.Detriment, cardChoice, game)) { + String cardName = cardChoice.getChoice(); + if (!game.isSimulation()) { + game.informPlayers(controller.getLogName() + ": \"Ach! Hans, run! It's the " + cardName + "!\""); + } + FilterCard nameFilter = new FilterCard(); + nameFilter.add(new NamePredicate(cardName)); + TargetCardInLibrary target = new TargetCardInLibrary(1, 1, nameFilter); + if (controller.searchLibrary(target, game)) { + Card card = controller.getLibrary().remove(target.getFirstTarget(), game); + if (card != null) { + if (card != null && controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { + Permanent creature = game.getPermanent(card.getId()); + if (creature != null) { + // gains haste + ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); + effect.setTargetPointer(new FixedTarget(creature, game)); + game.addEffect(effect, source); + // Exile at begin of next end step + ExileTargetEffect exileEffect = new ExileTargetEffect(null, null, Zone.BATTLEFIELD); + exileEffect.setTargetPointer(new FixedTarget(creature, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + game.addDelayedTriggeredAbility(delayedAbility, source); + } + } + } + controller.shuffleLibrary(source, game); + } + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BINGO.java b/Mage.Sets/src/mage/cards/b/BINGO.java new file mode 100644 index 0000000000..185acbd19d --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BINGO.java @@ -0,0 +1,186 @@ + +package mage.cards.b; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterSpell; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.util.CardUtil; + +/** + * + * @author L_J + */ +public final class BINGO extends CardImpl { + + public BINGO(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}"); + this.subtype.add(SubType.HOUND); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever a player casts a spell, put a chip counter on its converted mana cost. + this.addAbility(new SpellCastAllTriggeredAbility(new BingoEffect(), new FilterSpell("a spell"), false, SetTargetPointer.SPELL)); + + // B-I-N-G-O gets +9/+9 for each set of three numbers in a row with chip counters on them. + BingoCount count = new BingoCount(); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(count, count, Duration.WhileOnBattlefield))); + } + + public BINGO(final BINGO card) { + super(card); + } + + @Override + public BINGO copy() { + return new BINGO(this); + } +} + +class BingoEffect extends OneShotEffect { + + public BingoEffect() { + super(Outcome.Neutral); + staticText = "put a chip counter on its converted mana cost"; + } + + public BingoEffect(final BingoEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = game.getStack().getSpell(this.getTargetPointer().getFirst(game, source)); + if (spell != null) { + if (spell.getConvertedManaCost() > 9) { + return true; + } + MageObject mageObject = game.getObject(source.getSourceId()); + if (mageObject != null) { + Map chipCounters = new HashMap<>(); // Map + if (game.getState().getValue(mageObject.getId() + "_chip") != null) { + chipCounters.putAll((Map) game.getState().getValue(mageObject.getId() + "_chip")); + } + chipCounters.putIfAbsent(spell.getConvertedManaCost(), 0); + chipCounters.put(spell.getConvertedManaCost(), chipCounters.get(spell.getConvertedManaCost()) + 1); + game.getState().setValue(mageObject.getId() + "_chip", chipCounters); + if (mageObject instanceof Permanent) { + StringBuilder sb = new StringBuilder(); + int i = 0; + for (Map.Entry entry : chipCounters.entrySet()) { + i++; + sb.append(entry.getKey()); + if (i < chipCounters.size()) { + sb.append(", "); + } + } + ((Permanent) mageObject).addInfo("chip counters", CardUtil.addToolTipMarkTags("Chip counters at: " + sb), game); + new AddCountersSourceEffect(CounterType.CHIP.createInstance()).apply(game, source); + } + return true; + } + } + return false; + } + + @Override + public BingoEffect copy() { + return new BingoEffect(this); + } +} + +class BingoCount implements DynamicValue { + + public BingoCount() { + } + + public BingoCount(final BingoCount countersCount) { + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + MageObject mageObject = game.getObject(sourceAbility.getSourceId()); + if (mageObject instanceof Permanent) { + Permanent permanent = game.getPermanentOrLKIBattlefield(sourceAbility.getSourceId()); + if (permanent != null && game.getState().getValue(mageObject.getId() + "_chip") != null) { + int rows = 0; + Set nums = ((Map) game.getState().getValue(mageObject.getId() + "_chip")).keySet(); + // if (nums.size() <= permanent.getCounters(game).getCount(CounterType.CHIP)) { + + // 1 4 7 + // 8 5 3 + // 2 0 6 + + if (nums.contains(1) && nums.contains(4) && nums.contains(7)) { + rows++; + } + if (nums.contains(1) && nums.contains(8) && nums.contains(2)) { + rows++; + } + if (nums.contains(1) && nums.contains(5) && nums.contains(6)) { + rows++; + } + if (nums.contains(8) && nums.contains(5) && nums.contains(3)) { + rows++; + } + if (nums.contains(4) && nums.contains(5) && nums.contains(0)) { + rows++; + } + if (nums.contains(2) && nums.contains(0) && nums.contains(6)) { + rows++; + } + if (nums.contains(7) && nums.contains(3) && nums.contains(6)) { + rows++; + } + if (nums.contains(2) && nums.contains(5) && nums.contains(7)) { + rows++; + } + return rows * 9; + // } + } + } + return 0; + } + + @Override + public BingoCount copy() { + return new BingoCount(this); + } + + @Override + public String toString() { + return "9"; + } + + @Override + public String getMessage() { + return "set of three numbers in a row with chip counters on them"; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlastFromThePast.java b/Mage.Sets/src/mage/cards/b/BlastFromThePast.java new file mode 100644 index 0000000000..72af5d013e --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlastFromThePast.java @@ -0,0 +1,57 @@ + +package mage.cards.b; + +import java.util.UUID; +import mage.abilities.condition.common.KickedCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.BuybackAbility; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.FlashbackAbility; +import mage.abilities.keyword.KickerAbility; +import mage.abilities.keyword.MadnessAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TimingRule; +import mage.game.permanent.token.GoblinToken; +import mage.target.common.TargetAnyTarget; + +/** + * + * @author L_J + */ +public final class BlastFromThePast extends CardImpl { + + public BlastFromThePast (UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{R}"); + + // Madness {R} + this.addAbility(new MadnessAbility(this, new ManaCostsImpl("{R}"))); + // Cycling {1}{R} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{1}{R}"))); + // Kicker {2}{R} + this.addAbility(new KickerAbility("{2}{R}")); + // Flashback {3}{R} + this.addAbility(new FlashbackAbility(new ManaCostsImpl("{3}{R}"), TimingRule.INSTANT)); + // Buyback {4}{R} + this.addAbility(new BuybackAbility("{4}{R}")); + + // Blast from the Past deals 2 damage to any target. If this spell was kicked, create a 1/1 red Goblin creature token. + this.getSpellAbility().addEffect(new DamageTargetEffect(2)); + this.getSpellAbility().addTarget(new TargetAnyTarget()); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new CreateTokenEffect(new GoblinToken()), KickedCondition.instance)); + } + + public BlastFromThePast (final BlastFromThePast card) { + super(card); + } + + @Override + public BlastFromThePast copy() { + return new BlastFromThePast(this); + } + +} diff --git a/Mage.Sets/src/mage/cards/b/Bloodletter.java b/Mage.Sets/src/mage/cards/b/Bloodletter.java new file mode 100644 index 0000000000..779050d889 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/Bloodletter.java @@ -0,0 +1,110 @@ + +package mage.cards.b; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.StateTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageEverythingEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.common.FilterNonlandPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * + * @author L_J + */ +public final class Bloodletter extends CardImpl { + + public Bloodletter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}{B}"); + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // When the names of three or more nonland permanents begin with the same letter, sacrifice Bloodletter. If you do, it deals 2 damage to each creature and each player. + this.addAbility(new BloodletterStateTriggeredAbility()); + } + + public Bloodletter(final Bloodletter card) { + super(card); + } + + @Override + public Bloodletter copy() { + return new Bloodletter(this); + } +} + +class BloodletterStateTriggeredAbility extends StateTriggeredAbility { + + public BloodletterStateTriggeredAbility() { + super(Zone.BATTLEFIELD, new BloodletterEffect()); + } + + public BloodletterStateTriggeredAbility(final BloodletterStateTriggeredAbility ability) { + super(ability); + } + + @Override + public BloodletterStateTriggeredAbility copy() { + return new BloodletterStateTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Map initialCount = new HashMap<>(); + for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterNonlandPermanent(), getControllerId(), getSourceId(), game)) { + Character initial = permanent.getName().charAt(0); + initialCount.putIfAbsent(initial, 0); + initialCount.put(initial, initialCount.get(initial) + 1); + } + for (Map.Entry entry : initialCount.entrySet()) { + if (entry.getValue() >= 3) { + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "When the names of three or more nonland permanents begin with the same letter, " + super.getRule(); + } +} + +class BloodletterEffect extends OneShotEffect { + + public BloodletterEffect() { + super(Outcome.Sacrifice); + staticText = "sacrifice {this}. If you do, it deals 2 damage to each creature and each player"; + } + + public BloodletterEffect(final BloodletterEffect effect) { + super(effect); + } + + @Override + public BloodletterEffect copy() { + return new BloodletterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null && permanent.sacrifice(source.getSourceId(), game)) { + return new DamageEverythingEffect(2).apply(game, source); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BoosterTutor.java b/Mage.Sets/src/mage/cards/b/BoosterTutor.java new file mode 100644 index 0000000000..78d2d21b08 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BoosterTutor.java @@ -0,0 +1,106 @@ + +package mage.cards.b; + +import java.util.HashSet; +import java.util.Set; +import java.util.List; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseExpansionSetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.cards.ExpansionSet; +import mage.cards.Sets; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; + +/** + * + * @author spjspj & L_J + */ +public final class BoosterTutor extends CardImpl { + + public BoosterTutor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + + // Open a sealed Magic booster pack, reveal the cards, and put one of those cards into your hand. + this.getSpellAbility().addEffect(new BoosterTutorEffect()); + } + + public BoosterTutor(final BoosterTutor card) { + super(card); + } + + @Override + public BoosterTutor copy() { + return new BoosterTutor(this); + } +} + +class BoosterTutorEffect extends OneShotEffect { + + public BoosterTutorEffect() { + super(Outcome.DestroyPermanent); + this.staticText = "Open a sealed Magic booster pack, reveal the cards, and put one of those cards into your hand"; + } + + public BoosterTutorEffect(final BoosterTutorEffect effect) { + super(effect); + } + + @Override + public BoosterTutorEffect copy() { + return new BoosterTutorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + ChooseExpansionSetEffect effect = new ChooseExpansionSetEffect(Outcome.UnboostCreature); + effect.apply(game, source); + Player controller = game.getPlayer(source.getControllerId()); + + String setChosen = null; + if (effect.getValue("setchosen") != null) { + setChosen = (String) effect.getValue("setchosen"); + } else if (game.getState().getValue(this.getId() + "_set") != null) { + setChosen = (String) game.getState().getValue(this.getId() + "_set"); + } + + if (setChosen != null && controller != null) { + //ExpansionInfo set = ExpansionRepository.instance.getSetByName(setChosen); + ExpansionSet expansionSet = Sets.findSet(setChosen); + if (expansionSet != null) { + List boosterPack = expansionSet.create15CardBooster(); + if (boosterPack != null) { + StringBuilder message = new StringBuilder(controller.getLogName()).append(" opened: "); + for (Card card : boosterPack) { + message.append(card.getName()).append(" "); + } + game.informPlayers(message.toString()); + + TargetCard targetCard = new TargetCard(Zone.ALL, new FilterCard()); + Set cardsToLoad = new HashSet(boosterPack); + game.loadCards(cardsToLoad, controller.getId()); + CardsImpl cards = new CardsImpl(); + cards.addAll(boosterPack); + if (controller.choose(Outcome.Benefit, cards, targetCard, game)) { + Card card = game.getCard(targetCard.getFirstTarget()); + if (card != null) { + controller.moveCards(card, Zone.HAND, source, game); + } + } + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DoubleHeader.java b/Mage.Sets/src/mage/cards/d/DoubleHeader.java new file mode 100644 index 0000000000..917cf7fced --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DoubleHeader.java @@ -0,0 +1,94 @@ + +package mage.cards.d; + +import java.util.UUID; +import java.util.regex.Pattern; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.SplitCard; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SpellAbilityType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.target.TargetPermanent; + +/** + * + * @author L_J + */ +public final class DoubleHeader extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("permanent with a two-word name"); + + static { + filter.add(new DoubleHeaderPredicate()); + } + + public DoubleHeader(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}"); + this.subtype.add(SubType.JELLYFISH); + + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Double Header enters the battlefield, you may return target permanent with a two-word name to its owner’s hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), true); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + public DoubleHeader(final DoubleHeader card) { + super(card); + } + + @Override + public DoubleHeader copy() { + return new DoubleHeader(this); + } +} + +class DoubleHeaderPredicate implements Predicate { + + public DoubleHeaderPredicate() { + } + + @Override + public boolean apply(MageObject input, Game game) { + String name = input.getName(); + if (input instanceof SplitCard) { + return hasTwoWords(((SplitCard)input).getLeftHalfCard().getName()) || hasTwoWords(((SplitCard)input).getRightHalfCard().getName()); + } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED){ + SplitCard card = (SplitCard) ((Spell)input).getCard(); + return hasTwoWords(card.getLeftHalfCard().getName()) || hasTwoWords(card.getRightHalfCard().getName()); + } else { + if (name.contains(" // ")) { + String leftName = name.substring(0, name.indexOf(" // ")); + String rightName = name.substring(name.indexOf(" // ") + 4, name.length()); + return hasTwoWords(leftName) || hasTwoWords(rightName); + } else { + return hasTwoWords(name); + } + } + } + + private boolean hasTwoWords(String str) { + return Pattern.compile("\\s+").split(str).length == 2; + } + + @Override + public String toString() { + return ""; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FirstComeFirstServed.java b/Mage.Sets/src/mage/cards/f/FirstComeFirstServed.java new file mode 100644 index 0000000000..3f3fa1c97c --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FirstComeFirstServed.java @@ -0,0 +1,74 @@ + +package mage.cards.f; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.UUID; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.filter.common.FilterAttackingOrBlockingCreature; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentCard; + +/** + * + * @author L_J + */ +public final class FirstComeFirstServed extends CardImpl { + + private static final FilterAttackingOrBlockingCreature filter = new FilterAttackingOrBlockingCreature("Each attacking or blocking creature with the lowest collector number among attacking or blocking creatures"); + + static { + filter.add(new FirstComeFirstServedPredicate()); + } + + public FirstComeFirstServed(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + + // Each attacking or blocking creature with the lowest collector number among attacking or blocking creatures has first strike. + GainAbilityAllEffect gainEffect = new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter, false); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, gainEffect)); + } + + public FirstComeFirstServed(final FirstComeFirstServed card) { + super(card); + } + + @Override + public FirstComeFirstServed copy() { + return new FirstComeFirstServed(this); + } +} + +class FirstComeFirstServedPredicate implements Predicate { + + @Override + public boolean apply(Permanent input, Game game) { + if (input instanceof PermanentCard) { + int lowestNumber = Integer.MAX_VALUE; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterAttackingOrBlockingCreature(), game)) { + int number = parseCardNumber(permanent); + if (lowestNumber > number) { + lowestNumber = number; + } + } + return parseCardNumber(input) == lowestNumber; + } + return false; + } + + public int parseCardNumber(Permanent input) { + String str = input.getCardNumber(); + Matcher matcher = Pattern.compile("\\d+").matcher(str); + matcher.find(); + return Integer.valueOf(matcher.group()); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FormOfTheSquirrel.java b/Mage.Sets/src/mage/cards/f/FormOfTheSquirrel.java new file mode 100644 index 0000000000..f41bb741d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FormOfTheSquirrel.java @@ -0,0 +1,122 @@ + +package mage.cards.f; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.LoseGameTargetPlayerEffect; +import mage.abilities.effects.common.combat.CantAttackYouAllEffect; +import mage.abilities.effects.common.continuous.GainAbilityControllerEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.ShroudAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.SquirrelToken; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author L_J + */ +public final class FormOfTheSquirrel extends CardImpl { + + public FormOfTheSquirrel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{G}"); + + // As Form of the Squirrel enters the battlefield, create a 1/1 green Squirrel creature token. You lose the game when that creature leaves the battlefield. + this.addAbility(new AsEntersBattlefieldAbility(new FormOfTheSquirrelCreateTokenEffect())); + + // Creatures can't attack you. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackYouAllEffect(Duration.WhileOnBattlefield))); + + // You have shroud. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControllerEffect(ShroudAbility.getInstance()))); + + // You can't cast spells. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new FormOfTheSquirrelCantCastEffect())); + } + + public FormOfTheSquirrel(final FormOfTheSquirrel card) { + super(card); + } + + @Override + public FormOfTheSquirrel copy() { + return new FormOfTheSquirrel(this); + } +} + +class FormOfTheSquirrelCreateTokenEffect extends OneShotEffect { + + public FormOfTheSquirrelCreateTokenEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "create a 1/1 green Squirrel creature token. You lose the game when that creature leaves the battlefield"; + } + + public FormOfTheSquirrelCreateTokenEffect(final FormOfTheSquirrelCreateTokenEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player sourceController = game.getPlayer(source.getControllerId()); + if (sourceController != null) { + CreateTokenEffect effect = new CreateTokenEffect(new SquirrelToken()); + effect.apply(game, source); + game.getState().setValue(source.getSourceId() + "_token", effect.getLastAddedTokenIds()); + for (UUID addedTokenId : effect.getLastAddedTokenIds()) { + Effect loseGameEffect = new LoseGameTargetPlayerEffect(); + loseGameEffect.setTargetPointer(new FixedTarget(sourceController.getId(), game)); + LeavesBattlefieldTriggeredAbility triggerAbility = new LeavesBattlefieldTriggeredAbility(loseGameEffect, false); + ContinuousEffect continuousEffect = new GainAbilityTargetEffect(triggerAbility, Duration.WhileOnBattlefield); + continuousEffect.setTargetPointer(new FixedTarget(addedTokenId, game)); + game.addEffect(continuousEffect, source); + } + return true; + } + return false; + } + + @Override + public FormOfTheSquirrelCreateTokenEffect copy() { + return new FormOfTheSquirrelCreateTokenEffect(this); + } +} + +class FormOfTheSquirrelCantCastEffect extends ContinuousRuleModifyingEffectImpl { + + public FormOfTheSquirrelCantCastEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment); + staticText = "You can't cast spells"; + } + + public FormOfTheSquirrelCantCastEffect(final FormOfTheSquirrelCantCastEffect effect) { + super(effect); + } + + @Override + public FormOfTheSquirrelCantCastEffect copy() { + return new FormOfTheSquirrelCantCastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getType() == GameEvent.EventType.CAST_SPELL && event.getPlayerId().equals(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoblinSecretAgent.java b/Mage.Sets/src/mage/cards/g/GoblinSecretAgent.java new file mode 100644 index 0000000000..3599c2e2c7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoblinSecretAgent.java @@ -0,0 +1,83 @@ + +package mage.cards.g; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.game.Game; +import mage.players.Player; +/** + * + * @author L_J + */ +public final class GoblinSecretAgent extends CardImpl { + + public GoblinSecretAgent(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}"); + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // At the beginning of your upkeep, reveal a card from your hand at random. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new GoblinSecretAgentEffect(), TargetController.YOU, false)); + } + + public GoblinSecretAgent(final GoblinSecretAgent card) { + super(card); + } + + @Override + public GoblinSecretAgent copy() { + return new GoblinSecretAgent(this); + } +} + +class GoblinSecretAgentEffect extends OneShotEffect { + + public GoblinSecretAgentEffect() { + super(Outcome.Detriment); + this.staticText = "reveal a card from your hand at random"; + } + + public GoblinSecretAgentEffect(final GoblinSecretAgentEffect effect) { + super(effect); + } + + @Override + public GoblinSecretAgentEffect copy() { + return new GoblinSecretAgentEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null) { + if (!controller.getHand().isEmpty()) { + CardsImpl randomCard = new CardsImpl(); + Card card = controller.getHand().getRandom(game); + randomCard.add(card); + controller.revealCards(sourceObject.getIdName(), randomCard, game); + } + return true; + } + return false; + } + +} diff --git a/Mage.Sets/src/mage/cards/m/ManaScrew.java b/Mage.Sets/src/mage/cards/m/ManaScrew.java new file mode 100644 index 0000000000..f2228b428b --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/ManaScrew.java @@ -0,0 +1,96 @@ + +package mage.cards.m; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.mana.BasicManaEffect; +import mage.abilities.mana.ActivatedManaAbilityImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author L_J + */ +public final class ManaScrew extends CardImpl { + + public ManaScrew(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + // {1}: Flip a coin. If you win the flip, add {C}{C}. Activate this ability only any time you could cast an instant. + this.addAbility(new ManaScrewAbility()); + } + + public ManaScrew(final ManaScrew card) { + super(card); + } + + @Override + public ManaScrew copy() { + return new ManaScrew(this); + } +} + +class ManaScrewAbility extends ActivatedManaAbilityImpl { + + public ManaScrewAbility() { + super(Zone.BATTLEFIELD, new ManaScrewEffect(), new GenericManaCost(1)); + this.netMana.add(new Mana(0, 0, 0, 0, 0, 2, 0, 0)); + } + + public ManaScrewAbility(final ManaScrewAbility ability) { + super(ability); + } + + @Override + public ActivationStatus canActivate(UUID playerId, Game game) { + Player player = game.getPlayer(playerId); + if (player != null && !player.isInPayManaMode()) { + return super.canActivate(playerId, game); + } + return ActivationStatus.getFalse(); + } + + @Override + public ManaScrewAbility copy() { + return new ManaScrewAbility(this); + } + + @Override + public String getRule() { + return super.getRule() + " Activate this ability only any time you could cast an instant."; + } +} + +class ManaScrewEffect extends BasicManaEffect { + + public ManaScrewEffect() { + super(Mana.ColorlessMana(2)); + this.staticText = "Flip a coin. If you win the flip, add {C}{C}"; + } + + public ManaScrewEffect(final ManaScrewEffect effect) { + super(effect); + this.manaTemplate = effect.manaTemplate.copy(); + } + + @Override + public ManaScrewEffect copy() { + return new ManaScrewEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null && player.flipCoin(game)) { + player.getManaPool().addMana(getMana(game, source), game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/Mise.java b/Mage.Sets/src/mage/cards/m/Mise.java new file mode 100644 index 0000000000..23cdc07e72 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/Mise.java @@ -0,0 +1,75 @@ + +package mage.cards.m; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseACardNameEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author L_J + */ +public final class Mise extends CardImpl { + + public Mise(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); + + // Choose a nonland card name, then reveal the top card of your library. If that card has the chosen name, you draw three cards. + this.getSpellAbility().addEffect(new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME)); + this.getSpellAbility().addEffect(new MiseEffect()); + } + + public Mise(final Mise card) { + super(card); + } + + @Override + public Mise copy() { + return new Mise(this); + } +} + +class MiseEffect extends OneShotEffect { + + public MiseEffect() { + super(Outcome.Detriment); + staticText = "then reveal the top card of your library. If that card has the chosen name, you draw three cards"; + } + + public MiseEffect(final MiseEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Object object = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + if (player != null && object instanceof String) { + Card card = player.getLibrary().getFromTop(game); + String namedCard = (String) object; + CardsImpl cards = new CardsImpl(card); + if (card != null) { + player.revealCards("Mise", cards, game, true); + if (card.getName().equals(namedCard)) { + player.drawCards(3, game); + } + } + return true; + } + return false; + } + + @Override + public MiseEffect copy() { + return new MiseEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MonkeyMonkeyMonkey.java b/Mage.Sets/src/mage/cards/m/MonkeyMonkeyMonkey.java new file mode 100644 index 0000000000..4d3f500902 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MonkeyMonkeyMonkey.java @@ -0,0 +1,150 @@ + +package mage.cards.m; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.common.FilterNonlandPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author L_J + */ +public final class MonkeyMonkeyMonkey extends CardImpl { + + public MonkeyMonkeyMonkey(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}"); + this.subtype.add(SubType.MONKEY); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // As Monkey Monkey Monkey enters the battlefield, choose a letter. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseLetterEffect())); + + // Monkey Monkey Monkey gets +1/+1 for each nonland permanent whose name begins with the chosen letter. + MonkeyMonkeyMonkeyCount count = new MonkeyMonkeyMonkeyCount(); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(count, count, Duration.WhileOnBattlefield))); + + } + + public MonkeyMonkeyMonkey(final MonkeyMonkeyMonkey card) { + super(card); + } + + @Override + public MonkeyMonkeyMonkey copy() { + return new MonkeyMonkeyMonkey(this); + } +} + +class ChooseLetterEffect extends OneShotEffect { + + public ChooseLetterEffect() { + super(Outcome.Benefit); + staticText = "choose a letter"; + } + + public ChooseLetterEffect(final ChooseLetterEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); + if (mageObject == null) { + mageObject = game.getObject(source.getSourceId()); + } + + ChoiceImpl choice = new ChoiceImpl(true); + choice.setMessage("Choose letter"); + Set choices = new HashSet<>(); + for (Character letter = 'A'; letter <= 'Z'; letter++) { + choices.add(letter.toString()); + } + choice.setChoices(choices); + + if (controller != null && mageObject != null && controller.choose(outcome, choice, game)) { + if (!game.isSimulation()) { + game.informPlayers(mageObject.getLogName() + ": " + controller.getLogName() + " has chosen " + choice.getChoice()); + } + game.getState().setValue(mageObject.getId() + "_letter", choice.getChoice()); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo("chosen letter", CardUtil.addToolTipMarkTags("Chosen letter: " + choice.getChoice()), game); + } + return true; + } + return false; + } + + @Override + public ChooseLetterEffect copy() { + return new ChooseLetterEffect(this); + } +} + +class MonkeyMonkeyMonkeyCount implements DynamicValue { + + public MonkeyMonkeyMonkeyCount() { + } + + public MonkeyMonkeyMonkeyCount(final MonkeyMonkeyMonkeyCount countersCount) { + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + + + MageObject mageObject = game.getObject(sourceAbility.getSourceId()); + if (mageObject instanceof Permanent) { + Permanent permanent = game.getPermanentOrLKIBattlefield(sourceAbility.getSourceId()); + if (permanent != null && game.getState().getValue(mageObject.getId() + "_letter") != null) { + int letters = 0; + for (Permanent p : game.getBattlefield().getActivePermanents(new FilterNonlandPermanent(), sourceAbility.getControllerId(), sourceAbility.getSourceId(), game)) { + Character initial = Character.toUpperCase(p.getName().charAt(0)); + if (initial.toString().equals(game.getState().getValue(mageObject.getId() + "_letter"))) { + letters++; + } + } + return letters; + } + } + return 0; + } + + @Override + public MonkeyMonkeyMonkeyCount copy() { + return new MonkeyMonkeyMonkeyCount(this); + } + + @Override + public String toString() { + return "1"; + } + + @Override + public String getMessage() { + return "nonland permanent whose name begins with the chosen letter"; + } +} diff --git a/Mage.Sets/src/mage/cards/n/NowIKnowMyABCs.java b/Mage.Sets/src/mage/cards/n/NowIKnowMyABCs.java new file mode 100644 index 0000000000..ee3f6b47f5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NowIKnowMyABCs.java @@ -0,0 +1,69 @@ + +package mage.cards.n; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.effects.common.WinGameSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author L_J + */ +public final class NowIKnowMyABCs extends CardImpl { + + public NowIKnowMyABCs(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}{U}"); + + // At the beginning of your upkeep, if you control permanents with names that include all twenty-six letters of the English alphabet, you win the game. + this.addAbility(new ConditionalTriggeredAbility( + new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect(), TargetController.YOU, false), + new NowIKnowMyABCsCondition(), + "At the beginning of your upkeep, if you control permanents with names that include all twenty-six letters of the English alphabet, you win the game.")); + } + + public NowIKnowMyABCs(final NowIKnowMyABCs card) { + super(card); + } + + @Override + public NowIKnowMyABCs copy() { + return new NowIKnowMyABCs(this); + } +} + +class NowIKnowMyABCsCondition implements Condition { + + public NowIKnowMyABCsCondition() { + } + + @Override + public boolean apply(Game game, Ability source) { + Set letters = new HashSet<>(); + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { + String permName = permanent.getName(); + for (int i = 0; i < permName.length(); i++) { + Character letter = permName.charAt(i); + if (Character.isLetter(letter)) { + letters.add(Character.toUpperCase(letter)); + } + } + } + return letters.size() >= 26; + } + + @Override + public String toString() { + return "if you control permanents with names that include all twenty-six letters of the English alphabet"; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RareBGone.java b/Mage.Sets/src/mage/cards/r/RareBGone.java new file mode 100644 index 0000000000..77d2521c8d --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RareBGone.java @@ -0,0 +1,119 @@ + +package mage.cards.r; + +import java.util.*; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author L_J + */ +public final class RareBGone extends CardImpl { + + public RareBGone(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{R}"); + + // Each player sacrifices all permanents that are rare or mythic rare, then each player reveals their hand and discards all cards that are rare or mythic rare. + this.getSpellAbility().addEffect(new RareBGoneEffect()); + + } + + public RareBGone(final RareBGone card) { + super(card); + } + + @Override + public RareBGone copy() { + return new RareBGone(this); + } +} + +class RareBGoneEffect extends OneShotEffect { + + private static final FilterPermanent filterPermanent = new FilterPermanent(); + private static final FilterCard filterCard = new FilterCard(); + + static { + filterPermanent.add(Predicates.or( + new RarityPredicate(Rarity.RARE), + new RarityPredicate(Rarity.MYTHIC) + )); + filterCard.add(Predicates.or( + new RarityPredicate(Rarity.RARE), + new RarityPredicate(Rarity.MYTHIC) + )); + } + + public RareBGoneEffect() { + super(Outcome.Benefit); + this.staticText = "Each player sacrifices all permanents that are rare or mythic rare, then each player reveals their hand and discards all cards that are rare or mythic rare"; + } + + public RareBGoneEffect(final RareBGoneEffect effect) { + super(effect); + } + + @Override + public RareBGoneEffect copy() { + return new RareBGoneEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterPermanent, playerId, game)) { + permanent.sacrifice(source.getSourceId(), game); + } + Cards hand = player.getHand(); + player.revealCards("Rare-B-Gone", hand, game); + Set cards = hand.getCards(game); + for (Card card : cards) { + if (card != null && filterCard.match(card, game)) { + player.discard(card, source, game); + } + } + } + } + return true; + } + return false; + } +} + +class RarityPredicate implements Predicate { + + private final Rarity rarity; + + public RarityPredicate(Rarity rarity) { + this.rarity = rarity; + } + + @Override + public boolean apply(Card input, Game game) { + return input.getRarity().equals(rarity); + } + + @Override + public String toString() { + return "Rarity(" + rarity + ')'; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SixyBeast.java b/Mage.Sets/src/mage/cards/s/SixyBeast.java new file mode 100644 index 0000000000..662f032f63 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SixyBeast.java @@ -0,0 +1,95 @@ + +package mage.cards.s; + +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetOpponent; + +/** + * + * @author L_J + */ +public final class SixyBeast extends CardImpl { + + public SixyBeast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // As Six-y Beast enters the battlefield, you secretly put six or fewer +1/+1 counters on it, then an opponent guesses the number of counters. If that player guesses right, sacrifice Six-y Beast after it enters the battlefield. + this.addAbility(new AsEntersBattlefieldAbility(new SixyBeastEffect())); + + } + + public SixyBeast(final SixyBeast card) { + super(card); + } + + @Override + public SixyBeast copy() { + return new SixyBeast(this); + } +} + +class SixyBeastEffect extends OneShotEffect { + + public SixyBeastEffect() { + super(Outcome.BoostCreature); + this.staticText = "you secretly put six or fewer +1/+1 counters on it, then an opponent guesses the number of counters. If that player guesses right, sacrifice {this} after it enters the battlefield"; + } + + public SixyBeastEffect(final SixyBeastEffect effect) { + super(effect); + } + + @Override + public SixyBeastEffect copy() { + return new SixyBeastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + Player controller = game.getPlayer(source.getControllerId()); + if (permanent != null && controller != null) { + int counterAmount = controller.getAmount(0, 6, "Secretly put up to six counters on " + permanent.getName(), game); + permanent.addCounters(CounterType.P1P1.createInstance(counterAmount), source, game); + Player opponent = null; + Set opponents = game.getOpponents(source.getControllerId()); + if (!opponents.isEmpty()) { + if (opponents.size() > 1) { + Target targetOpponent = new TargetOpponent(true); + if (controller.chooseTarget(Outcome.Neutral, targetOpponent, source, game)) { + opponent = game.getPlayer(targetOpponent.getFirstTarget()); + } + } else { + opponent = game.getPlayer(opponents.iterator().next()); + } + } + if (opponent != null) { + int guessedAmount = opponent.getAmount(0, 6, "Guess the number of counters on " + permanent.getName(), game); + game.informPlayers(opponent.getLogName() + " guessed " + guessedAmount + " as the number of counters on " + permanent.getLogName()); + if (counterAmount == guessedAmount) { + permanent.sacrifice(source.getSourceId(), game); + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SymbolStatus.java b/Mage.Sets/src/mage/cards/s/SymbolStatus.java new file mode 100644 index 0000000000..ea40bec2e4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SymbolStatus.java @@ -0,0 +1,65 @@ + +package mage.cards.s; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.ExpansionSymbolToken; + +/** + * + * @author L_J + */ +public class SymbolStatus extends CardImpl { + + public SymbolStatus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}{G}"); + + // Create a 1/1 colorless Expansion-Symbol creature token for each different expansion symbol among permanents you control. + this.getSpellAbility().addEffect(new SymbolStatusEffect()); + } + + public SymbolStatus(final SymbolStatus card) { + super(card); + } + + @Override + public SymbolStatus copy() { + return new SymbolStatus(this); + } +} + +class SymbolStatusEffect extends OneShotEffect { + + public SymbolStatusEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "Create a 1/1 colorless Expansion-Symbol creature token for each different expansion symbol among permanents you control"; + } + + public SymbolStatusEffect(final SymbolStatusEffect effect) { + super(effect); + } + + @Override + public SymbolStatusEffect copy() { + return new SymbolStatusEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Set symbols = new HashSet<>(); + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { + symbols.add(permanent.getExpansionSetCode()); + } + return new CreateTokenEffect(new ExpansionSymbolToken(), symbols.size()).apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheFallenApart.java b/Mage.Sets/src/mage/cards/t/TheFallenApart.java new file mode 100644 index 0000000000..66f887455a --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheFallenApart.java @@ -0,0 +1,186 @@ + +package mage.cards.t; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.DealtDamageToSourceTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author L_J + */ +public final class TheFallenApart extends CardImpl { + + public TheFallenApart(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + this.subtype.add(SubType.ZOMBIE); + + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // The Fallen Apart enters the battlefield with two arms and two legs. + this.addAbility(new EntersBattlefieldAbility(new TheFallenApartEntersEffect())); + + // Whenever damage is dealt to The Fallen Apart, remove an arm or a leg from it. + this.addAbility(new DealtDamageToSourceTriggeredAbility(Zone.BATTLEFIELD, new TheFallenApartToggleEffect(), false)); + + // The Fallen Apart can’t attack if it has no legs and can’t block if it has no arms. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new TheFallenApartRestrictionEffect())); + } + + public TheFallenApart(final TheFallenApart card) { + super(card); + } + + @Override + public TheFallenApart copy() { + return new TheFallenApart(this); + } +} + +class TheFallenApartEntersEffect extends OneShotEffect { + + public TheFallenApartEntersEffect() { + super(Outcome.Neutral); + staticText = "with two arms and two legs"; + } + + public TheFallenApartEntersEffect(final TheFallenApartEntersEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); + if (mageObject == null) { + mageObject = game.getObject(source.getSourceId()); + } + if (mageObject != null) { + game.getState().setValue(mageObject.getId() + "_arms", 2); + game.getState().setValue(mageObject.getId() + "_legs", 2); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo("armslegs", CardUtil.addToolTipMarkTags("Arms: 2, Legs: 2"), game); + } + return true; + } + return false; + } + + @Override + public TheFallenApartEntersEffect copy() { + return new TheFallenApartEntersEffect(this); + } +} + +class TheFallenApartToggleEffect extends OneShotEffect { + + public TheFallenApartToggleEffect() { + super(Outcome.Neutral); + staticText = "remove an arm or a leg from it"; + } + + public TheFallenApartToggleEffect(final TheFallenApartToggleEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getObject(source.getSourceId()); + if (controller != null && mageObject != null) { + if (game.getState().getValue(mageObject.getId() + "_arms") == null + || game.getState().getValue(mageObject.getId() + "_legs") == null) { + return false; + } + int arms = (Integer) game.getState().getValue(mageObject.getId() + "_arms"); + int legs = (Integer) game.getState().getValue(mageObject.getId() + "_legs"); + if (arms > 0) { + if (legs > 0) { + if (controller.chooseUse(Outcome.Detriment, "Remove an arm or a leg:", + source.getSourceObject(game).getLogName(), "Arm", "Leg", source, game)) { + arms -= 1; + game.informPlayers(mageObject.getLogName() + " loses an arm"); + } else { + legs -= 1; + game.informPlayers(mageObject.getLogName() + " loses a leg"); + } + } else { + arms -= 1; + game.informPlayers(mageObject.getLogName() + " loses an arm"); + } + } else { + if (legs > 0) { + legs -= 1; + game.informPlayers(mageObject.getLogName() + " loses a leg"); + } + } + game.getState().setValue(mageObject.getId() + "_arms", arms); + game.getState().setValue(mageObject.getId() + "_legs", legs); + ((Permanent) mageObject).addInfo("armslegs", CardUtil.addToolTipMarkTags("Arms: " + arms + ", Legs: " + legs), game); + return true; + } + return false; + } + + @Override + public TheFallenApartToggleEffect copy() { + return new TheFallenApartToggleEffect(this); + } +} + +class TheFallenApartRestrictionEffect extends RestrictionEffect { + + public TheFallenApartRestrictionEffect() { + super(Duration.WhileOnBattlefield); + staticText = "{this} can’t attack if it has no legs and can’t block if it has no arms"; + } + + public TheFallenApartRestrictionEffect(final TheFallenApartRestrictionEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent.getId().equals(source.getSourceId()); + } + + @Override + public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) { + MageObject mageObject = game.getObject(source.getSourceId()); + if (mageObject != null) { + return (Integer) game.getState().getValue(mageObject.getId() + "_arms") > 0; + } + return false; + } + + @Override + public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game) { + MageObject mageObject = game.getObject(source.getSourceId()); + if (mageObject != null) { + return (Integer) game.getState().getValue(mageObject.getId() + "_legs") > 0; + } + return false; + } + + @Override + public TheFallenApartRestrictionEffect copy() { + return new TheFallenApartRestrictionEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/Togglodyte.java b/Mage.Sets/src/mage/cards/t/Togglodyte.java new file mode 100644 index 0000000000..ff3df93bd9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/Togglodyte.java @@ -0,0 +1,199 @@ + +package mage.cards.t; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalReplacementEffect; +import mage.abilities.decorator.ConditionalRestrictionEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.RestrictionEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.util.CardUtil; + +/** + * + * @author L_J + */ +public final class Togglodyte extends CardImpl { + + public Togglodyte(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + this.subtype.add(SubType.GOLEM); + + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Togglodyte enters the battlefield turned on. + this.addAbility(new EntersBattlefieldAbility(new TogglodyteEntersEffect())); + + // Whenever a player casts a spell, toggle Togglodyte’s ON/OFF switch. + this.addAbility(new SpellCastAllTriggeredAbility(new TogglodyteToggleEffect(), false)); + + // As long as Togglodyte is turned off, it can’t attack or block, and prevent all damage it would deal. + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalRestrictionEffect(new TogglodyteRestrictionEffect(), new TogglodyteCondition())); + ability.addEffect(new ConditionalReplacementEffect(new TogglodytePreventionEffect(), new TogglodyteCondition())); + this.addAbility(ability); + } + + public Togglodyte(final Togglodyte card) { + super(card); + } + + @Override + public Togglodyte copy() { + return new Togglodyte(this); + } +} + +class TogglodyteEntersEffect extends OneShotEffect { + + public TogglodyteEntersEffect() { + super(Outcome.Neutral); + staticText = "turned on"; + } + + public TogglodyteEntersEffect(final TogglodyteEntersEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); + if (mageObject == null) { + mageObject = game.getObject(source.getSourceId()); + } + if (mageObject != null) { + boolean toggled = true; + game.getState().setValue(mageObject.getId() + "_toggle", toggled); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo("toggle", CardUtil.addToolTipMarkTags("Switch: " + (toggled ? "ON" : "OFF")), game); + } + return true; + } + return false; + } + + @Override + public TogglodyteEntersEffect copy() { + return new TogglodyteEntersEffect(this); + } +} + +class TogglodyteToggleEffect extends OneShotEffect { + + public TogglodyteToggleEffect() { + super(Outcome.Neutral); + staticText = "toggle {this}’s ON/OFF switch"; + } + + public TogglodyteToggleEffect(final TogglodyteToggleEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject mageObject = game.getObject(source.getSourceId()); + if (mageObject != null) { + if (game.getState().getValue(mageObject.getId() + "_toggle") == null) { + return false; + } + boolean toggled = (Boolean) game.getState().getValue(mageObject.getId() + "_toggle"); + game.getState().setValue(mageObject.getId() + "_toggle", !toggled); + ((Permanent) mageObject).addInfo("toggle", CardUtil.addToolTipMarkTags("Switch: " + (!toggled ? "ON" : "OFF")), game); + return true; + } + return false; + } + + @Override + public TogglodyteToggleEffect copy() { + return new TogglodyteToggleEffect(this); + } +} + +class TogglodyteRestrictionEffect extends RestrictionEffect { + + public TogglodyteRestrictionEffect() { + super(Duration.WhileOnBattlefield); + staticText = ""; + } + + public TogglodyteRestrictionEffect(final TogglodyteRestrictionEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent.getId().equals(source.getSourceId()); + } + + @Override + public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) { + return false; + } + + @Override + public boolean canAttack(Game game) { + return false; + } + + @Override + public TogglodyteRestrictionEffect copy() { + return new TogglodyteRestrictionEffect(this); + } +} + +class TogglodytePreventionEffect extends PreventionEffectImpl { + + public TogglodytePreventionEffect() { + super(Duration.WhileOnBattlefield, Integer.MAX_VALUE, false); + staticText = "As long as {this} is turned off, it can’t attack or block, and prevent all damage it would deal"; + } + + public TogglodytePreventionEffect(final TogglodytePreventionEffect effect) { + super(effect); + } + + @Override + public TogglodytePreventionEffect copy() { + return new TogglodytePreventionEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game)) { + if (event.getSourceId().equals(source.getSourceId())) { + return true; + } + } + return false; + } +} + +class TogglodyteCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + MageObject mageObject = game.getObject(source.getSourceId()); + if (mageObject != null && game.getState().getValue(mageObject.getId() + "_toggle") != null) { + return !((Boolean) game.getState().getValue(mageObject.getId() + "_toggle")); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/u/UktabiKong.java b/Mage.Sets/src/mage/cards/u/UktabiKong.java new file mode 100644 index 0000000000..d3e92b6b97 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UktabiKong.java @@ -0,0 +1,62 @@ + +package mage.cards.u; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.permanent.token.UktabiKongApeToken; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author LoneFox + */ +public final class UktabiKong extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("untapped Apes you control"); + + static { + filter.add(Predicates.not(new TappedPredicate())); + filter.add(new SubtypePredicate(SubType.APE)); + } + + public UktabiKong(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{G}{G}{G}"); + this.subtype.add(SubType.APE); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When Uktabi Kong enters the battlefield, destroy all artifacts. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(new FilterArtifactPermanent("artifacts")), false)); + + // Tap two untapped Apes you control: Create a 1/1 green Ape creature token. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new UktabiKongApeToken()), new TapTargetCost(new TargetControlledPermanent(2, 2, filter, true)))); + } + + public UktabiKong(final UktabiKong card) { + super(card); + } + + @Override + public UktabiKong copy() { + return new UktabiKong(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UrzasHotTub.java b/Mage.Sets/src/mage/cards/u/UrzasHotTub.java new file mode 100644 index 0000000000..6a816e8d97 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UrzasHotTub.java @@ -0,0 +1,130 @@ + +package mage.cards.u; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.SplitCard; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SpellAbilityType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author L_J + */ +public class UrzasHotTub extends CardImpl { + + public UrzasHotTub(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + + // {2}, Discard a card: Search your library for a card that shares a complete word in its name with the discarded card, reveal it, put it into your hand, then shuffle your library. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new UrzasHotTubEffect(), new ManaCostsImpl("{2}")); + ability.addCost(new DiscardTargetCost(new TargetCardInHand())); + this.addAbility(ability); + } + + public UrzasHotTub(final UrzasHotTub card) { + super(card); + } + + @Override + public UrzasHotTub copy() { + return new UrzasHotTub(this); + } +} + +class UrzasHotTubEffect extends OneShotEffect { + + public UrzasHotTubEffect() { + super(Outcome.ReturnToHand); + this.staticText = "Search your library for a card that shares a complete word in its name with the discarded card, reveal it, put it into your hand, then shuffle your library"; + } + + public UrzasHotTubEffect(final UrzasHotTubEffect effect) { + super(effect); + } + + @Override + public UrzasHotTubEffect copy() { + return new UrzasHotTubEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Cost cost : source.getCosts()) { + if (cost instanceof DiscardTargetCost) { + DiscardTargetCost discardCost = (DiscardTargetCost) cost; + Card discardedCard = discardCost.getCards().get(0); + if (discardedCard != null) { + FilterCard filter = new FilterCard(); + filter.add(new UrzasHotTubPredicate(discardedCard.getName())); + return new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true).apply(game, source); + } + } + } + return false; + } +} + +class UrzasHotTubPredicate implements Predicate { + + private final String referenceName; + + public UrzasHotTubPredicate(String referenceName) { + this.referenceName = referenceName; + } + + @Override + public boolean apply(MageObject input, Game game) { + String name = input.getName(); + if (input instanceof SplitCard) { + return sharesWordWithName(((SplitCard)input).getLeftHalfCard().getName()) || sharesWordWithName(((SplitCard)input).getRightHalfCard().getName()); + } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED){ + SplitCard card = (SplitCard) ((Spell)input).getCard(); + return sharesWordWithName(card.getLeftHalfCard().getName()) || sharesWordWithName(card.getRightHalfCard().getName()); + } else { + if (name.contains(" // ")) { + String leftName = name.substring(0, name.indexOf(" // ")); + String rightName = name.substring(name.indexOf(" // ") + 4, name.length()); + return sharesWordWithName(leftName) || sharesWordWithName(rightName); + } else { + return sharesWordWithName(name); + } + } + } + + private boolean sharesWordWithName(String str) { + if (referenceName == null || referenceName == "") { + return false; + } + String[] arr = referenceName.split("\\s+"); + for (int i = 0; i < arr.length; i++) { + if (str.contains(arr[i].replaceAll(",", ""))) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return ""; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WhenFluffyBunniesAttack.java b/Mage.Sets/src/mage/cards/w/WhenFluffyBunniesAttack.java new file mode 100644 index 0000000000..a2db93e53c --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WhenFluffyBunniesAttack.java @@ -0,0 +1,101 @@ + +package mage.cards.w; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author L_J + */ +public final class WhenFluffyBunniesAttack extends CardImpl { + + public WhenFluffyBunniesAttack (UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{B}"); + + // Target creature gets -X/-X until end of turn, where X is the number of times the letter of your choice appears in that creature’s name. + this.getSpellAbility().addEffect(new WhenFluffyBunniesAttackEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + public WhenFluffyBunniesAttack (final WhenFluffyBunniesAttack card) { + super(card); + } + + @Override + public WhenFluffyBunniesAttack copy() { + return new WhenFluffyBunniesAttack(this); + } + +} + +class WhenFluffyBunniesAttackEffect extends OneShotEffect { + + public WhenFluffyBunniesAttackEffect() { + super(Outcome.Detriment); + staticText = "Target creature gets -X/-X until end of turn, where X is the number of times the letter of your choice appears in that creature’s name"; + } + + public WhenFluffyBunniesAttackEffect(final WhenFluffyBunniesAttackEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + + ChoiceImpl choice = new ChoiceImpl(true); + choice.setMessage("Choose letter"); + Set choices = new HashSet<>(); + for (Character letter = 'A'; letter <= 'Z'; letter++) { + choices.add(letter.toString()); + } + choice.setChoices(choices); + + if (controller != null && permanent != null && controller.choose(outcome, choice, game)) { + if (!game.isSimulation()) { + MageObject mageObject = game.getObject(source.getSourceId()); + if (mageObject != null) { + game.informPlayers(mageObject.getLogName() + ": " + controller.getLogName() + " has chosen " + choice.getChoice()); + } + } + + Character chosenLetter = choice.getChoice().charAt(0); + int unboostValue = 0; + String permName = permanent.getName(); + for (int i = 0; i < permName.length(); i++) { + Character letter = permName.charAt(i); + if (Character.isLetter(letter) && Character.toUpperCase(letter) == chosenLetter) { + unboostValue--; + } + } + BoostTargetEffect effect = new BoostTargetEffect(unboostValue, unboostValue, Duration.EndOfTurn); + effect.setTargetPointer(new FixedTarget(permanent.getId())); + game.addEffect(effect, source); + return true; + } + return false; + } + + @Override + public WhenFluffyBunniesAttackEffect copy() { + return new WhenFluffyBunniesAttackEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/Wordmail.java b/Mage.Sets/src/mage/cards/w/Wordmail.java new file mode 100644 index 0000000000..e6aa8654e1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/Wordmail.java @@ -0,0 +1,90 @@ + +package mage.cards.w; + +import java.util.UUID; +import java.util.regex.Pattern; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author L_J + */ +public final class Wordmail extends CardImpl { + + public Wordmail(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}"); + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget.getTargetName())); + + // Enchanted creature gets +1/+1 for each word in its name. + WordmailCount count = new WordmailCount(); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(count, count, Duration.WhileOnBattlefield))); + } + + public Wordmail(final Wordmail card) { + super(card); + } + + @Override + public Wordmail copy() { + return new Wordmail(this); + } +} + +class WordmailCount implements DynamicValue { + + public WordmailCount() { + } + + public WordmailCount(final WordmailCount dynamicValue) { + } + + @Override + public int calculate(Game game, Ability source, Effect effect) { + Permanent aura = game.getPermanent(source.getSourceId()); + if (aura != null) { + Permanent permanent = game.getPermanent(aura.getAttachedTo()); + if (permanent != null) { + return Pattern.compile("\\s+").split(permanent.getName()).length; + } + } + return 0; + } + + @Override + public WordmailCount copy() { + return new WordmailCount(this); + } + + @Override + public String toString() { + return "1"; + } + + @Override + public String getMessage() { + return "word in its name"; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WorldBottlingKit.java b/Mage.Sets/src/mage/cards/w/WorldBottlingKit.java new file mode 100644 index 0000000000..60d683a620 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WorldBottlingKit.java @@ -0,0 +1,94 @@ + +package mage.cards.w; + +import java.util.List; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseExpansionSetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.SupertypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author L_J + */ +public final class WorldBottlingKit extends CardImpl { + + public WorldBottlingKit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}"); + + // {5}, Sacrifice World-Bottling Kit: Choose a Magic set. Exile all permanents with that set’s expansion symbol except for basic lands. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new WorldBottlingKitEffect(), new ManaCostsImpl("{5}")); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + public WorldBottlingKit(final WorldBottlingKit card) { + super(card); + } + + @Override + public WorldBottlingKit copy() { + return new WorldBottlingKit(this); + } +} + +class WorldBottlingKitEffect extends OneShotEffect { + + public WorldBottlingKitEffect() { + super(Outcome.DestroyPermanent); + this.staticText = "Choose a Magic set. Exile all permanents with that set’s expansion symbol except for basic lands"; + } + + public WorldBottlingKitEffect(final WorldBottlingKitEffect effect) { + super(effect); + } + + @Override + public WorldBottlingKitEffect copy() { + return new WorldBottlingKitEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + ChooseExpansionSetEffect effect = new ChooseExpansionSetEffect(Outcome.Exile); + effect.apply(game, source); + String setChosen = null; + if (effect.getValue("setchosen") != null) { + setChosen = (String) effect.getValue("setchosen"); + } else if (game.getState().getValue(this.getId() + "_set") != null) { + setChosen = (String) game.getState().getValue(this.getId() + "_set"); + } + if (setChosen != null) { + game.informPlayers(controller.getLogName() + " has chosen set " + setChosen); + FilterPermanent filter = new FilterPermanent(); + filter.add(Predicates.not(Predicates.and(new CardTypePredicate(CardType.LAND), new SupertypePredicate(SuperType.BASIC)))); + List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game); + for (Permanent permanent : permanents) { + if (permanent.getExpansionSetCode().equals(setChosen)) { + controller.moveCardToExileWithInfo(permanent, null, "", source.getSourceId(), game, Zone.BATTLEFIELD, true); + } + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZzzyxassAbyss.java b/Mage.Sets/src/mage/cards/z/ZzzyxassAbyss.java new file mode 100644 index 0000000000..62b9a3cd67 --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZzzyxassAbyss.java @@ -0,0 +1,82 @@ + +package mage.cards.z; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author L_J + */ +public final class ZzzyxassAbyss extends CardImpl { + + public ZzzyxassAbyss(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{B}{B}"); + + // At the beginning of your upkeep, destroy all nonland permanents with the first name alphabetically among nonland permanents. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ZzzyxassAbyssEffect(), TargetController.YOU, false)); + } + + public ZzzyxassAbyss(final ZzzyxassAbyss card) { + super(card); + } + + @Override + public ZzzyxassAbyss copy() { + return new ZzzyxassAbyss(this); + } + +} + +class ZzzyxassAbyssEffect extends OneShotEffect { + + public ZzzyxassAbyssEffect() { + super(Outcome.DestroyPermanent); + this.staticText = "destroy all nonland permanents with the first name alphabetically among nonland permanents"; + } + + public ZzzyxassAbyssEffect(final ZzzyxassAbyssEffect effect) { + super(effect); + } + + @Override + public ZzzyxassAbyssEffect copy() { + return new ZzzyxassAbyssEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + List permanentNames = new ArrayList<>(); + FilterPermanent filter = new FilterNonlandPermanent(); + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, controller.getId(), game)) { + permanentNames.add(permanent.getName()); + } + if (permanentNames.isEmpty()) { + return true; + } + Collections.sort(permanentNames); + filter.add(new NamePredicate(permanentNames.get(0))); + return new DestroyAllEffect(filter).apply(game, source); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Unhinged.java b/Mage.Sets/src/mage/sets/Unhinged.java index a209835217..9a927bad34 100644 --- a/Mage.Sets/src/mage/sets/Unhinged.java +++ b/Mage.Sets/src/mage/sets/Unhinged.java @@ -21,12 +21,36 @@ public final class Unhinged extends ExpansionSet { private Unhinged() { super("Unhinged", "UNH", ExpansionSet.buildDate(2004, 11, 20), SetType.JOKESET); + cards.add(new SetCardInfo("\"Ach! Hans, Run!\"", 116, Rarity.RARE, mage.cards.a.AchHansRun.class)); + cards.add(new SetCardInfo("B-I-N-G-O", 92, Rarity.RARE, mage.cards.b.BINGO.class)); + cards.add(new SetCardInfo("Blast from the Past", 72, Rarity.RARE, mage.cards.b.BlastFromThePast.class)); + cards.add(new SetCardInfo("Bloodletter", 50, Rarity.COMMON, mage.cards.b.Bloodletter.class)); + cards.add(new SetCardInfo("Booster Tutor", 51, Rarity.UNCOMMON, mage.cards.b.BoosterTutor.class)); + cards.add(new SetCardInfo("Double Header", 31, Rarity.COMMON, mage.cards.d.DoubleHeader.class)); + cards.add(new SetCardInfo("First Come, First Served", 12, Rarity.UNCOMMON, mage.cards.f.FirstComeFirstServed.class)); cards.add(new SetCardInfo("Forest", 140, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Form of the Squirrel", 96, Rarity.RARE, mage.cards.f.FormOfTheSquirrel.class)); + cards.add(new SetCardInfo("Goblin Secret Agent", 79, Rarity.COMMON, mage.cards.g.GoblinSecretAgent.class)); cards.add(new SetCardInfo("Island", 137, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Johnny, Combo Player", 35, Rarity.RARE, mage.cards.j.JohnnyComboPlayer.class)); + cards.add(new SetCardInfo("Mana Screw", 123, Rarity.UNCOMMON, mage.cards.m.ManaScrew.class)); + cards.add(new SetCardInfo("Mise", 38, Rarity.UNCOMMON, mage.cards.m.Mise.class)); + cards.add(new SetCardInfo("Monkey Monkey Monkey", 104, Rarity.COMMON, mage.cards.m.MonkeyMonkeyMonkey.class)); cards.add(new SetCardInfo("Mountain", 139, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Mox Lotus", 124, Rarity.RARE, mage.cards.m.MoxLotus.class)); + cards.add(new SetCardInfo("Now I Know My ABC's", 41, Rarity.RARE, mage.cards.n.NowIKnowMyABCs.class)); cards.add(new SetCardInfo("Plains", 136, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Rare-B-Gone", 119, Rarity.RARE, mage.cards.r.RareBGone.class)); + cards.add(new SetCardInfo("Six-y Beast", 89, Rarity.UNCOMMON, mage.cards.s.SixyBeast.class)); cards.add(new SetCardInfo("Swamp", 138, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Symbol Status", 114, Rarity.UNCOMMON, mage.cards.s.SymbolStatus.class)); + cards.add(new SetCardInfo("The Fallen Apart", 55, Rarity.COMMON, mage.cards.t.TheFallenApart.class)); + cards.add(new SetCardInfo("Togglodyte", 129, Rarity.UNCOMMON, mage.cards.t.Togglodyte.class)); + cards.add(new SetCardInfo("Uktabi Kong", 115, Rarity.RARE, mage.cards.u.UktabiKong.class)); + cards.add(new SetCardInfo("Urza's Hot Tub", 131, Rarity.UNCOMMON, mage.cards.u.UrzasHotTub.class)); + cards.add(new SetCardInfo("When Fluffy Bunnies Attack", 67, Rarity.COMMON, mage.cards.w.WhenFluffyBunniesAttack.class)); + cards.add(new SetCardInfo("Wordmail", 22, Rarity.COMMON, mage.cards.w.Wordmail.class)); + cards.add(new SetCardInfo("World-Bottling Kit", 133, Rarity.RARE, mage.cards.w.WorldBottlingKit.class)); + cards.add(new SetCardInfo("Zzzyxas's Abyss", 70, Rarity.RARE, mage.cards.z.ZzzyxassAbyss.class)); } } diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index fef2c75e13..837da1b83c 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -131,6 +131,7 @@ public enum SubType { ELK("Elk", SubTypeSet.CreatureType), EYE("Eye", SubTypeSet.CreatureType), EWOK("Ewok", SubTypeSet.CreatureType, true), // Star Wars + EXPANSION_SYMBOL("Expansion-Symbol", SubTypeSet.CreatureType, true), // Unhinged // F FAERIE("Faerie", SubTypeSet.CreatureType), FERRET("Ferret", SubTypeSet.CreatureType), diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index 865cab23a5..98b2389ee1 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -19,6 +19,7 @@ public enum CounterType { CAGE("cage"), CARRION("carrion"), CHARGE("charge"), + CHIP("chip"), CORPSE("corpse"), CREDIT("credit"), CRYSTAL("crystal"), diff --git a/Mage/src/main/java/mage/game/permanent/token/ExpansionSymbolToken.java b/Mage/src/main/java/mage/game/permanent/token/ExpansionSymbolToken.java new file mode 100644 index 0000000000..1f608901ce --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/ExpansionSymbolToken.java @@ -0,0 +1,29 @@ + +package mage.game.permanent.token; + +import mage.constants.CardType; +import mage.constants.SubType; +import mage.MageInt; + +/** + * + * @author L_J + */ +public final class ExpansionSymbolToken extends TokenImpl { + + public ExpansionSymbolToken() { + super("Expansion-Symbol", "1/1 colorless Expansion-Symbol creature token"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.EXPANSION_SYMBOL); + power = new MageInt(1); + toughness = new MageInt(1); + } + + public ExpansionSymbolToken(final ExpansionSymbolToken token) { + super(token); + } + + public ExpansionSymbolToken copy() { + return new ExpansionSymbolToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/UktabiKongApeToken.java b/Mage/src/main/java/mage/game/permanent/token/UktabiKongApeToken.java new file mode 100644 index 0000000000..8fb6538aaf --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/UktabiKongApeToken.java @@ -0,0 +1,30 @@ + +package mage.game.permanent.token; + +import mage.constants.CardType; +import mage.constants.SubType; +import mage.MageInt; + +/** + * + * @author L_J + */ +public final class UktabiKongApeToken extends TokenImpl { + + public UktabiKongApeToken() { + super("Ape", "1/1 green Ape creature token"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.APE); + color.setGreen(true); + power = new MageInt(1); + toughness = new MageInt(1); + } + + public UktabiKongApeToken(final UktabiKongApeToken token) { + super(token); + } + + public UktabiKongApeToken copy() { + return new UktabiKongApeToken(this); + } +}