diff --git a/Mage.Sets/src/mage/cards/b/BerserkersFrenzy.java b/Mage.Sets/src/mage/cards/b/BerserkersFrenzy.java
new file mode 100644
index 00000000000..2dc7617417e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BerserkersFrenzy.java
@@ -0,0 +1,107 @@
+package mage.cards.b;
+
+import mage.abilities.Ability;
+import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.dynamicvalue.common.StaticValue;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.RollDieWithResultTableEffect;
+import mage.abilities.effects.common.combat.BlocksIfAbleTargetEffect;
+import mage.abilities.effects.common.combat.ChooseBlockersEffect;
+import mage.abilities.hint.ConditionHint;
+import mage.abilities.hint.Hint;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
+import mage.constants.*;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetCreaturePermanent;
+import mage.target.targetpointer.FixedTargets;
+import mage.watchers.common.ControlCombatRedundancyWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class BerserkersFrenzy extends CardImpl {
+
+ private static final Hint hint = new ConditionHint(BerserkersFrenzyCondition.instance, "Can be cast");
+
+ public BerserkersFrenzy(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}");
+
+ // Cast this spell only before combat or during combat before blockers are declared.
+ this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(
+ null, null, BerserkersFrenzyCondition.instance,
+ "Cast this spell only before combat or during combat before blockers are declared"
+ ).addHint(hint));
+
+ // Roll two d20 and ignore the lower roll.
+ RollDieWithResultTableEffect effect = new RollDieWithResultTableEffect(
+ 20, "roll two d20 and ignore the lower roll", StaticValue.get(0), 1
+ );
+
+ // 1-14 | Choose any number of creatures. They block this turn if able.
+ effect.addTableEntry(1, 14, new BerserkersFrenzyEffect());
+
+ // 15-20 | You choose which creatures block this turn and how those creatures block.
+ effect.addTableEntry(15, 20, new ChooseBlockersEffect(Duration.EndOfTurn));
+ this.getSpellAbility().addEffect(effect);
+ this.getSpellAbility().addWatcher(new ControlCombatRedundancyWatcher());
+ }
+
+ private BerserkersFrenzy(final BerserkersFrenzy card) {
+ super(card);
+ }
+
+ @Override
+ public BerserkersFrenzy copy() {
+ return new BerserkersFrenzy(this);
+ }
+}
+
+enum BerserkersFrenzyCondition implements Condition {
+ instance;
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ if (game.getPhase().getType() == TurnPhase.COMBAT) {
+ return game.getStep().getType().isBefore(PhaseStep.DECLARE_BLOCKERS);
+ }
+ return !game.getTurn().isDeclareAttackersStepStarted();
+ }
+}
+
+class BerserkersFrenzyEffect extends OneShotEffect {
+
+ BerserkersFrenzyEffect() {
+ super(Outcome.Benefit);
+ staticText = "choose any number of creatures. They block this turn if able";
+ }
+
+ private BerserkersFrenzyEffect(final BerserkersFrenzyEffect effect) {
+ super(effect);
+ }
+
+ @Override
+ public BerserkersFrenzyEffect copy() {
+ return new BerserkersFrenzyEffect(this);
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ Player player = game.getPlayer(source.getControllerId());
+ if (player == null) {
+ return false;
+ }
+ TargetPermanent target = new TargetCreaturePermanent(0, Integer.MAX_VALUE);
+ target.setNotTarget(true);
+ player.choose(outcome, target, source.getSourceId(), game);
+ game.addEffect(new BlocksIfAbleTargetEffect(Duration.EndOfTurn)
+ .setTargetPointer(new FixedTargets(new CardsImpl(target.getTargets()), game)), source);
+ return true;
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/b/BrutalHordechief.java b/Mage.Sets/src/mage/cards/b/BrutalHordechief.java
index 2cf83cfb40b..8b0eb470a61 100644
--- a/Mage.Sets/src/mage/cards/b/BrutalHordechief.java
+++ b/Mage.Sets/src/mage/cards/b/BrutalHordechief.java
@@ -1,17 +1,14 @@
-
package mage.cards.b;
-import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
-import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LoseLifeTargetEffect;
import mage.abilities.effects.common.combat.BlocksIfAbleAllEffect;
+import mage.abilities.effects.common.combat.ChooseBlockersEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
@@ -19,12 +16,12 @@ import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
-import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
-import mage.watchers.common.ChooseBlockersRedundancyWatcher;
+import mage.watchers.common.ControlCombatRedundancyWatcher;
+
+import java.util.UUID;
/**
- *
* @author LevelX2
*/
public final class BrutalHordechief extends CardImpl {
@@ -36,7 +33,7 @@ public final class BrutalHordechief extends CardImpl {
}
public BrutalHordechief(UUID ownerId, CardSetInfo setInfo) {
- super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}");
+ super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
this.subtype.add(SubType.ORC, SubType.WARRIOR);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
@@ -45,10 +42,11 @@ public final class BrutalHordechief extends CardImpl {
this.addAbility(new BrutalHordechiefTriggeredAbility());
// {3}{R/W}{R/W}: Creatures your opponents control block this turn if able, and you choose how those creatures block.
- Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BlocksIfAbleAllEffect(filter, Duration.EndOfTurn), new ManaCostsImpl("{3}{R/W}{R/W}"));
- ability.addEffect(new BrutalHordechiefChooseBlockersEffect());
- ability.addWatcher(new ChooseBlockersRedundancyWatcher());
- ability.addEffect(new ChooseBlockersRedundancyWatcherIncrementEffect());
+ Ability ability = new SimpleActivatedAbility(
+ new BlocksIfAbleAllEffect(filter, Duration.EndOfTurn), new ManaCostsImpl<>("{3}{R/W}{R/W}")
+ );
+ ability.addEffect(new ChooseBlockersEffect(Duration.EndOfTurn).setText("and you choose how those creatures block"));
+ ability.addWatcher(new ControlCombatRedundancyWatcher());
this.addAbility(ability);
}
@@ -60,32 +58,6 @@ public final class BrutalHordechief extends CardImpl {
public BrutalHordechief copy() {
return new BrutalHordechief(this);
}
-
- private class ChooseBlockersRedundancyWatcherIncrementEffect extends OneShotEffect {
-
- ChooseBlockersRedundancyWatcherIncrementEffect() {
- super(Outcome.Neutral);
- }
-
- ChooseBlockersRedundancyWatcherIncrementEffect(final ChooseBlockersRedundancyWatcherIncrementEffect effect) {
- super(effect);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class);
- if (watcher != null) {
- watcher.increment();
- return true;
- }
- return false;
- }
-
- @Override
- public ChooseBlockersRedundancyWatcherIncrementEffect copy() {
- return new ChooseBlockersRedundancyWatcherIncrementEffect(this);
- }
- }
}
class BrutalHordechiefTriggeredAbility extends TriggeredAbilityImpl {
@@ -112,9 +84,9 @@ class BrutalHordechiefTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent source = game.getPermanent(event.getSourceId());
- if (source != null && source.isControlledBy(controllerId)) {
+ if (source != null && source.isControlledBy(getControllerId())) {
UUID defendingPlayerId = game.getCombat().getDefendingPlayerId(event.getSourceId(), game);
- this.getEffects().get(0).setTargetPointer(new FixedTarget(defendingPlayerId));
+ this.getEffects().setTargetPointer(new FixedTarget(defendingPlayerId));
return true;
}
return false;
@@ -125,50 +97,3 @@ class BrutalHordechiefTriggeredAbility extends TriggeredAbilityImpl {
return "Whenever a creature you control attacks, defending player loses 1 life and you gain 1 life.";
}
}
-
-class BrutalHordechiefChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl {
-
- public BrutalHordechiefChooseBlockersEffect() {
- super(Duration.EndOfTurn, Outcome.Benefit, false, false);
- staticText = "You choose which creatures block this turn and how those creatures block";
- }
-
- public BrutalHordechiefChooseBlockersEffect(final BrutalHordechiefChooseBlockersEffect effect) {
- super(effect);
- }
-
- @Override
- public BrutalHordechiefChooseBlockersEffect copy() {
- return new BrutalHordechiefChooseBlockersEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- return false;
- }
-
- @Override
- public boolean checksEventType(GameEvent event, Game game) {
- return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS;
- }
-
- @Override
- public boolean applies(GameEvent event, Ability source, Game game) {
- ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class);
- if(watcher == null){
- return false;
- }
- watcher.decrement();
- if (watcher.copyCountApply > 0) {
- game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply");
- return false;
- }
- watcher.copyCountApply = watcher.copyCount;
- Player blockController = game.getPlayer(source.getControllerId());
- if (blockController != null) {
- game.getCombat().selectBlockers(blockController, source, game);
- return true;
- }
- return false;
- }
-}
diff --git a/Mage.Sets/src/mage/cards/d/DivinersPortent.java b/Mage.Sets/src/mage/cards/d/DivinersPortent.java
index c127f3b36b0..cb8f5a731dc 100644
--- a/Mage.Sets/src/mage/cards/d/DivinersPortent.java
+++ b/Mage.Sets/src/mage/cards/d/DivinersPortent.java
@@ -26,7 +26,7 @@ public final class DivinersPortent extends CardImpl {
// Roll a d20 and add the number of cards in your hand.
RollDieWithResultTableEffect effect = new RollDieWithResultTableEffect(
20, "roll a d20 and add the number " +
- "of cards in your hand", CardsInControllerHandCount.instance
+ "of cards in your hand", CardsInControllerHandCount.instance, 0
);
this.getSpellAbility().addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java
index e739dbb994f..f8451295410 100644
--- a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java
+++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java
@@ -1,20 +1,20 @@
-
package mage.cards.m;
-import java.util.*;
-
import mage.abilities.Ability;
import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility;
import mage.abilities.condition.common.BeforeAttackersAreDeclaredCondition;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
-import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.RequirementEffect;
import mage.abilities.effects.RestrictionEffect;
import mage.abilities.effects.common.combat.AttacksIfAbleTargetEffect;
import mage.abilities.effects.common.combat.CantAttackTargetEffect;
+import mage.abilities.effects.common.combat.ChooseBlockersEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
@@ -23,11 +23,13 @@ import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
-import mage.watchers.Watcher;
-import mage.watchers.common.ChooseBlockersRedundancyWatcher;
+import mage.watchers.common.ControlCombatRedundancyWatcher;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
/**
- *
* @author L_J
*/
public final class MasterWarcraft extends CardImpl {
@@ -36,20 +38,19 @@ public final class MasterWarcraft extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R/W}{R/W}");
// Cast Master Warcraft only before attackers are declared.
- this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, null, BeforeAttackersAreDeclaredCondition.instance, "Cast this spell only before attackers are declared"));
+ this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(
+ null, null, BeforeAttackersAreDeclaredCondition.instance,
+ "Cast this spell only before attackers are declared"
+ ));
// You choose which creatures attack this turn.
this.getSpellAbility().addEffect(new MasterWarcraftChooseAttackersEffect());
// You choose which creatures block this turn and how those creatures block.
- this.getSpellAbility().addEffect(new MasterWarcraftChooseBlockersEffect());
-
+ this.getSpellAbility().addEffect(new ChooseBlockersEffect(Duration.EndOfTurn).concatBy("
"));
// (only the last resolved Master Warcraft spell's effects apply)
- this.getSpellAbility().addWatcher(new MasterWarcraftCastWatcher());
- this.getSpellAbility().addEffect(new MasterWarcraftCastWatcherIncrementEffect());
- this.getSpellAbility().addWatcher(new ChooseBlockersRedundancyWatcher());
- this.getSpellAbility().addEffect(new ChooseBlockersRedundancyWatcherIncrementEffect());
+ this.getSpellAbility().addWatcher(new ControlCombatRedundancyWatcher());
}
private MasterWarcraft(final MasterWarcraft card) {
@@ -60,73 +61,22 @@ public final class MasterWarcraft extends CardImpl {
public MasterWarcraft copy() {
return new MasterWarcraft(this);
}
-
- private class MasterWarcraftCastWatcherIncrementEffect extends OneShotEffect {
-
- MasterWarcraftCastWatcherIncrementEffect() {
- super(Outcome.Neutral);
- }
-
- MasterWarcraftCastWatcherIncrementEffect(final MasterWarcraftCastWatcherIncrementEffect effect) {
- super(effect);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- MasterWarcraftCastWatcher watcher = game.getState().getWatcher(MasterWarcraftCastWatcher.class);
- if (watcher != null) {
- watcher.increment();
- return true;
- }
- return false;
- }
-
- @Override
- public MasterWarcraftCastWatcherIncrementEffect copy() {
- return new MasterWarcraftCastWatcherIncrementEffect(this);
- }
- }
-
- private class ChooseBlockersRedundancyWatcherIncrementEffect extends OneShotEffect {
-
- ChooseBlockersRedundancyWatcherIncrementEffect() {
- super(Outcome.Neutral);
- }
-
- ChooseBlockersRedundancyWatcherIncrementEffect(final ChooseBlockersRedundancyWatcherIncrementEffect effect) {
- super(effect);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class);
- if (watcher != null) {
- watcher.increment();
- return true;
- }
- return false;
- }
-
- @Override
- public ChooseBlockersRedundancyWatcherIncrementEffect copy() {
- return new ChooseBlockersRedundancyWatcherIncrementEffect(this);
- }
- }
}
class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures that will attack this combat (creatures not chosen won't attack this combat)");
+
static {
filter.add(TargetController.ACTIVE.getControllerPredicate());
}
- public MasterWarcraftChooseAttackersEffect() {
+ MasterWarcraftChooseAttackersEffect() {
super(Duration.EndOfTurn, Outcome.Benefit, false, false);
staticText = "You choose which creatures attack this turn";
}
- public MasterWarcraftChooseAttackersEffect(final MasterWarcraftChooseAttackersEffect effect) {
+ private MasterWarcraftChooseAttackersEffect(final MasterWarcraftChooseAttackersEffect effect) {
super(effect);
}
@@ -145,131 +95,54 @@ class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectI
return event.getType() == GameEvent.EventType.DECLARING_ATTACKERS;
}
+ @Override
+ public void init(Ability source, Game game) {
+ super.init(source, game);
+ ControlCombatRedundancyWatcher.addAttackingController(source.getControllerId(), duration, game);
+ }
+
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
- MasterWarcraftCastWatcher watcher = game.getState().getWatcher(MasterWarcraftCastWatcher.class);
- if(watcher == null){
- return false;
- }
- watcher.decrement();
- if (watcher.copyCountApply > 0) {
+ if (!ControlCombatRedundancyWatcher.checkAttackingController(source.getControllerId(), game)) {
game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply");
return false;
}
- watcher.copyCountApply = watcher.copyCount;
Player controller = game.getPlayer(source.getControllerId());
Player attackingPlayer = game.getPlayer(game.getCombat().getAttackingPlayerId());
- if (controller != null && attackingPlayer != null && !attackingPlayer.getAvailableAttackers(game).isEmpty()) {
- Target target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true);
- if (controller.chooseTarget(Outcome.Benefit, target, source, game)) {
- for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) {
-
- // Choose creatures that will be attacking this combat
- if (target.getTargets().contains(permanent.getId())) {
- RequirementEffect effect = new AttacksIfAbleTargetEffect(Duration.EndOfCombat);
- effect.setText("");
- effect.setTargetPointer(new FixedTarget(permanent, game));
- game.addEffect(effect, source);
- game.informPlayers(controller.getLogName() + " has decided that " + permanent.getLogName() + " attacks this combat if able");
-
- // All other creatures can't attack (unless they must attack)
- } else {
- boolean hasToAttack = false;
- for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(permanent, false, game).entrySet()) {
- RequirementEffect effect2 = entry.getKey();
- if (effect2.mustAttack(game)) {
- hasToAttack = true;
- }
- }
- if (!hasToAttack) {
- RestrictionEffect effect = new CantAttackTargetEffect(Duration.EndOfCombat);
- effect.setText("");
- effect.setTargetPointer(new FixedTarget(permanent, game));
- game.addEffect(effect, source);
- }
+ if (controller == null || attackingPlayer == null || attackingPlayer.getAvailableAttackers(game).isEmpty()) {
+ return false; // the attack declaration resumes for the active player as normal
+ }
+ Target target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true);
+ if (!controller.chooseTarget(Outcome.Benefit, target, source, game)) {
+ return false; // the attack declaration resumes for the active player as normal
+ }
+ for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) {
+
+ // Choose creatures that will be attacking this combat
+ if (target.getTargets().contains(permanent.getId())) {
+ RequirementEffect effect = new AttacksIfAbleTargetEffect(Duration.EndOfCombat);
+ effect.setText("");
+ effect.setTargetPointer(new FixedTarget(permanent, game));
+ game.addEffect(effect, source);
+ game.informPlayers(controller.getLogName() + " has decided that " + permanent.getLogName() + " attacks this combat if able");
+
+ // All other creatures can't attack (unless they must attack)
+ } else {
+ boolean hasToAttack = false;
+ for (Map.Entry> entry : game.getContinuousEffects().getApplicableRequirementEffects(permanent, false, game).entrySet()) {
+ RequirementEffect effect2 = entry.getKey();
+ if (effect2.mustAttack(game)) {
+ hasToAttack = true;
}
}
+ if (!hasToAttack) {
+ RestrictionEffect effect = new CantAttackTargetEffect(Duration.EndOfCombat);
+ effect.setText("");
+ effect.setTargetPointer(new FixedTarget(permanent, game));
+ game.addEffect(effect, source);
+ }
}
}
return false; // the attack declaration resumes for the active player as normal
}
}
-
-class MasterWarcraftChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl {
-
- public MasterWarcraftChooseBlockersEffect() {
- super(Duration.EndOfTurn, Outcome.Benefit, false, false);
- staticText = "You choose which creatures block this turn and how those creatures block";
- }
-
- public MasterWarcraftChooseBlockersEffect(final MasterWarcraftChooseBlockersEffect effect) {
- super(effect);
- }
-
- @Override
- public MasterWarcraftChooseBlockersEffect copy() {
- return new MasterWarcraftChooseBlockersEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- return false;
- }
-
- @Override
- public boolean checksEventType(GameEvent event, Game game) {
- return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS;
- }
-
- @Override
- public boolean applies(GameEvent event, Ability source, Game game) {
- ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class);
- if(watcher == null){
- return false;
- }
- watcher.decrement();
- if (watcher.copyCountApply > 0) {
- game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply");
- return false;
- }
- watcher.copyCountApply = watcher.copyCount;
- Player blockController = game.getPlayer(source.getControllerId());
- if (blockController != null) {
- game.getCombat().selectBlockers(blockController, source, game);
- return true;
- }
- return false;
- }
-}
-
-class MasterWarcraftCastWatcher extends Watcher {
-
- public int copyCount = 0;
- public int copyCountApply = 0;
-
- public MasterWarcraftCastWatcher() {
- super(WatcherScope.GAME);
- }
-
-
- @Override
- public void reset() {
- copyCount = 0;
- copyCountApply = 0;
- }
-
- @Override
- public void watch(GameEvent event, Game game) {
- }
-
- public void increment() {
- copyCount++;
- copyCountApply = copyCount;
- }
-
- public void decrement() {
- if (copyCountApply > 0) {
- copyCountApply--;
- }
- }
-}
diff --git a/Mage.Sets/src/mage/cards/m/Melee.java b/Mage.Sets/src/mage/cards/m/Melee.java
index 1d9cca9086f..3ff9968ce8c 100644
--- a/Mage.Sets/src/mage/cards/m/Melee.java
+++ b/Mage.Sets/src/mage/cards/m/Melee.java
@@ -1,6 +1,5 @@
package mage.cards.m;
-import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility;
import mage.abilities.condition.CompoundCondition;
@@ -8,25 +7,23 @@ import mage.abilities.condition.Condition;
import mage.abilities.condition.common.BeforeBlockersAreDeclaredCondition;
import mage.abilities.condition.common.IsPhaseCondition;
import mage.abilities.condition.common.MyTurnCondition;
-import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
-import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.RemoveFromCombatTargetEffect;
import mage.abilities.effects.common.UntapTargetEffect;
+import mage.abilities.effects.common.combat.ChooseBlockersEffect;
import mage.abilities.hint.ConditionHint;
+import mage.abilities.hint.Hint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
-import mage.constants.Outcome;
import mage.constants.TurnPhase;
import mage.game.Game;
import mage.game.combat.CombatGroup;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
-import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
-import mage.watchers.common.ChooseBlockersRedundancyWatcher;
+import mage.watchers.common.ControlCombatRedundancyWatcher;
import java.util.UUID;
@@ -35,21 +32,26 @@ import java.util.UUID;
*/
public final class Melee extends CardImpl {
+ private static final Condition condition = new CompoundCondition(
+ BeforeBlockersAreDeclaredCondition.instance,
+ new IsPhaseCondition(TurnPhase.COMBAT),
+ MyTurnCondition.instance
+ );
+ private static final Hint hint = new ConditionHint(condition, "Can be cast");
+
public Melee(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{R}");
// Cast Melee only during your turn and only during combat before blockers are declared.
- Condition condition = new CompoundCondition(BeforeBlockersAreDeclaredCondition.instance,
- new IsPhaseCondition(TurnPhase.COMBAT),
- MyTurnCondition.instance);
- this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, null, condition, "Cast this spell only during your turn and only during combat before blockers are declared")
- .addHint(new ConditionHint(condition, "Can cast melee (it's combat phase on your turn)")));
+ this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(
+ null, null, condition,
+ "Cast this spell only during your turn and only during combat before blockers are declared"
+ ).addHint(hint));
// You choose which creatures block this combat and how those creatures block.
// (only the last resolved Melee spell's blocking effect applies)
- this.getSpellAbility().addEffect(new MeleeChooseBlockersEffect());
- this.getSpellAbility().addWatcher(new ChooseBlockersRedundancyWatcher());
- this.getSpellAbility().addEffect(new ChooseBlockersRedundancyWatcherIncrementEffect());
+ this.getSpellAbility().addEffect(new ChooseBlockersEffect(Duration.EndOfCombat));
+ this.getSpellAbility().addWatcher(new ControlCombatRedundancyWatcher());
// Whenever a creature attacks and isn't blocked this combat, untap it and remove it from combat.
this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new MeleeTriggeredAbility()));
@@ -63,82 +65,6 @@ public final class Melee extends CardImpl {
public Melee copy() {
return new Melee(this);
}
-
- private class ChooseBlockersRedundancyWatcherIncrementEffect extends OneShotEffect {
-
- ChooseBlockersRedundancyWatcherIncrementEffect() {
- super(Outcome.Neutral);
- }
-
- ChooseBlockersRedundancyWatcherIncrementEffect(final ChooseBlockersRedundancyWatcherIncrementEffect effect) {
- super(effect);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class);
- if (watcher != null) {
- watcher.increment();
- return true;
- }
- return false;
- }
-
- @Override
- public ChooseBlockersRedundancyWatcherIncrementEffect copy() {
- return new ChooseBlockersRedundancyWatcherIncrementEffect(this);
- }
- }
-}
-
-class MeleeChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl {
-
- public MeleeChooseBlockersEffect() {
- super(Duration.EndOfCombat, Outcome.Benefit, false, false);
- staticText = "You choose which creatures block this combat and how those creatures block";
- }
-
- public MeleeChooseBlockersEffect(final MeleeChooseBlockersEffect effect) {
- super(effect);
- }
-
- @Override
- public MeleeChooseBlockersEffect copy() {
- return new MeleeChooseBlockersEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- return false;
- }
-
- @Override
- public boolean checksEventType(GameEvent event, Game game) {
- return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS;
- }
-
- @Override
- public boolean applies(GameEvent event, Ability source, Game game) {
- ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class);
- if (watcher == null) {
- return false;
- }
- watcher.decrement();
- watcher.copyCount--;
- if (watcher.copyCountApply > 0) {
- game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply");
- this.discard();
- return false;
- }
- watcher.copyCountApply = watcher.copyCount;
- Player blockController = game.getPlayer(source.getControllerId());
- if (blockController != null) {
- game.getCombat().selectBlockers(blockController, source, game);
- return true;
- }
- this.discard();
- return false;
- }
}
class MeleeTriggeredAbility extends DelayedTriggeredAbility {
diff --git a/Mage.Sets/src/mage/cards/o/OdricMasterTactician.java b/Mage.Sets/src/mage/cards/o/OdricMasterTactician.java
index 4128e6cc6db..cecbd547efe 100644
--- a/Mage.Sets/src/mage/cards/o/OdricMasterTactician.java
+++ b/Mage.Sets/src/mage/cards/o/OdricMasterTactician.java
@@ -1,19 +1,17 @@
package mage.cards.o;
-import java.util.UUID;
import mage.MageInt;
-import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
-import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
-import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.combat.ChooseBlockersEffect;
import mage.abilities.keyword.FirstStrikeAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
-import mage.players.Player;
-import mage.watchers.common.ChooseBlockersRedundancyWatcher;
+import mage.watchers.common.ControlCombatRedundancyWatcher;
+
+import java.util.UUID;
/**
* @author noxx
@@ -49,9 +47,8 @@ public final class OdricMasterTactician extends CardImpl {
class OdricMasterTacticianTriggeredAbility extends TriggeredAbilityImpl {
public OdricMasterTacticianTriggeredAbility() {
- super(Zone.BATTLEFIELD, new OdricMasterTacticianChooseBlockersEffect());
- this.addWatcher(new ChooseBlockersRedundancyWatcher());
- this.addEffect(new ChooseBlockersRedundancyWatcherIncrementEffect());
+ super(Zone.BATTLEFIELD, new ChooseBlockersEffect(Duration.EndOfCombat));
+ this.addWatcher(new ControlCombatRedundancyWatcher());
}
public OdricMasterTacticianTriggeredAbility(final OdricMasterTacticianTriggeredAbility ability) {
@@ -72,80 +69,4 @@ class OdricMasterTacticianTriggeredAbility extends TriggeredAbilityImpl {
public boolean checkTrigger(GameEvent event, Game game) {
return game.getCombat().getAttackers().size() >= 4 && game.getCombat().getAttackers().contains(this.sourceId);
}
-
- private class ChooseBlockersRedundancyWatcherIncrementEffect extends OneShotEffect {
-
- ChooseBlockersRedundancyWatcherIncrementEffect() {
- super(Outcome.Neutral);
- }
-
- ChooseBlockersRedundancyWatcherIncrementEffect(final ChooseBlockersRedundancyWatcherIncrementEffect effect) {
- super(effect);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class);
- if (watcher != null) {
- watcher.increment();
- return true;
- }
- return false;
- }
-
- @Override
- public ChooseBlockersRedundancyWatcherIncrementEffect copy() {
- return new ChooseBlockersRedundancyWatcherIncrementEffect(this);
- }
- }
-}
-
-class OdricMasterTacticianChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl {
-
- public OdricMasterTacticianChooseBlockersEffect() {
- super(Duration.EndOfCombat, Outcome.Benefit, false, false);
- staticText = "Whenever {this} and at least three other creatures attack, you choose which creatures block this combat and how those creatures block";
- }
-
- public OdricMasterTacticianChooseBlockersEffect(final OdricMasterTacticianChooseBlockersEffect effect) {
- super(effect);
- }
-
- @Override
- public OdricMasterTacticianChooseBlockersEffect copy() {
- return new OdricMasterTacticianChooseBlockersEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- return false;
- }
-
- @Override
- public boolean checksEventType(GameEvent event, Game game) {
- return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS;
- }
-
- @Override
- public boolean applies(GameEvent event, Ability source, Game game) {
- ChooseBlockersRedundancyWatcher watcher = game.getState().getWatcher(ChooseBlockersRedundancyWatcher.class);
- if (watcher == null) {
- return false;
- }
- watcher.decrement();
- watcher.copyCount--;
- if (watcher.copyCountApply > 0) {
- game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply");
- this.discard();
- return false;
- }
- watcher.copyCountApply = watcher.copyCount;
- Player blockController = game.getPlayer(source.getControllerId());
- if (blockController != null) {
- game.getCombat().selectBlockers(blockController, source, game);
- return true;
- }
- this.discard();
- return false;
- }
}
diff --git a/Mage.Sets/src/mage/cards/r/Revivify.java b/Mage.Sets/src/mage/cards/r/Revivify.java
index 690999b9807..7fbf9505243 100644
--- a/Mage.Sets/src/mage/cards/r/Revivify.java
+++ b/Mage.Sets/src/mage/cards/r/Revivify.java
@@ -41,7 +41,7 @@ public final class Revivify extends CardImpl {
// Roll a d20 and add the number of creature cards in your graveyard that were put there from the battlefield this turn.
RollDieWithResultTableEffect effect = new RollDieWithResultTableEffect(
20, "roll a d20 and add the number of creature cards " +
- "in your graveyard that were put there from the battlefield this turn", xValue
+ "in your graveyard that were put there from the battlefield this turn", xValue, 0
);
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addWatcher(new CardsPutIntoGraveyardWatcher());
diff --git a/Mage.Sets/src/mage/sets/ForgottenRealmsCommander.java b/Mage.Sets/src/mage/sets/ForgottenRealmsCommander.java
index d75528bf51b..df24b0f0bb4 100644
--- a/Mage.Sets/src/mage/sets/ForgottenRealmsCommander.java
+++ b/Mage.Sets/src/mage/sets/ForgottenRealmsCommander.java
@@ -42,6 +42,7 @@ public final class ForgottenRealmsCommander extends ExpansionSet {
cards.add(new SetCardInfo("Bedevil", 179, Rarity.RARE, mage.cards.b.Bedevil.class));
cards.add(new SetCardInfo("Behemoth Sledge", 180, Rarity.UNCOMMON, mage.cards.b.BehemothSledge.class));
cards.add(new SetCardInfo("Belt of Giant Strength", 38, Rarity.RARE, mage.cards.b.BeltOfGiantStrength.class));
+ cards.add(new SetCardInfo("Berserker's Frenzy", 29, Rarity.RARE, mage.cards.b.BerserkersFrenzy.class));
cards.add(new SetCardInfo("Bituminous Blast", 181, Rarity.UNCOMMON, mage.cards.b.BituminousBlast.class));
cards.add(new SetCardInfo("Bogardan Hellkite", 115, Rarity.MYTHIC, mage.cards.b.BogardanHellkite.class));
cards.add(new SetCardInfo("Bojuka Bog", 226, Rarity.COMMON, mage.cards.b.BojukaBog.class));
diff --git a/Mage/src/main/java/mage/abilities/effects/common/RollDieWithResultTableEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RollDieWithResultTableEffect.java
index b198f7daf85..1e65c1f8d08 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/RollDieWithResultTableEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/RollDieWithResultTableEffect.java
@@ -27,6 +27,7 @@ public class RollDieWithResultTableEffect extends OneShotEffect {
private final String prefixText;
private final List resultsTable = new ArrayList<>();
private final DynamicValue modifier;
+ private final int toIgnore;
public RollDieWithResultTableEffect() {
this(20);
@@ -37,14 +38,15 @@ public class RollDieWithResultTableEffect extends OneShotEffect {
}
public RollDieWithResultTableEffect(int sides, String prefixText) {
- this(sides, prefixText, StaticValue.get(0));
+ this(sides, prefixText, StaticValue.get(0), 0);
}
- public RollDieWithResultTableEffect(int sides, String prefixText, DynamicValue modifier) {
+ public RollDieWithResultTableEffect(int sides, String prefixText, DynamicValue modifier, int toIgnore) {
super(Outcome.Benefit);
this.sides = sides;
this.prefixText = prefixText;
this.modifier = modifier;
+ this.toIgnore = toIgnore;
}
protected RollDieWithResultTableEffect(final RollDieWithResultTableEffect effect) {
@@ -55,6 +57,7 @@ public class RollDieWithResultTableEffect extends OneShotEffect {
this.resultsTable.add(tableEntry.copy());
}
this.modifier = effect.modifier.copy();
+ this.toIgnore = effect.toIgnore;
}
@Override
@@ -68,7 +71,9 @@ public class RollDieWithResultTableEffect extends OneShotEffect {
if (player == null) {
return false;
}
- int result = player.rollDice(outcome, source, game, sides) + modifier.calculate(game, source, this);
+ int result = player.rollDice(
+ outcome, source, game, sides, 1 + toIgnore, toIgnore
+ ).get(0) + modifier.calculate(game, source, this);
this.applyResult(result, game, source);
return true;
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/ChooseBlockersEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/ChooseBlockersEffect.java
new file mode 100644
index 00000000000..07d1dfee53b
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/combat/ChooseBlockersEffect.java
@@ -0,0 +1,80 @@
+package mage.abilities.effects.common.combat;
+
+import mage.abilities.Ability;
+import mage.abilities.Mode;
+import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.players.Player;
+import mage.watchers.common.ControlCombatRedundancyWatcher;
+
+/**
+ * @author L_J, TheElk801
+ */
+public class ChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl {
+
+ public ChooseBlockersEffect(Duration duration) {
+ super(duration, Outcome.Benefit, false, false);
+ }
+
+ private ChooseBlockersEffect(final ChooseBlockersEffect effect) {
+ super(effect);
+ }
+
+ @Override
+ public ChooseBlockersEffect copy() {
+ return new ChooseBlockersEffect(this);
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ return false;
+ }
+
+ @Override
+ public boolean checksEventType(GameEvent event, Game game) {
+ return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS;
+ }
+
+ @Override
+ public void init(Ability source, Game game) {
+ super.init(source, game);
+ ControlCombatRedundancyWatcher.addBlockingController(source.getControllerId(), this.duration, game);
+ }
+
+ @Override
+ public boolean applies(GameEvent event, Ability source, Game game) {
+ if (!ControlCombatRedundancyWatcher.checkBlockingController(source.getControllerId(), game)) {
+ game.informPlayers(source.getSourceObject(game).getIdName() + " didn't apply");
+ return false;
+ }
+ Player blockController = game.getPlayer(source.getControllerId());
+ if (blockController != null) {
+ game.getCombat().selectBlockers(blockController, source, game);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String getText(Mode mode) {
+ if (staticText != null && !staticText.isEmpty()) {
+ return staticText;
+ }
+ StringBuilder sb = new StringBuilder("you choose which creatures block this ");
+ switch (duration) {
+ case EndOfTurn:
+ sb.append("turn");
+ break;
+ case EndOfCombat:
+ sb.append("combat");
+ break;
+ default:
+ throw new IllegalArgumentException("duration type not supported");
+ }
+ sb.append(" and how those creatures block");
+ return sb.toString();
+ }
+}
diff --git a/Mage/src/main/java/mage/watchers/common/ChooseBlockersRedundancyWatcher.java b/Mage/src/main/java/mage/watchers/common/ChooseBlockersRedundancyWatcher.java
deleted file mode 100644
index 0b9660699c2..00000000000
--- a/Mage/src/main/java/mage/watchers/common/ChooseBlockersRedundancyWatcher.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package mage.watchers.common;
-
-import mage.constants.WatcherScope;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.watchers.Watcher;
-
-/**
- * @author L_J
- */
-
-public class ChooseBlockersRedundancyWatcher extends Watcher { // workaround for solving timestamp issues regarding "you choose which creatures block and how those creatures block" effects
-
- public int copyCount = 0;
- public int copyCountApply = 0;
-
- public ChooseBlockersRedundancyWatcher() {
- super(WatcherScope.GAME);
- }
-
- @Override
- public void reset() {
- super.reset();
- copyCount = 0;
- copyCountApply = 0;
- }
-
- @Override
- public void watch(GameEvent event, Game game) {
- }
-
- public void increment() {
- copyCount++;
- copyCountApply = copyCount;
- }
-
- public void decrement() {
- if (copyCountApply > 0) {
- copyCountApply--;
- }
- }
-}
diff --git a/Mage/src/main/java/mage/watchers/common/ControlCombatRedundancyWatcher.java b/Mage/src/main/java/mage/watchers/common/ControlCombatRedundancyWatcher.java
new file mode 100644
index 00000000000..d7f75a965c1
--- /dev/null
+++ b/Mage/src/main/java/mage/watchers/common/ControlCombatRedundancyWatcher.java
@@ -0,0 +1,78 @@
+package mage.watchers.common;
+
+import mage.constants.Duration;
+import mage.constants.WatcherScope;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.watchers.Watcher;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * @author L_J
+ */
+public class ControlCombatRedundancyWatcher extends Watcher { // workaround for solving timestamp issues regarding "you choose which creatures block and how those creatures block" effects
+
+ private static final class PlayerDuration {
+
+ private final Duration duration;
+ private final UUID playerId;
+
+ private PlayerDuration(Duration duration, UUID playerId) {
+ this.duration = duration;
+ this.playerId = playerId;
+ }
+
+ private boolean isCombat() {
+ return duration == Duration.EndOfCombat;
+ }
+
+ private boolean isPlayer(UUID playerId) {
+ return playerId.equals(this.playerId);
+ }
+ }
+
+ private final List attackingControllers = new ArrayList<>();
+ private final List blockingControllers = new ArrayList<>();
+
+ public ControlCombatRedundancyWatcher() {
+ super(WatcherScope.GAME);
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ attackingControllers.clear();
+ blockingControllers.clear();
+ }
+
+ @Override
+ public void watch(GameEvent event, Game game) {
+ if (event.getType() == GameEvent.EventType.END_COMBAT_STEP_POST) {
+ attackingControllers.removeIf(PlayerDuration::isCombat);
+ blockingControllers.removeIf(PlayerDuration::isCombat);
+ }
+ }
+
+ public static void addAttackingController(UUID playerId, Duration duration, Game game) {
+ ControlCombatRedundancyWatcher watcher = game.getState().getWatcher(ControlCombatRedundancyWatcher.class);
+ watcher.attackingControllers.add(0, new PlayerDuration(duration, playerId));
+ }
+
+ public static void addBlockingController(UUID playerId, Duration duration, Game game) {
+ ControlCombatRedundancyWatcher watcher = game.getState().getWatcher(ControlCombatRedundancyWatcher.class);
+ watcher.blockingControllers.add(0, new PlayerDuration(duration, playerId));
+ }
+
+ public static boolean checkAttackingController(UUID playerId, Game game) {
+ ControlCombatRedundancyWatcher watcher = game.getState().getWatcher(ControlCombatRedundancyWatcher.class);
+ return !watcher.attackingControllers.isEmpty() && watcher.attackingControllers.get(0).isPlayer(playerId);
+ }
+
+ public static boolean checkBlockingController(UUID playerId, Game game) {
+ ControlCombatRedundancyWatcher watcher = game.getState().getWatcher(ControlCombatRedundancyWatcher.class);
+ return !watcher.blockingControllers.isEmpty() && watcher.blockingControllers.get(0).isPlayer(playerId);
+ }
+}