diff --git a/Mage.Sets/src/mage/cards/c/ChefsKiss.java b/Mage.Sets/src/mage/cards/c/ChefsKiss.java new file mode 100644 index 00000000000..46b15590b98 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChefsKiss.java @@ -0,0 +1,151 @@ +package mage.cards.c; + +import mage.MageItem; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.target.Target; +import mage.target.TargetSpell; +import mage.util.RandomUtil; +import mage.util.TargetAddress; +import mage.util.functions.StackObjectCopyApplier; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class ChefsKiss extends CardImpl { + + private static final FilterSpell filter + = new FilterSpell("spell that targets only a single permanent or player"); + + static { + filter.add(ChefsKissPredicate.instance); + } + + public ChefsKiss(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}{R}"); + + // Gain control of target spell that targets only a single permanent or player. Copy it, then reselect the targets at random for the spell and the copy. The new targets can't be you or a permanent you control. + this.getSpellAbility().addEffect(new ChefsKissEffect()); + this.getSpellAbility().addTarget(new TargetSpell(filter)); + } + + private ChefsKiss(final ChefsKiss card) { + super(card); + } + + @Override + public ChefsKiss copy() { + return new ChefsKiss(this); + } +} + +enum ChefsKissPredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject input, Game game) { + Spell spell = (Spell) input; + Set targets = new HashSet<>(); + for (TargetAddress addr : TargetAddress.walk(spell)) { + Target targetInstance = addr.getTarget(spell); + for (UUID target : targetInstance.getTargets()) { + if (game.getPermanent(target) != null || game.getPlayer(target) != null) { + targets.add(target); + } + if (targets.size() > 1) { + return false; + } + } + } + return targets.size() == 1; + } +} + +class ChefsKissEffect extends OneShotEffect { + + ChefsKissEffect() { + super(Outcome.Benefit); + staticText = "gain control of target spell that targets only a single permanent or player. " + + "Copy it, then reselect the targets at random for the spell and the copy. " + + "The new targets can't be you or a permanent you control"; + } + + private ChefsKissEffect(final ChefsKissEffect effect) { + super(effect); + } + + @Override + public ChefsKissEffect copy() { + return new ChefsKissEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = game.getSpell(source.getFirstTarget()); + if (spell == null) { + return false; + } + spell.setControllerId(source.getControllerId()); + List possibleTargets = new ArrayList<>(game.getState().getPlayersInRange(source.getControllerId(), game)); + possibleTargets.remove(source.getControllerId()); + game.getBattlefield() + .getActivePermanents(source.getControllerId(), game) + .stream() + .filter(p -> !p.isControlledBy(source.getControllerId())) + .map(MageItem::getId) + .forEach(possibleTargets::add); + possibleTargets.removeIf(uuid -> !spell.canTarget(game, uuid)); + StackObjectCopyApplier applier; + MageObjectReferencePredicate predicate; + if (possibleTargets.isEmpty()) { + applier = null; + predicate = null; + } else { + applier = new ChefsKissApplier(possibleTargets.get(RandomUtil.nextInt(possibleTargets.size())), game); + predicate = new MageObjectReferencePredicate(new MageObjectReference( + possibleTargets.get(RandomUtil.nextInt(possibleTargets.size())), game + )); + } + spell.createCopyOnStack(game, source, source.getControllerId(), false, 1, applier); + if (predicate != null) { + spell.chooseNewTargets(game, source.getControllerId(), true, true, predicate); + } + return true; + } +} + +class ChefsKissApplier implements StackObjectCopyApplier { + + private final Iterator predicate; + + ChefsKissApplier(UUID targetId, Game game) { + this.predicate = Arrays.asList(new MageObjectReferencePredicate( + new MageObjectReference(targetId, game) + )).iterator(); + } + + @Override + public void modifySpell(StackObject stackObject, Game game) { + } + + @Override + public MageObjectReferencePredicate getNextPredicate() { + if (predicate.hasNext()) { + return predicate.next(); + } + return null; + } +} diff --git a/Mage.Sets/src/mage/sets/ModernHorizons2.java b/Mage.Sets/src/mage/sets/ModernHorizons2.java index af2ebea5295..05e81f8fc96 100644 --- a/Mage.Sets/src/mage/sets/ModernHorizons2.java +++ b/Mage.Sets/src/mage/sets/ModernHorizons2.java @@ -75,6 +75,7 @@ public final class ModernHorizons2 extends ExpansionSet { cards.add(new SetCardInfo("Chance Encounter", 277, Rarity.RARE, mage.cards.c.ChanceEncounter.class)); cards.add(new SetCardInfo("Chatterfang, Squirrel General", 151, Rarity.MYTHIC, mage.cards.c.ChatterfangSquirrelGeneral.class)); cards.add(new SetCardInfo("Chatterstorm", 152, Rarity.COMMON, mage.cards.c.Chatterstorm.class)); + cards.add(new SetCardInfo("Chef's Kiss", 120, Rarity.RARE, mage.cards.c.ChefsKiss.class)); cards.add(new SetCardInfo("Clattering Augur", 79, Rarity.UNCOMMON, mage.cards.c.ClatteringAugur.class)); cards.add(new SetCardInfo("Chitterspitter", 153, Rarity.RARE, mage.cards.c.Chitterspitter.class)); cards.add(new SetCardInfo("Chrome Courier", 190, Rarity.COMMON, mage.cards.c.ChromeCourier.class)); diff --git a/Mage/src/main/java/mage/game/stack/StackObject.java b/Mage/src/main/java/mage/game/stack/StackObject.java index 7e078e61ef0..af396000c54 100644 --- a/Mage/src/main/java/mage/game/stack/StackObject.java +++ b/Mage/src/main/java/mage/game/stack/StackObject.java @@ -1,5 +1,6 @@ package mage.game.stack; +import mage.MageItem; import mage.MageObject; import mage.abilities.Ability; import mage.constants.Zone; @@ -28,7 +29,7 @@ public interface StackObject extends MageObject, Controllable { Ability getStackAbility(); - boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, Predicate extraPredicate); + boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, Predicate extraPredicate); boolean canTarget(Game game, UUID targetId); diff --git a/Mage/src/main/java/mage/game/stack/StackObjectImpl.java b/Mage/src/main/java/mage/game/stack/StackObjectImpl.java index d67130d9e18..52a3b21f7dd 100644 --- a/Mage/src/main/java/mage/game/stack/StackObjectImpl.java +++ b/Mage/src/main/java/mage/game/stack/StackObjectImpl.java @@ -1,5 +1,6 @@ package mage.game.stack; +import mage.MageItem; import mage.MageObject; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; @@ -219,7 +220,7 @@ public abstract class StackObjectImpl implements StackObject { * @return */ @Override - public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forceChange, boolean onlyOneTarget, Predicate extraPredicate) { + public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forceChange, boolean onlyOneTarget, Predicate extraPredicate) { Player targetController = game.getPlayer(targetControllerId); if (targetController != null) { StringBuilder oldTargetDescription = new StringBuilder();