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); } } }