diff --git a/Mage.Sets/src/mage/cards/e/EmissaryOfGrudges.java b/Mage.Sets/src/mage/cards/e/EmissaryOfGrudges.java index e0c64e50a19..80d2d256f42 100644 --- a/Mage.Sets/src/mage/cards/e/EmissaryOfGrudges.java +++ b/Mage.Sets/src/mage/cards/e/EmissaryOfGrudges.java @@ -3,10 +3,9 @@ package mage.cards.e; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; +import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.RevealSecretOpponentCost; -import mage.abilities.effects.EntersBattlefieldEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ChooseSecretOpponentEffect; import mage.abilities.keyword.FlyingAbility; @@ -16,7 +15,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; @@ -44,10 +42,11 @@ public class EmissaryOfGrudges extends CardImpl { this.addAbility(HasteAbility.getInstance()); // As Emissary of Grudges enters the battlefield, secretly choose an opponent. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new EntersBattlefieldEffect(new ChooseSecretOpponentEffect(), "As {this} enters the battlefield, secretly choose an opponent."))); + this.addAbility(new AsEntersBattlefieldAbility(new ChooseSecretOpponentEffect())); + // Choose new targets for target spell or ability if it’s controlled by the chosen player and if it targets you // or a permanent you control. Activate this ability only once. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new EmissaryOfGrudgesEffect(), new RevealSecretOpponentCost()); + Ability ability = new SimpleActivatedAbility(new EmissaryOfGrudgesEffect(), new RevealSecretOpponentCost()); ability.addTarget(new TargetStackObject()); this.addAbility(ability); } @@ -82,34 +81,30 @@ class EmissaryOfGrudgesEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); - if (stackObject != null) { - UUID opponentId = (UUID) game.getState().getValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OPPONENT); - if (opponentId != null && opponentId.equals(stackObject.getControllerId())) { - // find if it targets you or a permanent you control - boolean targetsYouOrAPermanentYouControl = false; - for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) { - Mode mode = stackObject.getStackAbility().getModes().get(modeId); - for (Target target : mode.getTargets()) { - for (UUID targetId : target.getTargets()) { - if (source.isControlledBy(targetId)) { - targetsYouOrAPermanentYouControl = true; - } - Permanent permanent = game.getPermanent(targetId); - if (permanent != null && source.isControlledBy(permanent.getControllerId())) { - targetsYouOrAPermanentYouControl = true; - } - } - } + StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); + if (controller == null || stackObject == null + || !stackObject.isControlledBy(ChooseSecretOpponentEffect.getChosenPlayer(source, game))) { + return false; + } + // find if it targets you or a permanent you control + boolean targetsYouOrAPermanentYouControl = false; + for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) { + Mode mode = stackObject.getStackAbility().getModes().get(modeId); + for (Target target : mode.getTargets()) { + for (UUID targetId : target.getTargets()) { + if (source.isControlledBy(targetId)) { + targetsYouOrAPermanentYouControl = true; } - if (targetsYouOrAPermanentYouControl) { - return stackObject.chooseNewTargets(game, source.getControllerId(), false, false, null); + Permanent permanent = game.getPermanent(targetId); + if (permanent != null && source.isControlledBy(permanent.getControllerId())) { + targetsYouOrAPermanentYouControl = true; } } } - return true; } - return false; + if (targetsYouOrAPermanentYouControl) { + return stackObject.chooseNewTargets(game, source.getControllerId(), false, false, null); + } + return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/g/GuardianArchon.java b/Mage.Sets/src/mage/cards/g/GuardianArchon.java new file mode 100644 index 00000000000..e2669d72f0e --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GuardianArchon.java @@ -0,0 +1,141 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.RevealSecretOpponentCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseSecretOpponentEffect; +import mage.abilities.effects.common.continuous.GainAbilityControllerEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.ProtectionAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GuardianArchon extends CardImpl { + + public GuardianArchon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}"); + + this.subtype.add(SubType.ARCHON); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // As Guardian Archon enters the battlefield, secretly choose an opponent. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseSecretOpponentEffect())); + + // Reveal the player you chose: You and target permanent you control each gain protection from the chosen player until end of turn. Activate only once. + Ability ability = new SimpleActivatedAbility(new GuardianArchonEffect(), new RevealSecretOpponentCost()); + ability.addTarget(new TargetControlledPermanent()); + this.addAbility(ability); + } + + private GuardianArchon(final GuardianArchon card) { + super(card); + } + + @Override + public GuardianArchon copy() { + return new GuardianArchon(this); + } +} + +class GuardianArchonEffect extends OneShotEffect { + + GuardianArchonEffect() { + super(Outcome.Benefit); + staticText = "you and target permanent you control each gain protection " + + "from the chosen player until end of turn. Activate only once"; + } + + private GuardianArchonEffect(final GuardianArchonEffect effect) { + super(effect); + } + + @Override + public GuardianArchonEffect copy() { + return new GuardianArchonEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(ChooseSecretOpponentEffect.getChosenPlayer(source, game)); + if (player == null) { + return false; + } + game.addEffect(new GainAbilityTargetEffect( + new GuardianArchonProtectionAbility(player.getId()), Duration.EndOfTurn + ), source); + game.addEffect(new GainAbilityControllerEffect( + new GuardianArchonProtectionAbility(player.getId()), Duration.EndOfTurn + ), source); + return true; + } +} + +class GuardianArchonProtectionAbility extends ProtectionAbility { + + private final UUID playerId; + + public GuardianArchonProtectionAbility(UUID playerId) { + super(new FilterCard()); + this.playerId = playerId; + } + + public GuardianArchonProtectionAbility(final GuardianArchonProtectionAbility ability) { + super(ability); + this.playerId = ability.playerId; + } + + @Override + public GuardianArchonProtectionAbility copy() { + return new GuardianArchonProtectionAbility(this); + } + + @Override + public String getRule() { + return "{this} has protection from the chosen player."; + } + + @Override + public boolean canTarget(MageObject source, Game game) { + if (playerId != null && source != null) { + if (source instanceof Permanent) { + return !((Permanent) source).isControlledBy(playerId); + } + if (source instanceof Spell) { + return !((Spell) source).isControlledBy(playerId); + } + if (source instanceof StackObject) { + return !((StackObject) source).isControlledBy(playerId); + } + if (source instanceof Card) { // e.g. for Vengeful Pharaoh + return !((Card) source).isOwnedBy(playerId); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/StalkingLeonin.java b/Mage.Sets/src/mage/cards/s/StalkingLeonin.java index 6a30a9b01af..9f47b2abeb5 100644 --- a/Mage.Sets/src/mage/cards/s/StalkingLeonin.java +++ b/Mage.Sets/src/mage/cards/s/StalkingLeonin.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.MageInt; @@ -14,20 +13,22 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreatureAttackingYou; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.filter.common.FilterCreatureAttackingYou; -import mage.target.common.TargetCreaturePermanent; +import mage.target.TargetPermanent; import java.util.UUID; /** - * * @author LevelX2 */ public final class StalkingLeonin extends CardImpl { + private static final FilterPermanent filter = new FilterCreatureAttackingYou(); + public StalkingLeonin(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); @@ -37,9 +38,10 @@ public final class StalkingLeonin extends CardImpl { // When Stalking Leonin enters the battlefield, secretly choose an opponent. this.addAbility(new EntersBattlefieldTriggeredAbility(new ChooseSecretOpponentEffect(), false)); + // Reveal the player you chose: Exile target creature that's attacking you if it's controlled by the chosen player. Activate this ability only once. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new StalkingLeoninEffect(), new RevealSecretOpponentCost()); - ability.addTarget(new TargetCreaturePermanent(new FilterCreatureAttackingYou())); + Ability ability = new SimpleActivatedAbility(new StalkingLeoninEffect(), new RevealSecretOpponentCost()); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } @@ -72,16 +74,12 @@ class StalkingLeoninEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (targetCreature != null) { - UUID opponentId = (UUID) game.getState().getValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OPPONENT); - if (opponentId != null && opponentId.equals(targetCreature.getControllerId())) { - controller.moveCards(targetCreature, Zone.EXILED, source, game); - } - } + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (controller == null || targetCreature == null + || !targetCreature.isControlledBy(ChooseSecretOpponentEffect.getChosenPlayer(source, game))) { return true; } - return false; + controller.moveCards(targetCreature, Zone.EXILED, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/sets/Commander2021Edition.java b/Mage.Sets/src/mage/sets/Commander2021Edition.java index bfb4a5b2fce..62f524985ef 100644 --- a/Mage.Sets/src/mage/sets/Commander2021Edition.java +++ b/Mage.Sets/src/mage/sets/Commander2021Edition.java @@ -139,6 +139,7 @@ public final class Commander2021Edition extends ExpansionSet { cards.add(new SetCardInfo("Golgari Rot Farm", 291, Rarity.UNCOMMON, mage.cards.g.GolgariRotFarm.class)); cards.add(new SetCardInfo("Great Furnace", 292, Rarity.COMMON, mage.cards.g.GreatFurnace.class)); cards.add(new SetCardInfo("Greed", 145, Rarity.RARE, mage.cards.g.Greed.class)); + cards.add(new SetCardInfo("Guardian Archon", 17, Rarity.RARE, mage.cards.g.GuardianArchon.class)); cards.add(new SetCardInfo("Guardian Augmenter", 62, Rarity.RARE, mage.cards.g.GuardianAugmenter.class)); cards.add(new SetCardInfo("Gyome, Master Chef", 5, Rarity.MYTHIC, mage.cards.g.GyomeMasterChef.class)); cards.add(new SetCardInfo("Healing Technique", 63, Rarity.RARE, mage.cards.h.HealingTechnique.class)); diff --git a/Mage/src/main/java/mage/abilities/costs/common/RevealSecretOpponentCost.java b/Mage/src/main/java/mage/abilities/costs/common/RevealSecretOpponentCost.java index 6b9e0929124..aa4ae4bdbcc 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/RevealSecretOpponentCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/RevealSecretOpponentCost.java @@ -12,57 +12,51 @@ import mage.players.Player; import java.util.UUID; /** - * * @author LevelX2 * @author credman0 */ public class RevealSecretOpponentCost extends CostImpl { - public RevealSecretOpponentCost() { - this.text = "Reveal the player you chose"; - } + public RevealSecretOpponentCost() { + this.text = "Reveal the player you chose"; + } - public RevealSecretOpponentCost(final RevealSecretOpponentCost cost) { - super(cost); - } + public RevealSecretOpponentCost(final RevealSecretOpponentCost cost) { + super(cost); + } - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - UUID playerThatChoseId = (UUID) game.getState().getValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OWNER); - if (playerThatChoseId == null || !playerThatChoseId.equals(controllerId)) { - return false; - } - UUID opponentId = (UUID) game.getState().getValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OPPONENT); - return opponentId != null; - } + @Override + public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + return controllerId != null + && controllerId.equals(ChooseSecretOpponentEffect.getSecretOwner(source, game)) + && ChooseSecretOpponentEffect.getChosenPlayer(source, game) != null; + } - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - UUID playerThatChoseId = (UUID) game.getState().getValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OWNER); - if (playerThatChoseId == null || !playerThatChoseId.equals(controllerId)) { - return false; - } - UUID opponentId = (UUID) game.getState().getValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OPPONENT); - if (opponentId != null) { - game.getState().setValue(source.getSourceId() + ChooseSecretOpponentEffect.SECRET_OWNER, null); // because only once, the vale is set to null - Player controller = game.getPlayer(controllerId); - Player opponent = game.getPlayer(opponentId); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && opponent != null && sourceObject != null) { - if (sourceObject instanceof Permanent) { - ((Permanent) sourceObject).addInfo(ChooseSecretOpponentEffect.SECRET_OPPONENT, null, game); - } - game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " reveals the secretly chosen opponent " + opponent.getLogName()); - } - paid = true; - } + @Override + public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { + if (controllerId == null || !controllerId.equals(ChooseSecretOpponentEffect.getSecretOwner(source, game))) { + return false; + } + UUID opponentId = ChooseSecretOpponentEffect.getChosenPlayer(source, game); + if (opponentId == null) { return paid; } - - @Override - public RevealSecretOpponentCost copy() { - return new RevealSecretOpponentCost(this); + ChooseSecretOpponentEffect.setSecretOwner(null, source, game); // because only once, the value is set to null + Player controller = game.getPlayer(controllerId); + Player opponent = game.getPlayer(opponentId); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && opponent != null && sourceObject != null) { + if (sourceObject instanceof Permanent) { + ((Permanent) sourceObject).addInfo(ChooseSecretOpponentEffect.SECRET_OPPONENT, null, game); + } + game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() + " reveals the secretly chosen opponent " + opponent.getLogName()); } + paid = true; + return paid; + } - + @Override + public RevealSecretOpponentCost copy() { + return new RevealSecretOpponentCost(this); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseSecretOpponentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseSecretOpponentEffect.java index e8b893706cb..e85b9ec7078 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChooseSecretOpponentEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseSecretOpponentEffect.java @@ -4,16 +4,19 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author LevelX2 * @author credman0 + * @author TheElk801 */ public class ChooseSecretOpponentEffect extends OneShotEffect { @@ -36,33 +39,53 @@ public class ChooseSecretOpponentEffect extends OneShotEffect { if (mageObject == null) { mageObject = game.getObject(source.getSourceId()); } - if (controller != null && mageObject != null) { - TargetOpponent targetOpponent = new TargetOpponent(true); - targetOpponent.setTargetName("opponent (secretly)"); - while (!controller.choose(outcome, targetOpponent, source.getSourceId(), game)) { - if (!controller.canRespond()) { - return false; - } - } - if (targetOpponent.getTargets().isEmpty()) { - return false; - } - if (!game.isSimulation()) { - game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has secretly chosen an opponent."); - } - game.getState().setValue(mageObject.getId() + SECRET_OPPONENT, targetOpponent.getTargets().get(0)); - game.getState().setValue(mageObject.getId() + SECRET_OWNER, controller.getId()); - if (mageObject instanceof Permanent) { - ((Permanent) mageObject).addInfo(SECRET_OPPONENT, - CardUtil.addToolTipMarkTags(controller.getLogName() + " has secretly chosen an opponent."), game); - } + if (controller == null || mageObject == null) { + return false; } - return false; + TargetOpponent targetOpponent = new TargetOpponent(true); + targetOpponent.setTargetName("opponent (secretly)"); + controller.choose(outcome, targetOpponent, source.getSourceId(), game); + if (targetOpponent.getFirstTarget() == null) { + return false; + } + game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has secretly chosen an opponent."); + setChosenPlayer(targetOpponent.getFirstTarget(), source, game); + setSecretOwner(controller.getId(), source, game); + if (!(mageObject instanceof Permanent)) { + return true; + } + ((Permanent) mageObject).addInfo( + SECRET_OPPONENT, CardUtil.addToolTipMarkTags( + controller.getLogName() + " has secretly chosen an opponent." + ), game); + return true; + } + + public static void setChosenPlayer(UUID value, Ability source, Game game) { + game.getState().setValue(getthing(source, game) + ChooseSecretOpponentEffect.SECRET_OPPONENT, value); + } + + public static UUID getChosenPlayer(Ability source, Game game) { + return (UUID) game.getState().getValue(getthing(source, game) + ChooseSecretOpponentEffect.SECRET_OPPONENT); + } + + public static void setSecretOwner(UUID value, Ability source, Game game) { + game.getState().setValue(getthing(source, game) + ChooseSecretOpponentEffect.SECRET_OWNER, value); + } + + public static UUID getSecretOwner(Ability source, Game game) { + return (UUID) game.getState().getValue(getthing(source, game) + ChooseSecretOpponentEffect.SECRET_OWNER); + } + + private static String getthing(Ability source, Game game) { + if (game.getState().getZone(source.getSourceId()) == Zone.BATTLEFIELD) { + return "" + source.getSourceId() + '_' + source.getSourceObjectZoneChangeCounter(); + } + return "" + source.getSourceId() + '_' + (source.getSourceObjectZoneChangeCounter() + 1); } @Override public ChooseSecretOpponentEffect copy() { return new ChooseSecretOpponentEffect(this); } - }