From b2fe13c8c8df754541780b0d74884daa73baa83e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 16 Apr 2015 15:57:17 +0200 Subject: [PATCH] * Fixed attack tap handling - You can no longer attack with a mana producing creature that will be get tapped for attacking and use the creature itself to produce mana to pay for effects like Ghostly Prison if the mana ability has the cost to tap the creature. --- .../src/mage/player/ai/SimulatedPlayer2.java | 5 +- .../mage/player/ai/SimulatedPlayerMCTS.java | 5 +- .../src/mage/player/ai/SimulatedPlayer.java | 8 ++- .../sets/bornofthegods/FloodtideSerpent.java | 6 -- .../championsofkamigawa/GhostlyPrison.java | 37 ++++++------ .../mage/sets/commander/KaaliaOfTheVast.java | 2 +- .../sets/commander2013/MysticBarrier.java | 31 +++++----- .../fifthedition/EvilEyeOfOrmsByGore.java | 29 +++++----- .../sets/futuresight/OrissSamiteGuardian.java | 33 ++++------- .../src/mage/sets/newphyrexia/NornsAnnex.java | 44 +++++++------- .../sets/returntoravnica/SphereOfSafety.java | 57 ++++++++----------- .../sets/saviorsofkamigawa/CowedByWisdom.java | 26 ++++----- .../src/mage/sets/tempest/LightOfDay.java | 39 +++++++------ .../src/mage/sets/tempest/Propaganda.java | 30 +++++----- .../src/mage/sets/tenth/WindbornMuse.java | 45 +++++++-------- .../src/mage/sets/visions/ElephantGrass.java | 19 +++---- .../org/mage/test/player/RandomPlayer.java | 10 +++- .../abilities/keyword/NinjutsuAbility.java | 2 +- Mage/src/mage/game/combat/Combat.java | 26 ++++++--- Mage/src/mage/players/PlayerImpl.java | 4 +- 20 files changed, 224 insertions(+), 234 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java index bc197b421a8..d58a47dbaa0 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java @@ -347,7 +347,10 @@ public class SimulatedPlayer2 extends ComputerPlayer { } for (int j = 0; j < attackersList.size(); j++) { if (binary.charAt(j) == '1') { - sim.getCombat().declareAttacker(attackersList.get(j).getId(), defenderId, sim); + setStoredBookmark(sim.bookmarkState()); // makes it possible to UNDO a declared attacker with costs from e.g. Propaganda + if(!sim.getCombat().declareAttacker(attackersList.get(j).getId(), defenderId, playerId, sim)) { + sim.undo(playerId); + } } } if (engagements.put(sim.getCombat().getValue().hashCode(), sim.getCombat()) != null) { diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java index 139710ad9dc..c64f365fd43 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java @@ -187,7 +187,10 @@ public class SimulatedPlayerMCTS extends MCTSPlayer { } for (int i = 0; i < attackersList.size(); i++) { if (binary.charAt(i) == '1') { - game.getCombat().declareAttacker(attackersList.get(i).getId(), defenderId, game); + setStoredBookmark(game.bookmarkState()); // makes it possible to UNDO a declared attacker with costs from e.g. Propaganda + if (!game.getCombat().declareAttacker(attackersList.get(i).getId(), defenderId, playerId, game)) { + game.undo(playerId); + } } } actionCount++; diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java index b114403fa5b..a43eddaa763 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java @@ -177,8 +177,12 @@ public class SimulatedPlayer extends ComputerPlayer { binary.insert(0, "0"); } for (int j = 0; j < attackersList.size(); j++) { - if (binary.charAt(j) == '1') - sim.getCombat().declareAttacker(attackersList.get(j).getId(), defenderId, sim); + if (binary.charAt(j) == '1') { + setStoredBookmark(sim.bookmarkState()); // makes it possible to UNDO a declared attacker with costs from e.g. Propaganda + if (!sim.getCombat().declareAttacker(attackersList.get(j).getId(), defenderId, playerId, sim)) { + sim.undo(playerId); + } + } } if (engagements.put(sim.getCombat().getValue().hashCode(), sim.getCombat()) != null) { logger.debug("simulating -- found redundant attack combination"); diff --git a/Mage.Sets/src/mage/sets/bornofthegods/FloodtideSerpent.java b/Mage.Sets/src/mage/sets/bornofthegods/FloodtideSerpent.java index 3fbea0866eb..efcdd29219f 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/FloodtideSerpent.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/FloodtideSerpent.java @@ -58,7 +58,6 @@ public class FloodtideSerpent extends CardImpl { this.expansionSetCode = "BNG"; this.subtype.add("Serpent"); - this.color.setBlue(true); this.power = new MageInt(4); this.toughness = new MageInt(4); @@ -95,11 +94,6 @@ class FloodtideSerpentReplacementEffect extends ReplacementEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - throw new UnsupportedOperationException("Not supported."); - } - @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(event.getPlayerId()); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/GhostlyPrison.java b/Mage.Sets/src/mage/sets/championsofkamigawa/GhostlyPrison.java index 394917e45cd..33e3749890f 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/GhostlyPrison.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/GhostlyPrison.java @@ -80,36 +80,35 @@ class GhostlyPrisonReplacementEffect extends ReplacementEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - throw new UnsupportedOperationException("Not supported."); + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; } @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - if ( event.getType() == GameEvent.EventType.DECLARE_ATTACKER) { - Player player = game.getPlayer(event.getPlayerId()); - if ( player != null && event.getTargetId().equals(source.getControllerId())) { - ManaCostsImpl attackTax = new ManaCostsImpl("{2}"); - if ( attackTax.canPay(source, source.getSourceId(), event.getPlayerId(), game) && - player.chooseUse(Outcome.Benefit, "Pay {2} to attack player?", game) ) { - if (attackTax.payOrRollback(source, game, this.getId(), event.getPlayerId())) { - return false; - } - } + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getTargetId().equals(source.getControllerId()) ) { + Player attackedPlayer = game.getPlayer(event.getTargetId()); + if (attackedPlayer != null) { + // only if a player is attacked. Attacking a planeswalker is free return true; } } return false; } + @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if ( event.getType() == GameEvent.EventType.DECLARE_ATTACKER && event.getTargetId().equals(source.getControllerId()) ) { - Player attackedPlayer = game.getPlayer(event.getTargetId()); - if (attackedPlayer != null) { - // only if a player is attacked. Attacking a planeswalker is free - return true; + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(event.getPlayerId()); + if ( player != null && event.getTargetId().equals(source.getControllerId())) { + ManaCostsImpl attackTax = new ManaCostsImpl("{2}"); + if (attackTax.canPay(source, source.getSourceId(), event.getPlayerId(), game) && + player.chooseUse(Outcome.Benefit, "Pay {2} to attack player?", game) ) { + if (attackTax.payOrRollback(source, game, this.getId(), event.getPlayerId())) { + return false; + } } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/commander/KaaliaOfTheVast.java b/Mage.Sets/src/mage/sets/commander/KaaliaOfTheVast.java index 4602fabd450..1fda32b5b58 100644 --- a/Mage.Sets/src/mage/sets/commander/KaaliaOfTheVast.java +++ b/Mage.Sets/src/mage/sets/commander/KaaliaOfTheVast.java @@ -159,7 +159,7 @@ class KaaliaOfTheVastEffect extends OneShotEffect { player.putOntoBattlefieldWithInfo(card, game, Zone.HAND, source.getSourceId(), true); Permanent creature = game.getPermanent(cardId); if (creature != null) { - game.getCombat().declareAttacker(card.getId(), defenderId, game); + game.getCombat().addAttackerToCombat(card.getId(), defenderId, game); return true; } diff --git a/Mage.Sets/src/mage/sets/commander2013/MysticBarrier.java b/Mage.Sets/src/mage/sets/commander2013/MysticBarrier.java index 2ede8ab6a7f..d09ebee60d1 100644 --- a/Mage.Sets/src/mage/sets/commander2013/MysticBarrier.java +++ b/Mage.Sets/src/mage/sets/commander2013/MysticBarrier.java @@ -65,8 +65,6 @@ public class MysticBarrier extends CardImpl { super(ownerId, 18, "Mystic Barrier", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}"); this.expansionSetCode = "C13"; - this.color.setWhite(true); - // When Mystic Barrier enters the battlefield or at the beginning of your upkeep, choose left or right. this.addAbility(new MysticBarrierTriggeredAbility()); @@ -98,16 +96,19 @@ class MysticBarrierTriggeredAbility extends TriggeredAbilityImpl { public MysticBarrierTriggeredAbility copy() { return new MysticBarrierTriggeredAbility(this); } - + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType().equals(EventType.ENTERS_THE_BATTLEFIELD) || event.getType().equals(EventType.UPKEEP_STEP_PRE); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType().equals(EventType.ENTERS_THE_BATTLEFIELD) && event.getTargetId().equals(this.getSourceId())) { - return true; + if (event.getType().equals(EventType.ENTERS_THE_BATTLEFIELD)) { + return event.getTargetId().equals(this.getSourceId()); + } else { + return event.getPlayerId().equals(this.getControllerId()); } - if (event.getType().equals(EventType.UPKEEP_STEP_PRE) && event.getPlayerId().equals(this.getControllerId())) { - return true; - } - return false; } @Override @@ -170,19 +171,19 @@ class MysticBarrierReplacementEffect extends ReplacementEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - throw new UnsupportedOperationException("Not supported."); - } - @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { return true; } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if ( event.getType() == GameEvent.EventType.DECLARE_ATTACKER && game.getPlayers().size() > 2) { + if (game.getPlayers().size() > 2) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { if (controller.getInRange().contains(event.getPlayerId())) { diff --git a/Mage.Sets/src/mage/sets/fifthedition/EvilEyeOfOrmsByGore.java b/Mage.Sets/src/mage/sets/fifthedition/EvilEyeOfOrmsByGore.java index ee9c9df8b74..1330a2c5b9f 100644 --- a/Mage.Sets/src/mage/sets/fifthedition/EvilEyeOfOrmsByGore.java +++ b/Mage.Sets/src/mage/sets/fifthedition/EvilEyeOfOrmsByGore.java @@ -64,7 +64,6 @@ public class EvilEyeOfOrmsByGore extends CardImpl { this.expansionSetCode = "5ED"; this.subtype.add("Eye"); - this.color.setBlack(true); this.power = new MageInt(3); this.toughness = new MageInt(6); @@ -96,33 +95,31 @@ class EvilEyeOfOrmsByGoreEffect extends ReplacementEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public EvilEyeOfOrmsByGoreEffect copy() { return new EvilEyeOfOrmsByGoreEffect(this); } @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; + } @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == EventType.DECLARE_ATTACKER) { - Permanent permanent = game.getPermanent(event.getSourceId()); - if (permanent != null) { - if (permanent.getControllerId().equals(source.getControllerId())) { - if (!permanent.hasSubtype("Eye")) { - return true; - } + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent != null) { + if (permanent.getControllerId().equals(source.getControllerId())) { + if (!permanent.hasSubtype("Eye")) { + return true; } } } return false; } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } } diff --git a/Mage.Sets/src/mage/sets/futuresight/OrissSamiteGuardian.java b/Mage.Sets/src/mage/sets/futuresight/OrissSamiteGuardian.java index 55f7eb83046..b9c7b6a04cf 100644 --- a/Mage.Sets/src/mage/sets/futuresight/OrissSamiteGuardian.java +++ b/Mage.Sets/src/mage/sets/futuresight/OrissSamiteGuardian.java @@ -61,7 +61,6 @@ public class OrissSamiteGuardian extends CardImpl { this.subtype.add("Human"); this.subtype.add("Cleric"); - this.color.setWhite(true); this.power = new MageInt(1); this.toughness = new MageInt(3); @@ -104,19 +103,14 @@ class OrissSamiteGuardianCantCastEffect extends ContinuousRuleModifyingEffectImp } @Override - public boolean apply(Game game, Ability source) { - return true; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.CAST_SPELL; } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == EventType.CAST_SPELL) { - Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (player != null && player.getId().equals(event.getPlayerId())) { - return true; - } - } - return false; + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + return player != null && player.getId().equals(event.getPlayerId()); } } @@ -135,20 +129,15 @@ class OrissSamiteGuardianCantAttackEffect extends ContinuousRuleModifyingEffectI public OrissSamiteGuardianCantAttackEffect copy() { return new OrissSamiteGuardianCantAttackEffect(this); } - + @Override - public boolean apply(Game game, Ability source) { - return true; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == EventType.DECLARE_ATTACKER) { - Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (player != null && player.getId().equals(event.getPlayerId())) { - return true; - } - } - return false; + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + return player != null && player.getId().equals(event.getPlayerId()); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/newphyrexia/NornsAnnex.java b/Mage.Sets/src/mage/sets/newphyrexia/NornsAnnex.java index bec57fffa54..f3c673928c4 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/NornsAnnex.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/NornsAnnex.java @@ -79,30 +79,12 @@ class NornsAnnexReplacementEffect extends ReplacementEffectImpl { NornsAnnexReplacementEffect(NornsAnnexReplacementEffect effect) { super(effect); } - + @Override - public boolean apply(Game game, Ability source) { - throw new UnsupportedOperationException("Not supported."); + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.DECLARE_ATTACKER) { - Player player = game.getPlayer(event.getPlayerId()); - if (player != null && event.getTargetId().equals(source.getControllerId())) { - ManaCostsImpl propagandaTax = new ManaCostsImpl("{WP}"); - if (propagandaTax.canPay(source, source.getSourceId(), event.getPlayerId(), game) && - player.chooseUse(Outcome.Benefit, "Pay {WP} to declare attacker?", game)) { - if (propagandaTax.payOrRollback(source, game, this.getId(), event.getPlayerId())) { - return false; - } - } - } - return true; - } - return false; - } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType() == GameEvent.EventType.DECLARE_ATTACKER) { @@ -119,6 +101,24 @@ class NornsAnnexReplacementEffect extends ReplacementEffectImpl { return false; } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(event.getPlayerId()); + if (player != null && event.getTargetId().equals(source.getControllerId())) { + ManaCostsImpl propagandaTax = new ManaCostsImpl("{WP}"); + if (propagandaTax.canPay(source, source.getSourceId(), event.getPlayerId(), game) && + player.chooseUse(Outcome.Benefit, "Pay {WP} to declare attacker?", game)) { + if (propagandaTax.payOrRollback(source, game, this.getId(), event.getPlayerId())) { + return false; + } + } + return true; + } + return false; + } + + @Override public NornsAnnexReplacementEffect copy() { return new NornsAnnexReplacementEffect(this); diff --git a/Mage.Sets/src/mage/sets/returntoravnica/SphereOfSafety.java b/Mage.Sets/src/mage/sets/returntoravnica/SphereOfSafety.java index 3afcbdedd43..203b13ab4f9 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/SphereOfSafety.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/SphereOfSafety.java @@ -54,8 +54,6 @@ public class SphereOfSafety extends CardImpl { super(ownerId, 24, "Sphere of Safety", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}"); this.expansionSetCode = "RTR"; - this.color.setWhite(true); - // Creatures can't attack you or a planeswalker you control unless their controller pays {X} for each of those creatures, where X is the number of enchantments you control. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SphereOfSafetyReplacementEffect())); @@ -79,7 +77,7 @@ class SphereOfSafetyReplacementEffect extends ReplacementEffectImpl { static { filter.add(new ControllerPredicate(TargetController.YOU)); } - private PermanentsOnBattlefieldCount countEnchantments = new PermanentsOnBattlefieldCount(filter); + private final PermanentsOnBattlefieldCount countEnchantments = new PermanentsOnBattlefieldCount(filter); SphereOfSafetyReplacementEffect ( ) { @@ -90,51 +88,46 @@ class SphereOfSafetyReplacementEffect extends ReplacementEffectImpl { SphereOfSafetyReplacementEffect ( SphereOfSafetyReplacementEffect effect ) { super(effect); } - + @Override - public boolean apply(Game game, Ability source) { - throw new UnsupportedOperationException("Not supported."); + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; } @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - if ( event.getType() == GameEvent.EventType.DECLARE_ATTACKER) { - Player player = game.getPlayer(event.getPlayerId()); - if ( player != null ) { - int ce = countEnchantments.calculate(game, source, this); - ManaCostsImpl safetyCosts = new ManaCostsImpl("{"+ ce +"}"); - if ( safetyCosts.canPay(source, source.getSourceId(), event.getPlayerId(), game) && - player.chooseUse(Outcome.Benefit, "Pay {"+ ce +"} to declare attacker?", game) ) - { - if (safetyCosts.payOrRollback(source, game, this.getId(), event.getPlayerId())) { - return false; - } - } - } + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getTargetId().equals(source.getControllerId()) ) { + return true; + } + // planeswalker + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && permanent.getControllerId().equals(source.getControllerId()) + && permanent.getCardType().contains(CardType.PLANESWALKER)) { return true; } return false; } @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if ( event.getType() == GameEvent.EventType.DECLARE_ATTACKER) { - if (event.getTargetId().equals(source.getControllerId()) ) { - return true; - } - // planeswalker - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.getControllerId().equals(source.getControllerId()) - && permanent.getCardType().contains(CardType.PLANESWALKER)) { - return true; + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(event.getPlayerId()); + if ( player != null ) { + int ce = countEnchantments.calculate(game, source, this); + ManaCostsImpl safetyCosts = new ManaCostsImpl("{"+ ce +"}"); + if ( safetyCosts.canPay(source, source.getSourceId(), event.getPlayerId(), game) && + player.chooseUse(Outcome.Benefit, "Pay {"+ ce +"} to declare attacker?", game) ) { + if (safetyCosts.payOrRollback(source, game, this.getId(), event.getPlayerId())) { + return false; + } } + return true; } return false; } - + @Override public SphereOfSafetyReplacementEffect copy() { return new SphereOfSafetyReplacementEffect(this); } -} +} diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/CowedByWisdom.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/CowedByWisdom.java index cb8acbbbf0f..42d10820b90 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/CowedByWisdom.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/CowedByWisdom.java @@ -97,6 +97,17 @@ class CowedByWisdomEffect extends ReplacementEffectImpl { throw new UnsupportedOperationException("Not supported."); } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_ATTACKER || event.getType().equals(GameEvent.EventType.DECLARE_BLOCKER); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent enchantment = game.getPermanent(event.getSourceId()); + return enchantment != null && enchantment.getAttachments().contains(source.getSourceId()); + } + @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player player = game.getPlayer(event.getPlayerId()); @@ -123,20 +134,7 @@ class CowedByWisdomEffect extends ReplacementEffectImpl { } return false; } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType().equals(GameEvent.EventType.DECLARE_ATTACKER)) { - Permanent attacker = game.getPermanent(event.getSourceId()); - return attacker != null && attacker.getAttachments().contains(source.getSourceId()); - } - if (event.getType().equals(GameEvent.EventType.DECLARE_BLOCKER)) { - Permanent blocker = game.getPermanent(event.getSourceId()); - return blocker != null && blocker.getAttachments().contains(source.getSourceId()); - } - return false; - } - + @Override public CowedByWisdomEffect copy() { return new CowedByWisdomEffect(this); diff --git a/Mage.Sets/src/mage/sets/tempest/LightOfDay.java b/Mage.Sets/src/mage/sets/tempest/LightOfDay.java index 54b75d7f44b..a934aa4ae1b 100644 --- a/Mage.Sets/src/mage/sets/tempest/LightOfDay.java +++ b/Mage.Sets/src/mage/sets/tempest/LightOfDay.java @@ -81,33 +81,32 @@ class LightOfDayEffect extends ReplacementEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - return true; + public LightOfDayEffect copy() { + return new LightOfDayEffect(this); } @Override - public LightOfDayEffect copy() { - return new LightOfDayEffect(this); + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.DECLARE_ATTACKER || event.getType() == EventType.DECLARE_BLOCKER; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent != null) { + Player player = game.getPlayer(source.getControllerId()); + if (player.getInRange().contains(permanent.getControllerId())) { + if (permanent.getColor().isBlack()) { + return true; + } + } + } + return false; } @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { return true; } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == EventType.DECLARE_ATTACKER || event.getType() == EventType.DECLARE_BLOCKER) { - Permanent permanent = game.getPermanent(event.getSourceId()); - if (permanent != null) { - Player player = game.getPlayer(source.getControllerId()); - if (player.getInRange().contains(permanent.getControllerId())) { - if (permanent.getColor().isBlack()) { - return true; - } - } - } - } - return false; - } + } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/tempest/Propaganda.java b/Mage.Sets/src/mage/sets/tempest/Propaganda.java index ab30cc45335..ad4fabbee30 100644 --- a/Mage.Sets/src/mage/sets/tempest/Propaganda.java +++ b/Mage.Sets/src/mage/sets/tempest/Propaganda.java @@ -81,8 +81,20 @@ class PropagandaReplacementEffect extends ReplacementEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - throw new UnsupportedOperationException("Not supported."); + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getTargetId().equals(source.getControllerId())) { + Player attackedPlayer = game.getPlayer(event.getTargetId()); + if (attackedPlayer != null) { + // only if a player is attacked. Attacking a planeswalker is free + return true; + } + } + return false; } @Override @@ -101,19 +113,7 @@ class PropagandaReplacementEffect extends ReplacementEffectImpl { } return false; } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if ( event.getType() == GameEvent.EventType.DECLARE_ATTACKER && event.getTargetId().equals(source.getControllerId())) { - Player attackedPlayer = game.getPlayer(event.getTargetId()); - if (attackedPlayer != null) { - // only if a player is attacked. Attacking a planeswalker is free - return true; - } - } - return false; - } - + @Override public PropagandaReplacementEffect copy() { return new PropagandaReplacementEffect(this); diff --git a/Mage.Sets/src/mage/sets/tenth/WindbornMuse.java b/Mage.Sets/src/mage/sets/tenth/WindbornMuse.java index 97a9264059e..2b96f6d07c9 100644 --- a/Mage.Sets/src/mage/sets/tenth/WindbornMuse.java +++ b/Mage.Sets/src/mage/sets/tenth/WindbornMuse.java @@ -51,7 +51,6 @@ public class WindbornMuse extends CardImpl { super(ownerId, 60, "Windborn Muse", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.expansionSetCode = "10E"; this.subtype.add("Spirit"); - this.color.setWhite(true); this.power = new MageInt(2); this.toughness = new MageInt(3); @@ -86,32 +85,13 @@ class WindbornMuseReplacementEffect extends ReplacementEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - throw new UnsupportedOperationException("Not supported."); + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - if ( event.getType() == GameEvent.EventType.DECLARE_ATTACKER) { - Player player = game.getPlayer(event.getPlayerId()); - if ( player != null && event.getTargetId().equals(source.getControllerId())) { - ManaCostsImpl attackTax = new ManaCostsImpl("{2}"); - if ( attackTax.canPay(source, source.getSourceId(), event.getPlayerId(), game) && - player.chooseUse(Outcome.Benefit, "Pay {2} to attack player?", game) ) - { - if (attackTax.payOrRollback(source, game, this.getId(), event.getPlayerId())) { - return false; - } - } - } - return true; - } - return false; - } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if ( event.getType() == GameEvent.EventType.DECLARE_ATTACKER && event.getTargetId().equals(source.getControllerId()) ) { + if (event.getTargetId().equals(source.getControllerId()) ) { Player attackedPlayer = game.getPlayer(event.getTargetId()); if (attackedPlayer != null) { // only if a player is attacked. Attacking a planeswalker is free @@ -120,6 +100,23 @@ class WindbornMuseReplacementEffect extends ReplacementEffectImpl { } return false; } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(event.getPlayerId()); + if ( player != null && event.getTargetId().equals(source.getControllerId())) { + ManaCostsImpl attackTax = new ManaCostsImpl("{2}"); + if ( attackTax.canPay(source, source.getSourceId(), event.getPlayerId(), game) && + player.chooseUse(Outcome.Benefit, "Pay {2} to attack player?", game) ) + { + if (attackTax.payOrRollback(source, game, this.getId(), event.getPlayerId())) { + return false; + } + } + return true; + } + return false; + } @Override public WindbornMuseReplacementEffect copy() { diff --git a/Mage.Sets/src/mage/sets/visions/ElephantGrass.java b/Mage.Sets/src/mage/sets/visions/ElephantGrass.java index 27947e56c6a..c54819decd6 100644 --- a/Mage.Sets/src/mage/sets/visions/ElephantGrass.java +++ b/Mage.Sets/src/mage/sets/visions/ElephantGrass.java @@ -51,8 +51,6 @@ public class ElephantGrass extends CardImpl { super(ownerId, 54, "Elephant Grass", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{G}"); this.expansionSetCode = "VIS"; - this.color.setGreen(true); - // Cumulative upkeep {1} this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl("{1}"))); // Black creatures can't attack you. @@ -85,18 +83,13 @@ class ElephantGrassReplacementEffect extends ReplacementEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARE_ATTACKER; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - if ( event.getType() == GameEvent.EventType.DECLARE_ATTACKER && event.getTargetId().equals(source.getControllerId()) ) { + if (event.getTargetId().equals(source.getControllerId()) ) { Permanent creature = game.getPermanent(event.getSourceId()); if(creature != null && creature.getColor().isBlack()){ return true; @@ -105,6 +98,12 @@ class ElephantGrassReplacementEffect extends ReplacementEffectImpl { return false; } + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override public ElephantGrassReplacementEffect copy() { return new ElephantGrassReplacementEffect(this); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java index 29b8faed422..435c57719db 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java @@ -190,9 +190,13 @@ public class RandomPlayer extends ComputerPlayer { binary.insert(0, "0"); //pad with zeros } for (int i = 0; i < attackersList.size(); i++) { - if (binary.charAt(i) == '1') - game.getCombat().declareAttacker(attackersList.get(i).getId(), defenderId, game); - } + if (binary.charAt(i) == '1') { + setStoredBookmark(game.bookmarkState()); // makes it possible to UNDO a declared attacker with costs from e.g. Propaganda + if (!game.getCombat().declareAttacker(attackersList.get(i).getId(), defenderId, playerId, game)) { + game.undo(playerId); + } + } + } actionCount++; } diff --git a/Mage/src/mage/abilities/keyword/NinjutsuAbility.java b/Mage/src/mage/abilities/keyword/NinjutsuAbility.java index 4896c7ef907..23d23b2aad4 100644 --- a/Mage/src/mage/abilities/keyword/NinjutsuAbility.java +++ b/Mage/src/mage/abilities/keyword/NinjutsuAbility.java @@ -134,7 +134,7 @@ class NinjutsuEffect extends OneShotEffect { } } if (defendingPlayerId != null) { - game.getCombat().declareAttacker(permanent.getId(), defendingPlayerId, game); + game.getCombat().addAttackerToCombat(permanent.getId(), defendingPlayerId, game); permanent.setTapped(true); return true; } diff --git a/Mage/src/mage/game/combat/Combat.java b/Mage/src/mage/game/combat/Combat.java index 172ca2c26ec..e09997e387f 100644 --- a/Mage/src/mage/game/combat/Combat.java +++ b/Mage/src/mage/game/combat/Combat.java @@ -182,6 +182,7 @@ public class Combat implements Serializable, Copyable { /** * Add an additional attacker to the combat (e.g. token of Geist of Saint * Traft) This method doesn't trigger ATTACKER_DECLARED event (as intended). + * If the creature has to be tapped that won't do this method. * * @param creatureId - creature that shall be added to the combat * @param game @@ -190,14 +191,14 @@ public class Combat implements Serializable, Copyable { public boolean addAttackingCreature(UUID creatureId, Game game) { Player player = game.getPlayer(attackerId); if (defenders.size() == 1) { - declareAttacker(creatureId, defenders.iterator().next(), game); + addAttackerToCombat(creatureId, defenders.iterator().next(), game); return true; } else { TargetDefender target = new TargetDefender(defenders, creatureId); target.setRequired(true); player.chooseTarget(Outcome.Damage, target, null, game); if (target.getFirstTarget() != null) { - declareAttacker(creatureId, target.getFirstTarget(), game); + addAttackerToCombat(creatureId, target.getFirstTarget(), game); return true; } } @@ -844,7 +845,20 @@ public class Combat implements Serializable, Copyable { } } - public boolean declareAttacker(UUID attackerId, UUID defenderId, Game game) { + public boolean declareAttacker(UUID attackerId, UUID defenderId, UUID playerId, Game game) { + Permanent attacker = game.getPermanent(attackerId); + if (!attacker.getAbilities().containsKey(VigilanceAbility.getInstance().getId())) { + if (!attacker.isTapped()) { + attacker.tap(game); + } + } + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER, defenderId, attackerId, playerId))) { + return addAttackerToCombat(attackerId, defenderId, game); + } + return false; + } + + public boolean addAttackerToCombat(UUID attackerId, UUID defenderId, Game game) { if (!defenders.contains(defenderId)) { return false; } @@ -853,16 +867,12 @@ public class Combat implements Serializable, Copyable { if (!canDefenderBeAttacked(attackerId, defenderId, game)) { return false; } - CombatGroup newGroup = new CombatGroup(defenderId, defender != null, defender != null ? defender.getControllerId() : defenderId); newGroup.attackers.add(attackerId); Permanent attacker = game.getPermanent(attackerId); - if (!attacker.getAbilities().containsKey(VigilanceAbility.getInstance().getId())) { - attacker.tap(game); - } attacker.setAttacking(true); groups.add(newGroup); - return true; + return true; } public boolean canDefenderBeAttacked(UUID attackerId, UUID defenderId, Game game) { diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index d588c27cfb8..bc4084a77ba 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -1989,8 +1989,8 @@ public abstract class PlayerImpl implements Player, Serializable { } Permanent attacker = game.getPermanent(attackerId); if (attacker != null && attacker.canAttack(defenderId, game) && attacker.getControllerId().equals(playerId)) { - if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER, defenderId, attackerId, playerId))) { - game.getCombat().declareAttacker(attackerId, defenderId, game); + if(!game.getCombat().declareAttacker(attackerId, defenderId, playerId, game)) { + game.undo(playerId); } } }