diff --git a/Mage.Client/src/main/java/mage/client/components/ext/dlg/DialogContainer.java b/Mage.Client/src/main/java/mage/client/components/ext/dlg/DialogContainer.java index 3a5a36f2da..39845bae64 100644 --- a/Mage.Client/src/main/java/mage/client/components/ext/dlg/DialogContainer.java +++ b/Mage.Client/src/main/java/mage/client/components/ext/dlg/DialogContainer.java @@ -112,7 +112,7 @@ public class DialogContainer extends JPanel { backgroundColor = new Color(0, 0, 50, 110); alpha = 0; - ChoiceDialog dlg = new ChoiceDialog(params, "Command Zone (Commander and Emblems)"); + ChoiceDialog dlg = new ChoiceDialog(params, "Command Zone (Commander, Emblems and Planes)"); add(dlg); dlg.setLocation(X_OFFSET + 10, Y_OFFSET + 10); dlg.updateSize(params.rect.width - 80, params.rect.height - 80); diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index 5a0d5a5b58..898915e681 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -46,7 +46,6 @@ import mage.game.Game; import mage.game.command.Emblem; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; -import mage.game.permanent.token.TokenImpl; import mage.game.permanent.token.Token; import mage.game.stack.Spell; import mage.game.stack.StackAbility; @@ -55,6 +54,7 @@ import mage.target.Targets; import mage.util.SubTypeList; import com.google.gson.annotations.Expose; +import mage.game.command.Plane; /** * @author BetaSteward_at_googlemail.com @@ -521,6 +521,13 @@ public class CardView extends SimpleCardView { Emblem emblem = (Emblem) object; this.rarity = Rarity.SPECIAL; this.rules = emblem.getAbilities().getRules(emblem.getName()); + } else if (object instanceof Plane) { + this.mageObjectType = MageObjectType.PLANE; + Plane plane = (Plane) object; + this.rarity = Rarity.SPECIAL; + // Display in landscape/rotated/on its side + this.rotate = true; + this.rules = plane.getAbilities().getRules(plane.getName()); } if (this.rarity == null && object instanceof StackAbility) { StackAbility stackAbility = (StackAbility) object; @@ -557,6 +564,21 @@ public class CardView extends SimpleCardView { this.rarity = Rarity.COMMON; } + public CardView(PlaneView plane) { + this(true); + this.gameObject = true; + this.id = plane.getId(); + this.mageObjectType = MageObjectType.EMBLEM; + this.name = plane.getName(); + this.displayName = name; + this.rules = plane.getRules(); + // Display the plane in landscape (similar to Fused cards) + this.rotate = true; + this.frameStyle = FrameStyle.MPRP_FULL_ART_BASIC; + this.expansionSetCode = plane.getExpansionSetCode(); + this.rarity = Rarity.COMMON; + } + public CardView(Designation designation, StackAbility stackAbility) { this(true); this.gameObject = true; diff --git a/Mage.Common/src/main/java/mage/view/CardsView.java b/Mage.Common/src/main/java/mage/view/CardsView.java index e37b9801a6..a9c2792716 100644 --- a/Mage.Common/src/main/java/mage/view/CardsView.java +++ b/Mage.Common/src/main/java/mage/view/CardsView.java @@ -40,6 +40,7 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.GameState; import mage.game.command.Emblem; +import mage.game.command.Plane; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; import mage.target.targetpointer.TargetPointer; @@ -118,6 +119,9 @@ public class CardsView extends LinkedHashMap { abilityView = new AbilityView(ability, sourceObject.getName(), new CardView(new EmblemView((Emblem) sourceObject))); abilityView.setName(((Emblem) sourceObject).getName()); // abilityView.setExpansionSetCode(sourceCard.getExpansionSetCode()); + } else if (sourceObject instanceof Plane) { + abilityView = new AbilityView(ability, sourceObject.getName(), new CardView(new PlaneView((Plane) sourceObject))); + abilityView.setName(((Plane) sourceObject).getName()); } break; } diff --git a/Mage.Common/src/main/java/mage/view/GameView.java b/Mage.Common/src/main/java/mage/view/GameView.java index 6dc733112f..6ac1ff99ac 100644 --- a/Mage.Common/src/main/java/mage/view/GameView.java +++ b/Mage.Common/src/main/java/mage/view/GameView.java @@ -47,6 +47,7 @@ import mage.game.Game; import mage.game.GameState; import mage.game.combat.CombatGroup; import mage.game.command.Emblem; +import mage.game.command.Plane; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentToken; @@ -139,6 +140,12 @@ public class GameView implements Serializable { stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, object.getName(), cardView)); checkPaid(stackObject.getId(), ((StackAbility) stackObject)); + } else if (object instanceof Plane) { + CardView cardView = new CardView(new PlaneView((Plane) object)); + ((StackAbility) stackObject).setName(((Plane) object).getName()); + stack.put(stackObject.getId(), + new StackAbilityView(game, (StackAbility) stackObject, object.getName(), cardView)); + checkPaid(stackObject.getId(), ((StackAbility) stackObject)); } else if (object instanceof Designation) { Designation designation = (Designation) game.getObject(object.getId()); if (designation != null) { diff --git a/Mage.Common/src/main/java/mage/view/PlaneView.java b/Mage.Common/src/main/java/mage/view/PlaneView.java new file mode 100644 index 0000000000..7b1eea1dfe --- /dev/null +++ b/Mage.Common/src/main/java/mage/view/PlaneView.java @@ -0,0 +1,57 @@ +package mage.view; + +import java.io.Serializable; +import java.util.List; +import java.util.UUID; +import mage.cards.Card; +import mage.game.command.Plane; + +/** + * @author spjspj + */ +public class PlaneView implements CommandObjectView, Serializable { + + protected UUID id; + protected String name; + protected String expansionSetCode; + protected List rules; + + public PlaneView(Plane plane, Card sourceCard) { + id = plane.getId(); + name = "Plane " + sourceCard.getName(); + if (plane.getExpansionSetCodeForImage() == null) { + expansionSetCode = sourceCard.getExpansionSetCode(); + } else { + expansionSetCode = plane.getExpansionSetCodeForImage(); + } + + rules = plane.getAbilities().getRules(sourceCard.getName()); + } + + public PlaneView(Plane plane) { + id = plane.getId(); + name = plane.getName(); + expansionSetCode = plane.getExpansionSetCodeForImage(); + rules = plane.getAbilities().getRules(plane.getName()); + } + + @Override + public String getExpansionSetCode() { + return expansionSetCode; + } + + @Override + public String getName() { + return name; + } + + @Override + public UUID getId() { + return id; + } + + @Override + public List getRules() { + return rules; + } +} diff --git a/Mage.Common/src/main/java/mage/view/PlayerView.java b/Mage.Common/src/main/java/mage/view/PlayerView.java index 1dcb1f796a..4e4bdc3d22 100644 --- a/Mage.Common/src/main/java/mage/view/PlayerView.java +++ b/Mage.Common/src/main/java/mage/view/PlayerView.java @@ -43,6 +43,7 @@ import mage.game.GameState; import mage.game.command.CommandObject; import mage.game.command.Commander; import mage.game.command.Emblem; +import mage.game.command.Plane; import mage.game.permanent.Permanent; import mage.players.Player; import mage.players.net.UserData; @@ -141,6 +142,10 @@ public class PlayerView implements Serializable { if (emblem.getControllerId().equals(this.playerId)) { commandList.add(new EmblemView(emblem)); } + } else if (commandObject instanceof Plane) { + Plane plane = (Plane) commandObject; + // Planes are universal and all players can see them. + commandList.add(new PlaneView(plane)); } else if (commandObject instanceof Commander) { Commander commander = (Commander) commandObject; if (commander.getControllerId().equals(this.playerId)) { diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 41d0c95c6d..2d8246bd67 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -2379,4 +2379,19 @@ public class TestPlayer implements Player { return computerPlayer.getHistory(); } + @Override + public PlanarDieRoll rollPlanarDie(Game game) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public PlanarDieRoll rollPlanarDie(Game game, ArrayList appliedEffects) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public PlanarDieRoll rollPlanarDie(Game game, ArrayList appliedEffects, int numberChaosSides, int numberPlanarSides) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java index 3caaeedf53..a3eaa5a5cb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java +++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java @@ -1268,4 +1268,19 @@ public class PlayerStub implements Player { return null; } + @Override + public PlanarDieRoll rollPlanarDie(Game game) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public PlanarDieRoll rollPlanarDie(Game game, ArrayList appliedEffects) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public PlanarDieRoll rollPlanarDie(Game game, ArrayList appliedEffects, int numberChaosSides, int numberPlanarSides) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 389ac3c450..ef1c862ead 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -63,6 +63,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.UUID; +import mage.game.command.Plane; /** * @author BetaSteward_at_googlemail.com @@ -621,9 +622,9 @@ public abstract class AbilityImpl implements Ability { @Override public void setControllerId(UUID controllerId) { this.controllerId = controllerId; - for (Watcher watcher : watchers) { - watcher.setControllerId(controllerId); - } + for (Watcher watcher : watchers) { + watcher.setControllerId(controllerId); + } if (subAbilities != null) { for (Ability subAbility : subAbilities) { @@ -649,9 +650,9 @@ public abstract class AbilityImpl implements Ability { subAbility.setSourceId(sourceId); } } - for (Watcher watcher : watchers) { - watcher.setSourceId(sourceId); - } + for (Watcher watcher : watchers) { + watcher.setSourceId(sourceId); + } } @@ -923,8 +924,8 @@ public abstract class AbilityImpl implements Ability { return true; } MageObject object = game.getObject(this.getSourceId()); - // emblem are always actual - if (object != null && object instanceof Emblem) { + // emblem/planes are always actual + if (object != null && (object instanceof Emblem || object instanceof Plane)) { return true; } } diff --git a/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java b/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java index 0c63d4595b..6ffde97b56 100644 --- a/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/ActivatedAbilityImpl.java @@ -45,6 +45,7 @@ import mage.constants.TimingRule; import mage.constants.Zone; import mage.game.Game; import mage.game.command.Emblem; +import mage.game.command.Plane; import mage.game.permanent.Permanent; import mage.util.CardUtil; @@ -240,10 +241,10 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa MageObject mageObject = game.getObject(this.sourceId); if (mageObject instanceof Emblem) { return ((Emblem) mageObject).getControllerId().equals(playerId); - } else { - if (game.getState().getZone(this.sourceId) != Zone.BATTLEFIELD) { - return ((Card) mageObject).getOwnerId().equals(playerId); - } + } else if (mageObject instanceof Plane) { + return ((Plane) mageObject).getControllerId().equals(playerId); + } else if (game.getState().getZone(this.sourceId) != Zone.BATTLEFIELD) { + return ((Card) mageObject).getOwnerId().equals(playerId); } } return false; diff --git a/Mage/src/main/java/mage/abilities/condition/common/MainPhaseStackEmptyCondition.java b/Mage/src/main/java/mage/abilities/condition/common/MainPhaseStackEmptyCondition.java new file mode 100644 index 0000000000..7973ffd2f4 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/MainPhaseStackEmptyCondition.java @@ -0,0 +1,56 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.condition.common; + +import java.util.EnumSet; +import java.util.Set; + +import mage.constants.TurnPhase; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; + +/** + * @author spjspj + */ +public enum MainPhaseStackEmptyCondition implements Condition { + + instance; + private static final Set turnPhases = EnumSet.of(TurnPhase.PRECOMBAT_MAIN, TurnPhase.POSTCOMBAT_MAIN); + + @Override + public boolean apply(Game game, Ability source) { + return game.getStack().isEmpty() + && turnPhases.contains(game.getTurn().getPhase().getType()); + } + + @Override + public String toString() { + return "during the main phase and the stack is empty"; + } +} diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java index 5a22141369..1ce5392da3 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java @@ -104,6 +104,9 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { + if (condition == null && baseCondition != null) { + condition = baseCondition; + } boolean conditionState = condition.apply(game, source); if (conditionState) { effect.setTargetPointer(this.targetPointer); diff --git a/Mage/src/main/java/mage/abilities/effects/common/RollPlanarDieEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RollPlanarDieEffect.java new file mode 100644 index 0000000000..940df87aae --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/RollPlanarDieEffect.java @@ -0,0 +1,187 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.effects.common; + +import java.util.List; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.PlanarDieRoll; +import mage.constants.Planes; +import mage.game.Game; +import mage.game.command.CommandObject; +import mage.game.command.Plane; +import mage.players.Player; +import mage.target.Target; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author spjspj + */ +public class RollPlanarDieEffect extends OneShotEffect { + + private static final Logger log = Logger.getLogger("Roll Planar Die"); + + protected List chaosEffects = null; + protected List chaosTargets = null; + + public RollPlanarDieEffect(List chaosEffects, List chaosTargets) { + this(chaosEffects, chaosTargets, Outcome.Neutral); + } + + public RollPlanarDieEffect(List chaosEffects, List chaosTargets, Outcome outcome) { + super(outcome); + addChaosEffects(chaosEffects); + addChaosTargets(chaosTargets); + } + + public RollPlanarDieEffect(final RollPlanarDieEffect effect) { + super(effect); + this.chaosEffects = effect.chaosEffects.stream().collect(Collectors.toList()); + this.chaosTargets = effect.chaosTargets.stream().collect(Collectors.toList()); + } + + public void addChaosEffects(List chaosEffects) { + if (chaosEffects != null) { + this.chaosEffects = chaosEffects; + } + } + + public void addChaosTargets(List chaosTargets) { + if (chaosTargets != null) { + this.chaosTargets = chaosTargets; + } + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getObject(source.getSourceId()); + if (controller != null && mageObject != null) { + PlanarDieRoll planarRoll = controller.rollPlanarDie(game); + if (planarRoll == PlanarDieRoll.CHAOS_ROLL && chaosEffects != null && chaosTargets != null) { + for (int i = 0; i < chaosTargets.size(); i++) { + Target target = chaosTargets.get(i); + if (target != null) { + target.clearChosen(); + } + } + + for (int i = 0; i < chaosEffects.size(); i++) { + Effect effect = chaosEffects.get(i); + Target target = null; + if (chaosTargets != null && chaosTargets.size() > i) { + target = chaosTargets.get(i); + } + boolean done = false; + while (controller.canRespond() && effect != null && !done) { + if (target != null && !target.isChosen() && target.canChoose(controller.getId(), game)) { + controller.chooseTarget(Outcome.Benefit, target, source, game); + source.addTarget(target); + } + if (target != null) { + effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); + } + try { + effect.apply(game, source); + } catch (UnsupportedOperationException exception) { + } + if (effect instanceof ContinuousEffect) { + game.addEffect((ContinuousEffect) effect, source); + } + done = true; + } + } + } else if (planarRoll == PlanarDieRoll.PLANAR_ROLL) { + // Steps: 1) Remove the last plane and set its effects to discarded + for (CommandObject cobject : game.getState().getCommand()) { + if (cobject instanceof Plane) { + game.getState().addSeenPlane((Plane) cobject, game, id); + if (((Plane) cobject).getAbilities() != null) { + for (Ability ability : ((Plane) cobject).getAbilities()) { + for (Effect effect : ability.getEffects()) { + if (effect instanceof ContinuousEffect) { + ((ContinuousEffect) effect).discard(); + } + } + } + } + game.getState().removeTriggersOfSourceId(((Plane) cobject).getId()); + game.getState().getCommand().remove(cobject); + break; + } + } + + // 2) Choose a new random plane we haven't been to, or reset if we've been everywhere + List planesVisited = game.getState().getSeenPlanes(); + if (game.getState().getSeenPlanes() != null) { + if (planesVisited.size() == Planes.values().length) { + game.getState().resetSeenPlanes(); + } + } + + boolean foundNextPlane = false; + while (!foundNextPlane) { + Plane plane = Plane.getRandomPlane(); + try { + if (plane != null && !planesVisited.contains(plane.getName())) { + foundNextPlane = true; + plane.setControllerId(controller.getId()); + game.addPlane(plane, null, controller.getId()); + game.informPlayers("You have planeswalked to " + plane.getLogName()); + } + } catch (Exception ex) { + } + } + } + return true; + } + return false; + } + + @Override + public String getText(Mode mode) { + if (!staticText.isEmpty()) { + return staticText; + } + StringBuilder sb = new StringBuilder("Roll the planar die"); + return sb.toString(); + } + + @Override + public RollPlanarDieEffect copy() { + return new RollPlanarDieEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayAdditionalLandsAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayAdditionalLandsAllEffect.java index ed0eb8167f..e3dc924b3c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayAdditionalLandsAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/PlayAdditionalLandsAllEffect.java @@ -24,8 +24,7 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ - + */ package mage.abilities.effects.common.continuous; import mage.abilities.Ability; @@ -44,13 +43,28 @@ import mage.players.Player; */ public class PlayAdditionalLandsAllEffect extends ContinuousEffectImpl { + private int numExtraLands = 1; + public PlayAdditionalLandsAllEffect() { super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit); staticText = "Each player may play an additional land on each of their turns"; + numExtraLands = 1; + } + + public PlayAdditionalLandsAllEffect(int numExtraLands) { + super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit); + this.numExtraLands = numExtraLands; + if (numExtraLands == Integer.MAX_VALUE) { + staticText = "Each player may play any number of additional lands on each of their turns"; + } else { + staticText = "Each player may play an additional " + numExtraLands + " lands on each of their turns"; + } } public PlayAdditionalLandsAllEffect(final PlayAdditionalLandsAllEffect effect) { super(effect); + this.numExtraLands = effect.numExtraLands; + this.staticText = effect.staticText; } @Override @@ -62,10 +76,13 @@ public class PlayAdditionalLandsAllEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(game.getActivePlayerId()); if (player != null) { - player.setLandsPerTurn(player.getLandsPerTurn() + 1); + if (numExtraLands == Integer.MAX_VALUE) { + player.setLandsPerTurn(Integer.MAX_VALUE); + } else { + player.setLandsPerTurn(player.getLandsPerTurn() + numExtraLands); + } return true; } return true; } - -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/constants/MageObjectType.java b/Mage/src/main/java/mage/constants/MageObjectType.java index 9426f86a1a..97782827d4 100644 --- a/Mage/src/main/java/mage/constants/MageObjectType.java +++ b/Mage/src/main/java/mage/constants/MageObjectType.java @@ -33,6 +33,7 @@ public enum MageObjectType { PERMANENT ("Permanent", true, true), EMBLEM ("Emblem", false, false), COMMANDER ("Commander", false, false), + PLANE ("Plane", false, false), NULL("NullObject", false, false); private final String text; diff --git a/Mage/src/main/java/mage/constants/PlanarDieRoll.java b/Mage/src/main/java/mage/constants/PlanarDieRoll.java new file mode 100644 index 0000000000..0272dc4476 --- /dev/null +++ b/Mage/src/main/java/mage/constants/PlanarDieRoll.java @@ -0,0 +1,51 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.constants; + +/** + * + * @author spjspj + */ + +public enum PlanarDieRoll { + NIL_ROLL ("Nil Roll"), + CHAOS_ROLL("Chaos Roll"), + PLANAR_ROLL ("Planar Roll"); + + private final String text; + + PlanarDieRoll(String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } + +} diff --git a/Mage/src/main/java/mage/constants/Planes.java b/Mage/src/main/java/mage/constants/Planes.java new file mode 100644 index 0000000000..21f2eca26f --- /dev/null +++ b/Mage/src/main/java/mage/constants/Planes.java @@ -0,0 +1,57 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.constants; + +/** + * + * @author spjspj + */ +public enum Planes { + PLANE_ACADEMY_AT_TOLARIA_WEST("AcademyAtTolariaWestPlane"), + PLANE_AGYREM("AgyremPlane"), + PLANE_BANT("BantPlane"), + PLANE_EDGE_OF_MALACOL("EdgeOfMalacolPlane"), + PLANE_FEEDING_GROUNDS("FeedingGroundsPlane"), + PLANE_FIELDS_OF_SUMMER("FieldsOfSummerPlane"), + PLANE_LETHE_LAKE("LetheLakePlane"), + PLANE_NAYA("NayaPlane"), + PLANE_THE_DARK_BARONY("TheDarkBaronyPlane"), + PLANE_THE_EON_FOG("TheEonFogPlane"), + PLANE_TURRI_ISLAND("TurriIslandPlane"); + + private final String text; + + Planes(String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } +} diff --git a/Mage/src/main/java/mage/game/Game.java b/Mage/src/main/java/mage/game/Game.java index 3956dd24b7..da64fd01a3 100644 --- a/Mage/src/main/java/mage/game/Game.java +++ b/Mage/src/main/java/mage/game/Game.java @@ -49,6 +49,7 @@ import mage.counters.Counters; import mage.game.combat.Combat; import mage.game.command.Commander; import mage.game.command.Emblem; +import mage.game.command.Plane; import mage.game.events.GameEvent; import mage.game.events.Listener; import mage.game.events.PlayerQueryEvent; @@ -365,6 +366,8 @@ public interface Game extends MageItem, Serializable { void addEmblem(Emblem emblem, MageObject sourceObject, UUID toPlayerId); + boolean addPlane(Plane plane, MageObject sourceObject, UUID toPlayerId); + void addCommander(Commander commander); void addPermanent(Permanent permanent); diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 07d14912ec..84d5600232 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -67,6 +67,7 @@ import mage.game.combat.Combat; import mage.game.command.CommandObject; import mage.game.command.Commander; import mage.game.command.Emblem; +import mage.game.command.Plane; import mage.game.events.*; import mage.game.events.TableEvent.EventType; import mage.game.permanent.Battlefield; @@ -1032,7 +1033,8 @@ public abstract class GameImpl implements Game, Serializable { watchers.add(new PlayerLostLifeWatcher()); watchers.add(new BlockedAttackerWatcher()); watchers.add(new DamageDoneWatcher()); - + watchers.add(new PlanarRollWatcher()); + //20100716 - 103.5 for (UUID playerId : state.getPlayerList(startingPlayerId)) { Player player = getPlayer(playerId); @@ -1070,7 +1072,11 @@ public abstract class GameImpl implements Game, Serializable { cardsWithOpeningAction.remove(card); } } - + + // 20180408 - 901.5 + if (state.isPlaneChase()) { + addPlane(Plane.getRandomPlane(), null, getActivePlayerId()); + } } protected void sendStartMessage(Player choosingPlayer, Player startingPlayer) { @@ -1526,6 +1532,37 @@ public abstract class GameImpl implements Game, Serializable { state.addCommandObject(newEmblem); } + /** + * + * @param plane + * @param sourceObject + * @param toPlayerId controller and owner of the plane (may only be one per + * game..) + * @return boolean - whether the plane was added successfully or not + */ + @Override + public boolean addPlane(Plane plane, MageObject sourceObject, UUID toPlayerId) { + // Implementing planechase as if it were 901.15. Single Planar Deck Option + // Here, can enforce the world plane restriction (the Grand Melee format may have some impact on this implementation) + + // Enforce 'world' rule for planes + for (CommandObject cobject : state.getCommand()) { + if (cobject instanceof Plane) { + return false; + } + } + Plane newPlane = plane.copy(); + newPlane.setSourceObject(sourceObject); + newPlane.setControllerId(toPlayerId); + newPlane.assignNewId(); + newPlane.getAbilities().newId(); + for (Ability ability : newPlane.getAbilities()) { + ability.setSourceId(newPlane.getId()); + } + state.addCommandObject(newPlane); + return true; + } + @Override public void addCommander(Commander commander) { state.addCommandObject(commander); @@ -2466,7 +2503,7 @@ public abstract class GameImpl implements Game, Serializable { //Remove all emblems the player controls for (Iterator it = this.getState().getCommand().iterator(); it.hasNext();) { CommandObject obj = it.next(); - if (obj instanceof Emblem && obj.getControllerId().equals(playerId)) { + if ((obj instanceof Emblem || obj instanceof Plane) && obj.getControllerId().equals(playerId)) { ((Emblem) obj).discardEffects();// This may not be the best fix but it works it.remove(); } diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 22cbccb7f9..ee56576ca3 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -44,6 +44,7 @@ import mage.game.combat.Combat; import mage.game.combat.CombatGroup; import mage.game.command.Command; import mage.game.command.CommandObject; +import mage.game.command.Plane; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.events.ZoneChangeGroupEvent; @@ -95,6 +96,8 @@ public class GameState implements Serializable, Copyable { private UUID monarchId; // player that is the monarch private SpellStack stack; private Command command; + private boolean isPlaneChase; + private List seenPlanes = new ArrayList<>(); private List designations = new ArrayList<>(); private Exile exile; private Battlefield battlefield; @@ -153,6 +156,8 @@ public class GameState implements Serializable, Copyable { this.stack = state.stack.copy(); this.command = state.command.copy(); + this.isPlaneChase = state.isPlaneChase; + this.seenPlanes.addAll(state.seenPlanes); this.designations.addAll(state.designations); this.exile = state.exile.copy(); this.battlefield = state.battlefield.copy(); @@ -203,7 +208,9 @@ public class GameState implements Serializable, Copyable { this.monarchId = state.monarchId; this.stack = state.stack; this.command = state.command; - this.designations = state.designations; + this.isPlaneChase = state.isPlaneChase; + this.seenPlanes = state.seenPlanes; + this.designations = state.designations; this.exile = state.exile; this.battlefield = state.battlefield; this.turnNum = state.turnNum; @@ -450,6 +457,14 @@ public class GameState implements Serializable, Copyable { return designations; } + public List getSeenPlanes() { + return seenPlanes; + } + + public boolean isPlaneChase() { + return isPlaneChase; + } + public Command getCommand() { return command; } @@ -855,6 +870,20 @@ public class GameState implements Serializable, Copyable { addAbility(ability, designation.getId(), null); } } + + public void addSeenPlane(Plane plane, Game game, UUID controllerId) { + if (plane != null) { + getSeenPlanes().add(plane.getName()); + } + } + + public void resetSeenPlanes() { + getSeenPlanes().clear(); + } + + public void setPlaneChase(Game game, boolean isPlaneChase) { + this.isPlaneChase = isPlaneChase; + } public void addCommandObject(CommandObject commandObject) { getCommand().add(commandObject); @@ -1014,6 +1043,8 @@ public class GameState implements Serializable, Copyable { exile.clear(); command.clear(); designations.clear(); + seenPlanes.clear(); + isPlaneChase = false; revealed.clear(); lookedAt.clear(); turnNum = 0; diff --git a/Mage/src/main/java/mage/game/command/Plane.java b/Mage/src/main/java/mage/game/command/Plane.java new file mode 100644 index 0000000000..6f98d8add6 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/Plane.java @@ -0,0 +1,321 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.command; + +import static java.lang.Math.log; +import java.lang.reflect.Constructor; +import java.util.EnumSet; +import java.util.List; +import java.util.Random; +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.ObjectColor; +import mage.abilities.Abilities; +import mage.abilities.AbilitiesImpl; +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.text.TextPart; +import mage.cards.Card; +import mage.cards.FrameStyle; +import mage.constants.CardType; +import mage.constants.Planes; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.Game; +import mage.game.events.ZoneChangeEvent; +import mage.util.GameLog; +import mage.util.SubTypeList; + +/** + * @author spjspj + */ +public class Plane implements CommandObject { + + private static EnumSet emptySet = EnumSet.noneOf(CardType.class); + private static ObjectColor emptyColor = new ObjectColor(); + private static ManaCosts emptyCost = new ManaCostsImpl(); + + private String name = ""; + private UUID id; + private UUID controllerId; + private MageObject sourceObject; + private FrameStyle frameStyle; + private Abilities abilites = new AbilitiesImpl<>(); + private String expansionSetCodeForImage = ""; + + public Plane() { + this.id = UUID.randomUUID(); + } + + public Plane(final Plane plane) { + this.id = plane.id; + this.name = plane.name; + this.frameStyle = plane.frameStyle; + this.controllerId = plane.controllerId; + this.sourceObject = plane.sourceObject; + this.abilites = plane.abilites.copy(); + this.expansionSetCodeForImage = plane.expansionSetCodeForImage; + } + + @Override + public FrameStyle getFrameStyle() { + return frameStyle; + } + + @Override + public void assignNewId() { + this.id = UUID.randomUUID(); + } + + public void setSourceObject(MageObject sourceObject) { + this.sourceObject = sourceObject; + if (sourceObject instanceof Card) { + if (name.isEmpty()) { + name = sourceObject.getSubtype(null).toString(); + } + if (expansionSetCodeForImage.isEmpty()) { + expansionSetCodeForImage = ((Card) sourceObject).getExpansionSetCode(); + } + } + } + + @Override + public MageObject getSourceObject() { + return sourceObject; + } + + @Override + public UUID getSourceId() { + if (sourceObject != null) { + return sourceObject.getId(); + } + return null; + } + + @Override + public UUID getControllerId() { + return this.controllerId; + } + + public void setControllerId(UUID controllerId) { + this.controllerId = controllerId; + this.abilites.setControllerId(controllerId); + } + + @Override + public String getName() { + return name; + } + + @Override + public String getIdName() { + return getName() + " [" + getId().toString().substring(0, 3) + ']'; + } + + @Override + public String getLogName() { + return GameLog.getColoredObjectIdName(this); + } + + @Override + public String getImageName() { + return this.name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public EnumSet getCardType() { + return emptySet; + } + + @Override + public SubTypeList getSubtype(Game game) { + return new SubTypeList(); + } + + @Override + public boolean hasSubtype(SubType subtype, Game game) { + return false; + } + + @Override + public EnumSet getSuperType() { + return EnumSet.noneOf(SuperType.class); + } + + @Override + public Abilities getAbilities() { + return abilites; + } + + @Override + public boolean hasAbility(UUID abilityId, Game game) { + return abilites.containsKey(abilityId); + } + + @Override + public ObjectColor getColor(Game game) { + return emptyColor; + } + + @Override + public ObjectColor getFrameColor(Game game) { + return emptyColor; + } + + @Override + public ManaCosts getManaCost() { + return emptyCost; + } + + @Override + public int getConvertedManaCost() { + return 0; + } + + @Override + public MageInt getPower() { + return MageInt.EmptyMageInt; + } + + @Override + public MageInt getToughness() { + return MageInt.EmptyMageInt; + } + + @Override + public int getStartingLoyalty() { + return 0; + } + + @Override + public void adjustCosts(Ability ability, Game game) { + } + + @Override + public void adjustTargets(Ability ability, Game game) { + } + + @Override + public UUID getId() { + return this.id; + } + + @Override + public void setCopy(boolean isCopy) { + } + + @Override + public boolean isCopy() { + return false; + } + + @Override + public Plane copy() { + return new Plane(this); + } + + public void setExpansionSetCodeForImage(String expansionSetCodeForImage) { + this.expansionSetCodeForImage = expansionSetCodeForImage; + } + + public String getExpansionSetCodeForImage() { + return expansionSetCodeForImage; + } + + @Override + public int getZoneChangeCounter(Game game) { + return 1; // Emblems can't move zones until now so return always 1 + } + + @Override + public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) { + throw new UnsupportedOperationException("Unsupported operation"); + } + + @Override + public void setZoneChangeCounter(int value, Game game) { + throw new UnsupportedOperationException("Unsupported operation"); + } + + public boolean isAllCreatureTypes() { + return false; + } + + public void setIsAllCreatureTypes(boolean value) { + } + + public void discardEffects() { + for (Ability ability : abilites) { + for (Effect effect : ability.getEffects()) { + if (effect instanceof ContinuousEffect) { + ((ContinuousEffect) effect).discard(); + } + } + } + } + + @Override + public List getTextParts() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public TextPart addTextPart(TextPart textPart) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void removePTCDA() { + } + + public static Plane getRandomPlane() { + int pick = new Random().nextInt(Planes.values().length); + String planeName = Planes.values()[pick].toString(); + planeName = "mage.game.command.planes." + planeName; + try { + Class c = Class.forName(planeName); + Constructor cons = c.getConstructor(); + Object plane = cons.newInstance(); + if (plane != null && plane instanceof mage.game.command.Plane) { + return (Plane) plane; + } + } catch (Exception ex) { + } + return null; + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/AcademyAtTolariaWestPlane.java b/Mage/src/main/java/mage/game/command/planes/AcademyAtTolariaWestPlane.java new file mode 100644 index 0000000000..31e6718c84 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/planes/AcademyAtTolariaWestPlane.java @@ -0,0 +1,79 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ..AS IS.. AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.command.planes; + +import java.util.ArrayList; +import java.util.List; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.HellbentCondition; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.discard.DiscardHandControllerEffect; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.command.Plane; +import mage.target.Target; +import mage.watchers.common.PlanarRollWatcher; + +/** + * + * @author spjspj + */ +public class AcademyAtTolariaWestPlane extends Plane { + + public AcademyAtTolariaWestPlane() { + this.setName("Plane - Academy at Tolaria West"); + this.setExpansionSetCodeForImage("PCA"); + + // At the beginning of your end step, if you have 0 cards in hand, draw seven cards + Ability ability = new BeginningOfEndStepTriggeredAbility(Zone.COMMAND, new DrawCardSourceControllerEffect(7), TargetController.ANY, HellbentCondition.instance, false); + this.getAbilities().add(ability); + + // Active player can roll the planar die: Whenever you roll {CHAOS}, discard your hand + Effect chaosEffect = new DiscardHandControllerEffect(); + Target chaosTarget = null; + + List chaosEffects = new ArrayList(); + chaosEffects.add(chaosEffect); + + List chaosTargets = new ArrayList(); + chaosTargets.add(chaosTarget); + + ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); + chaosAbility.addWatcher(new PlanarRollWatcher()); + this.getAbilities().add(chaosAbility); + chaosAbility.setMayActivate(TargetController.ANY); + this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java b/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java new file mode 100644 index 0000000000..ffe46756b0 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java @@ -0,0 +1,188 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ..AS IS.. AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.command.planes; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; +import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.cards.Card; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.command.Plane; +import mage.game.permanent.Permanent; +import mage.target.Target; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.PlanarRollWatcher; + +/** + * + * @author spjspj + */ +public class AgyremPlane extends Plane { + + private static final FilterControlledCreaturePermanent filterWhite = new FilterControlledCreaturePermanent("a white creature"); + private static final FilterControlledCreaturePermanent filterNonWhite = new FilterControlledCreaturePermanent("a nonwhite creature"); + + static { + filterWhite.add(new ColorPredicate(ObjectColor.WHITE)); + filterNonWhite.add(Predicates.not(new ColorPredicate(ObjectColor.WHITE))); + } + + public AgyremPlane() { + this.setName("Plane - Agyrem"); + this.setExpansionSetCodeForImage("PCA"); + + // Whenever a white creature dies, return it to the battlefield under its owner's control at the beginning of the next end step + DiesCreatureTriggeredAbility ability = new DiesCreatureTriggeredAbility(Zone.COMMAND, new AgyremEffect(), false, filterWhite, true); + this.getAbilities().add(ability); + DiesCreatureTriggeredAbility ability2 = new DiesCreatureTriggeredAbility(Zone.COMMAND, new AgyremEffect2(), false, filterNonWhite, true); + this.getAbilities().add(ability2); + + // Active player can roll the planar die: Whenever you roll {CHAOS}, creatures can't attack you until a player planeswalks + Effect chaosEffect = new AgyremRestrictionEffect(); + Target chaosTarget = null; + + List chaosEffects = new ArrayList(); + chaosEffects.add(chaosEffect); + List chaosTargets = new ArrayList(); + chaosTargets.add(chaosTarget); + + ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); + chaosAbility.addWatcher(new PlanarRollWatcher()); + this.getAbilities().add(chaosAbility); + chaosAbility.setMayActivate(TargetController.ANY); + this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); + } +} + +class AgyremEffect extends OneShotEffect { + + public AgyremEffect() { + super(Outcome.PutCardInPlay); + this.staticText = "return that card to the battlefield under its owner's control at the beginning of the next end step"; + } + + public AgyremEffect(final AgyremEffect effect) { + super(effect); + } + + @Override + public AgyremEffect copy() { + return new AgyremEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (card != null) { + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setText("return that card to the battlefield under its owner's control at the beginning of the next end step"); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.ANY), source); + return true; + } + return false; + } +} + +class AgyremEffect2 extends OneShotEffect { + + public AgyremEffect2() { + super(Outcome.PutCardInPlay); + this.staticText = "return it to its owner's hand at the beginning of the next end step"; + } + + public AgyremEffect2(final AgyremEffect2 effect) { + super(effect); + } + + @Override + public AgyremEffect2 copy() { + return new AgyremEffect2(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (card != null) { + Effect effect = new ReturnFromGraveyardToHandTargetEffect(); + effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setText("return it to its owner's hand at the beginning of the next end step"); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone.HAND, effect, TargetController.ANY), source); + return true; + } + return false; + } +} + +class AgyremRestrictionEffect extends RestrictionEffect { + + AgyremRestrictionEffect() { + super(Duration.Custom, Outcome.Benefit); + staticText = "Creatures can't attack you"; + } + + AgyremRestrictionEffect(final AgyremRestrictionEffect effect) { + super(effect); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent.isCreature(); + } + + @Override + public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game) { + return !defenderId.equals(source.getControllerId()); + } + + @Override + public AgyremRestrictionEffect copy() { + return new AgyremRestrictionEffect(this); + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/BantPlane.java b/Mage/src/main/java/mage/game/command/planes/BantPlane.java new file mode 100644 index 0000000000..84e63a30d2 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/planes/BantPlane.java @@ -0,0 +1,148 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ..AS IS.. AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.command.planes; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.condition.common.TargetHasCounterCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.ExaltedAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.command.Plane; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; +import mage.watchers.common.PlanarRollWatcher; + +/** + * + * @author spjspj + */ +public class BantPlane extends Plane { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Creatures"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("Green, White or Blue creatures"); + + static { + filter2.add(Predicates.or(new ColorPredicate(ObjectColor.GREEN), new ColorPredicate(ObjectColor.WHITE), new ColorPredicate(ObjectColor.BLUE))); + } + + private static final String rule = "{this} has indestructible as long as it has a divinity counter on it"; + + public BantPlane() { + this.setName("Plane - Bant"); + this.setExpansionSetCodeForImage("PCA"); + + // All creatures have exalted + Ability ability = new SimpleStaticAbility(Zone.COMMAND, new GainAbilityAllEffect(new ExaltedAbility(), Duration.Custom, StaticFilters.FILTER_PERMANENT_CREATURE)); + this.getAbilities().add(ability); + + // Active player can roll the planar die: Whenever you roll {CHAOS}, put a divinity counter on target green, white, or blue creature. That creature gains indestructible for as long as it has a divinity counter on it. + Effect chaosEffect = new ConditionalContinuousEffect(new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.Custom), new TargetHasCounterCondition(CounterType.DIVINITY), rule); + Target chaosTarget = new TargetCreaturePermanent(1, 1, filter2, false); + Effect chaosEffect2 = new AddCountersTargetEffect(CounterType.DIVINITY.createInstance()); + + List chaosEffects = new ArrayList(); + chaosEffects.add(chaosEffect2); + chaosEffects.add(chaosEffect); + + List chaosTargets = new ArrayList(); + chaosTargets.add(chaosTarget); + chaosTargets.add(chaosTarget); + + ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); + chaosAbility.addWatcher(new PlanarRollWatcher()); + this.getAbilities().add(chaosAbility); + chaosAbility.setMayActivate(TargetController.ANY); + this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); + } +} + +class PlanarDieRollCostIncreasingEffect extends CostModificationEffectImpl { + + private final UUID originalId; + + PlanarDieRollCostIncreasingEffect(UUID originalId) { + super(Duration.EndOfGame, Outcome.Benefit, CostModificationType.INCREASE_COST); + this.originalId = originalId; + } + + PlanarDieRollCostIncreasingEffect(final PlanarDieRollCostIncreasingEffect effect) { + super(effect); + this.originalId = effect.originalId; + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + Player activePlayer = game.getPlayer(game.getActivePlayerId()); + if (activePlayer != null) { + PlanarRollWatcher watcher = (PlanarRollWatcher) game.getState().getWatchers().get(PlanarRollWatcher.class.getSimpleName()); + int rolledCounter = 0; + if (watcher != null) { + rolledCounter = watcher.getNumberTimesPlanarDieRolled(activePlayer.getId()); + } + CardUtil.increaseCost(abilityToModify, rolledCounter); + } + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify.getOriginalId().equals(originalId); + } + + @Override + public PlanarDieRollCostIncreasingEffect copy() { + return new PlanarDieRollCostIncreasingEffect(this); + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java new file mode 100644 index 0000000000..f2b68d3e9c --- /dev/null +++ b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java @@ -0,0 +1,131 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ..AS IS.. AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.command.planes; + +import java.util.ArrayList; +import java.util.List; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.UntapAllControllerEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.PhaseStep; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.command.Plane; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.target.Target; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.PlanarRollWatcher; + +/** + * + * @author spjspj + */ +public class EdgeOfMalacolPlane extends Plane { + + public EdgeOfMalacolPlane() { + this.setName("Plane - Edge Of Malacol"); + this.setExpansionSetCodeForImage("PCA"); + + // If a creature you control would untap during your untap step, put two +1/+1 counters on it instead. + SimpleStaticAbility ability = new SimpleStaticAbility(Zone.COMMAND, new EdgeOfMalacolEffect()); + this.getAbilities().add(ability); + + // Active player can roll the planar die: Whenever you roll {CHAOS}, untap each creature you control + Effect chaosEffect = new UntapAllControllerEffect(new FilterControlledCreaturePermanent(), "untap each creature you control"); + Target chaosTarget = null; + + List chaosEffects = new ArrayList(); + chaosEffects.add(chaosEffect); + List chaosTargets = new ArrayList(); + chaosTargets.add(chaosTarget); + + ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); + chaosAbility.addWatcher(new PlanarRollWatcher()); + this.getAbilities().add(chaosAbility); + chaosAbility.setMayActivate(TargetController.ANY); + this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); + } +} + +class EdgeOfMalacolEffect extends ContinuousRuleModifyingEffectImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + public EdgeOfMalacolEffect() { + super(Duration.Custom, Outcome.Detriment); + } + + public EdgeOfMalacolEffect(final EdgeOfMalacolEffect effect) { + super(effect); + } + + @Override + public EdgeOfMalacolEffect copy() { + return new EdgeOfMalacolEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.UNTAP; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + // Prevent untap event of creatures of target player + if (game.getTurn().getStepType() == PhaseStep.UNTAP) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && filter.match(permanent, game)) { + Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + return true; + } + } + return false; + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java b/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java new file mode 100644 index 0000000000..93f66eeb1e --- /dev/null +++ b/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java @@ -0,0 +1,97 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ..AS IS.. AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.command.planes; + +import java.util.ArrayList; +import java.util.List; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.command.Plane; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.PlanarRollWatcher; + +/** + * + * @author spjspj + */ +public class FeedingGroundsPlane extends Plane { + + private static final FilterCard filter = new FilterCard("Red spells or Green spells"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("Red or Green creature"); + + static { + filter.add(Predicates.or( + new ColorPredicate(ObjectColor.RED), + new ColorPredicate(ObjectColor.GREEN))); + filter2.add(Predicates.or(new ColorPredicate(ObjectColor.RED), new ColorPredicate(ObjectColor.GREEN))); + } + + private static final String rule = "put X +1/+1 counters on target creature, where X is that creature's converted mana cost"; + + public FeedingGroundsPlane() { + this.setName("Plane - Feeding Grounds"); + this.setExpansionSetCodeForImage("PCA"); + + // Red spells cost {1} less to cast. Green spells cost {1} less to cast + Ability ability = new SimpleStaticAbility(Zone.COMMAND, new SpellsCostReductionControllerEffect(filter, 1)); + + this.getAbilities().add(ability); + + // Active player can roll the planar die: Whenever you roll {CHAOS}, target red or green creature gets X +1/+1 counters + Effect chaosEffect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(), new TargetConvertedManaCost()); + Target chaosTarget = new TargetCreaturePermanent(1, 1, filter2, false); + + List chaosEffects = new ArrayList(); + chaosEffects.add(chaosEffect); + List chaosTargets = new ArrayList(); + chaosTargets.add(chaosTarget); + + ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); + chaosAbility.addWatcher(new PlanarRollWatcher()); + this.getAbilities().add(chaosAbility); + chaosAbility.setMayActivate(TargetController.ANY); + this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java b/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java new file mode 100644 index 0000000000..aa8bc003bc --- /dev/null +++ b/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java @@ -0,0 +1,115 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ..AS IS.. AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.command.planes; + +import java.util.ArrayList; +import java.util.List; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.GainLifeTargetEffect; +import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.game.Game; +import mage.game.command.Plane; +import mage.players.Player; +import mage.target.Target; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.PlanarRollWatcher; + +/** + * + * @author spjspj + */ +public class FieldsOfSummerPlane extends Plane { + + private final static FilterSpell filter = new FilterSpell("a spell"); + + public FieldsOfSummerPlane() { + this.setName("Plane - Fields of Summer"); + this.setExpansionSetCodeForImage("PCA"); + + // Whenever a player casts a spell, that player may gain 2 life + SpellCastAllTriggeredAbility ability = new SpellCastAllTriggeredAbility(Zone.COMMAND, new FieldsOfSummerEffect(), filter, false, SetTargetPointer.PLAYER); + this.getAbilities().add(ability); + + // Active player can roll the planar die: Whenever you roll {CHAOS}, you may gain 10 life + Effect chaosEffect = new GainLifeEffect(10); + Target chaosTarget = null; + + List chaosEffects = new ArrayList(); + chaosEffects.add(chaosEffect); + List chaosTargets = new ArrayList(); + chaosTargets.add(chaosTarget); + + ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); + chaosAbility.addWatcher(new PlanarRollWatcher()); + this.getAbilities().add(chaosAbility); + chaosAbility.setMayActivate(TargetController.ANY); + this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); + } +} + +class FieldsOfSummerEffect extends OneShotEffect { + + public FieldsOfSummerEffect() { + super(Outcome.GainLife); + this.staticText = "that player may gain 2 life"; + } + + public FieldsOfSummerEffect(final FieldsOfSummerEffect effect) { + super(effect); + } + + @Override + public FieldsOfSummerEffect copy() { + return new FieldsOfSummerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player owner = game.getPlayer(this.getTargetPointer().getFirst(game, source)); + if (owner != null && owner.canRespond() && owner.chooseUse(Outcome.Benefit, "Gain 2 life?", source, game)) { + Effect effect = new GainLifeTargetEffect(2); + effect.setTargetPointer(new FixedTarget(owner.getId())).apply(game, source); + return true; + } + return false; + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java b/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java new file mode 100644 index 0000000000..09f61a54de --- /dev/null +++ b/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java @@ -0,0 +1,79 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ..AS IS.. AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.command.planes; + +import java.util.ArrayList; +import java.util.List; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; +import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveTargetEffect; +import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.command.Plane; +import mage.target.Target; +import mage.target.TargetPlayer; +import mage.watchers.common.PlanarRollWatcher; + +/** + * + * @author spjspj + */ +public class LetheLakePlane extends Plane { + + public LetheLakePlane() { + this.setName("Plane - Lethe Lake"); + this.setExpansionSetCodeForImage("PCA"); + + // At the beginning of your upkeep, put the top ten cards of your libary into your graveyard + Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.COMMAND, new PutLibraryIntoGraveTargetEffect(10).setText("that player puts the top 10 cards of their library into their graveyard"), TargetController.ANY, false, true); + this.getAbilities().add(ability); + + // Active player can roll the planar die: Whenever you roll {CHAOS}, target player puts the top ten cards of his or her library into his or her graveyard + Effect chaosEffect = new PutTopCardOfLibraryIntoGraveTargetEffect(10); + Target chaosTarget = new TargetPlayer(); + + List chaosEffects = new ArrayList(); + chaosEffects.add(chaosEffect); + + List chaosTargets = new ArrayList(); + chaosTargets.add(chaosTarget); + + ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); + chaosAbility.addWatcher(new PlanarRollWatcher()); + this.getAbilities().add(chaosAbility); + chaosAbility.setMayActivate(TargetController.ANY); + this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/NayaPlane.java b/Mage/src/main/java/mage/game/command/planes/NayaPlane.java new file mode 100644 index 0000000000..934a96d37d --- /dev/null +++ b/Mage/src/main/java/mage/game/command/planes/NayaPlane.java @@ -0,0 +1,94 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ..AS IS.. AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.command.planes; + +import java.util.ArrayList; +import java.util.List; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.PlayAdditionalLandsAllEffect; +import mage.constants.Duration; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.command.Plane; +import mage.target.Target; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.PlanarRollWatcher; + +/** + * + * @author spjspj + */ +public class NayaPlane extends Plane { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Red, Green or White creature"); + private static final FilterControlledLandPermanent filter2 = new FilterControlledLandPermanent("lands you control"); + + static { + filter.add(Predicates.or(new ColorPredicate(ObjectColor.RED), new ColorPredicate(ObjectColor.GREEN), new ColorPredicate(ObjectColor.WHITE))); + } + + private static final String rule = "{this} gets +1/+1 until end of of turn for each land you control"; + + public NayaPlane() { + this.setName("Plane - Naya"); + this.setExpansionSetCodeForImage("PCA"); + + // You may play any number of lands on each of your turns + Ability ability = new SimpleStaticAbility(Zone.COMMAND, new PlayAdditionalLandsAllEffect(Integer.MAX_VALUE)); + this.getAbilities().add(ability); + + // Active player can roll the planar die: Whenever you roll {CHAOS}, target red, green or white creature gets +1/+1 until end of turn for each land you control + Effect chaosEffect = new BoostTargetEffect(new PermanentsOnBattlefieldCount(filter2), new PermanentsOnBattlefieldCount(filter2), Duration.EndOfTurn); + Target chaosTarget = new TargetControlledCreaturePermanent(1, 1, filter, false); + + List chaosEffects = new ArrayList(); + chaosEffects.add(chaosEffect); + List chaosTargets = new ArrayList(); + chaosTargets.add(chaosTarget); + + ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); + chaosAbility.addWatcher(new PlanarRollWatcher()); + this.getAbilities().add(chaosAbility); + chaosAbility.setMayActivate(TargetController.ANY); + this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/TheDarkBaronyPlane.java b/Mage/src/main/java/mage/game/command/planes/TheDarkBaronyPlane.java new file mode 100644 index 0000000000..4cfeecf70d --- /dev/null +++ b/Mage/src/main/java/mage/game/command/planes/TheDarkBaronyPlane.java @@ -0,0 +1,89 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ..AS IS.. AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.command.planes; + +import java.util.ArrayList; +import java.util.List; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.PutCardIntoGraveFromAnywhereAllTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; +import mage.constants.SetTargetPointer; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.command.Plane; +import mage.target.Target; +import mage.watchers.common.PlanarRollWatcher; + +/** + * + * @author spjspj + */ +public class TheDarkBaronyPlane extends Plane { + + private static final FilterCard filter = new FilterCard("a nonblack card"); + + static { + filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK))); + } + + public TheDarkBaronyPlane() { + this.setName("Plane - The Dark Barony"); + this.setExpansionSetCodeForImage("PCA"); + + // Whenever a nonblack card is put into a player's graveyard from anywhere, that player loses 2 life + Ability ability = new PutCardIntoGraveFromAnywhereAllTriggeredAbility(Zone.COMMAND, + new LoseLifeTargetEffect(2), false, filter, TargetController.ANY, SetTargetPointer.PLAYER); + this.getAbilities().add(ability); + + // Active player can roll the planar die: Whenever you roll {CHAOS}, each player dicards a card + Effect chaosEffect = new DiscardEachPlayerEffect(TargetController.OPPONENT); + Target chaosTarget = null; + + List chaosEffects = new ArrayList(); + chaosEffects.add(chaosEffect); + List chaosTargets = new ArrayList(); + chaosTargets.add(chaosTarget); + + ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); + chaosAbility.addWatcher(new PlanarRollWatcher()); + this.getAbilities().add(chaosAbility); + chaosAbility.setMayActivate(TargetController.ANY); + this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java b/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java new file mode 100644 index 0000000000..f599e40077 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java @@ -0,0 +1,78 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ..AS IS.. AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.command.planes; + +import java.util.ArrayList; +import java.util.List; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.SkipUntapStepEffect; +import mage.abilities.effects.common.UntapAllControllerEffect; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.game.command.Plane; +import mage.target.Target; +import mage.watchers.common.PlanarRollWatcher; + +/** + * + * @author spjspj + */ +public class TheEonFogPlane extends Plane { + + public TheEonFogPlane() { + this.setName("Plane - The Eon Fog"); + this.setExpansionSetCodeForImage("PCA"); + + // All players miss their untap step + Ability ability = new SimpleStaticAbility(Zone.COMMAND, new SkipUntapStepEffect()); + this.getAbilities().add(ability); + + // Active player can roll the planar die: Whenever you roll {CHAOS}, untap all permanents you control + Effect chaosEffect = new UntapAllControllerEffect(new FilterControlledPermanent()); + Target chaosTarget = null; + + List chaosEffects = new ArrayList(); + chaosEffects.add(chaosEffect); + + List chaosTargets = new ArrayList(); + chaosTargets.add(chaosTarget); + + ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); + chaosAbility.addWatcher(new PlanarRollWatcher()); + this.getAbilities().add(chaosAbility); + chaosAbility.setMayActivate(TargetController.ANY); + this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/TurriIslandPlane.java b/Mage/src/main/java/mage/game/command/planes/TurriIslandPlane.java new file mode 100644 index 0000000000..08d216244a --- /dev/null +++ b/Mage/src/main/java/mage/game/command/planes/TurriIslandPlane.java @@ -0,0 +1,87 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ..AS IS.. AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.game.command.planes; + +import java.util.ArrayList; +import java.util.List; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.RevealLibraryPutIntoHandEffect; +import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionAllEffect; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.command.Plane; +import mage.target.Target; +import mage.watchers.common.PlanarRollWatcher; + +/** + * + * @author spjspj + */ +public class TurriIslandPlane extends Plane { + + private static final FilterCard filter = new FilterCard("creature spells"); + + static { + filter.add(new CardTypePredicate(CardType.CREATURE)); + } + + public TurriIslandPlane() { + this.setName("Plane - Turri Island"); + this.setExpansionSetCodeForImage("PCA"); + + // Creature spells cost {2} less to cast. + Ability ability = new SimpleStaticAbility(Zone.COMMAND, new SpellsCostReductionAllEffect(filter, 2)); + + this.getAbilities().add(ability); + + // Active player can roll the planar die: Whenever you roll {CHAOS}, reveal the top three cards of your library. Put all creature cards revealed this way into your hand and the rest into your graveyard. + Effect chaosEffect = new RevealLibraryPutIntoHandEffect(3, new FilterCreatureCard("creature cards"), Zone.GRAVEYARD); + Target chaosTarget = null; + + List chaosEffects = new ArrayList(); + chaosEffects.add(chaosEffect); + List chaosTargets = new ArrayList(); + chaosTargets.add(chaosTarget); + + ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); + chaosAbility.addWatcher(new PlanarRollWatcher()); + this.getAbilities().add(chaosAbility); + chaosAbility.setMayActivate(TargetController.ANY); + this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); + } +} diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index ef7ec2b5b3..5a928d165e 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -231,6 +231,7 @@ public class GameEvent implements Serializable { CAN_TAKE_MULLIGAN, FLIP_COIN, COIN_FLIPPED, SCRY, FATESEAL, ROLL_DICE, DICE_ROLLED, + ROLL_PLANAR_DIE, PLANAR_DIE_ROLLED, PAID_CUMULATIVE_UPKEEP, DIDNT_PAY_CUMULATIVE_UPKEEP, //permanent events diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index 47c1dba126..9d94d9976b 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -57,6 +57,7 @@ import mage.choices.Choice; import mage.constants.AbilityType; import mage.constants.ManaType; import mage.constants.Outcome; +import mage.constants.PlanarDieRoll; import mage.constants.PlayerAction; import mage.constants.RangeOfInfluence; import mage.constants.Zone; @@ -423,6 +424,11 @@ public interface Player extends MageItem, Copyable { int rollDice(Game game, ArrayList appliedEffects, int numSides); + PlanarDieRoll rollPlanarDie(Game game); + + PlanarDieRoll rollPlanarDie(Game game, ArrayList appliedEffects); + PlanarDieRoll rollPlanarDie(Game game, ArrayList appliedEffects, int numberChaosSides, int numberPlanarSides); + @Deprecated void discard(int amount, Ability source, Game game); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 1c2986aeac..17ddb76424 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2482,6 +2482,50 @@ public abstract class PlayerImpl implements Player, Serializable { return event.getAmount(); } + @Override + public PlanarDieRoll rollPlanarDie(Game game) { + return this.rollPlanarDie(game, null); + } + + @Override + public PlanarDieRoll rollPlanarDie(Game game, ArrayList appliedEffects) { + return rollPlanarDie(game, appliedEffects, 1, 1); + } + /** + * @param game + * @param appliedEffects + * @param numberChaosSides The number of chaos sides the planar die currently has (normally 1 but can be 5) + * @param numberPlanarSides The number of chaos sides the planar die currently has (normally 1) + * @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll or NilRoll + */ + @Override + public PlanarDieRoll rollPlanarDie(Game game, ArrayList appliedEffects, int numberChaosSides, int numberPlanarSides) { + int result = RandomUtil.nextInt(6) + 1; + PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL; + if (numberChaosSides + numberPlanarSides > 6) { + numberChaosSides = 1; + numberPlanarSides = 1; + } + if (result <= numberChaosSides) { + roll = PlanarDieRoll.CHAOS_ROLL; + } + else if (result > 6 - numberPlanarSides) { + roll = PlanarDieRoll.PLANAR_ROLL; + } + if (!game.isSimulation()) { + game.informPlayers("[Roll the planar die] " + getLogName() + " rolled a " + roll + " on the planar die"); + } + GameEvent event = new GameEvent(GameEvent.EventType.ROLL_PLANAR_DIE, playerId, null, playerId, result, true); + event.setAppliedEffects(appliedEffects); + event.setData(roll + ""); + if (!game.replaceEvent(event)) { + GameEvent ge = new GameEvent(GameEvent.EventType.PLANAR_DIE_ROLLED, playerId, null, playerId, event.getAmount(), event.getFlag()); + ge.setData(roll + ""); + game.fireEvent(ge); + } + return roll; + } + @Override public List getAvailableAttackers(Game game) { // TODO: get available opponents and their planeswalkers, check for each if permanent can attack one diff --git a/Mage/src/main/java/mage/watchers/common/PlanarRollWatcher.java b/Mage/src/main/java/mage/watchers/common/PlanarRollWatcher.java new file mode 100644 index 0000000000..2fafb9b94a --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/PlanarRollWatcher.java @@ -0,0 +1,89 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.watchers.common; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +/* + * Counts the number of times the planar die has been rolled per player per turn + * This watcher is automatically started in gameImpl.init for each game + * + * @author spjspj + */ +public class PlanarRollWatcher extends Watcher { + + private final Map numberTimesPlanarDieRolled = new HashMap<>(); + + public PlanarRollWatcher() { + super(PlanarRollWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public PlanarRollWatcher(final PlanarRollWatcher watcher) { + super(watcher); + for (Entry entry : watcher.numberTimesPlanarDieRolled.entrySet()) { + numberTimesPlanarDieRolled.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.PLANAR_DIE_ROLLED) { + UUID playerId = event.getPlayerId(); + if (playerId != null) { + Integer amount = numberTimesPlanarDieRolled.get(playerId); + if (amount == null) { + amount = 1; + } else { + amount ++; + } + numberTimesPlanarDieRolled.put(playerId, amount); + } + } + } + + public int getNumberTimesPlanarDieRolled(UUID playerId) { + return numberTimesPlanarDieRolled.getOrDefault(playerId, 0); + } + + @Override + public void reset() { + numberTimesPlanarDieRolled.clear(); + } + + @Override + public PlanarRollWatcher copy() { + return new PlanarRollWatcher(this); + } +}