From afcf3a43d29722cf860a8d1269dc91215fd39c16 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Tue, 21 Nov 2017 16:42:27 -0500 Subject: [PATCH 01/18] fixed Simic Manipulator implementation --- .../src/mage/cards/s/SimicManipulator.java | 67 ++----------------- Mage/src/main/java/mage/cards/CardImpl.java | 15 +++++ .../java/mage/constants/TargetAdjustment.java | 3 +- 3 files changed, 22 insertions(+), 63 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SimicManipulator.java b/Mage.Sets/src/mage/cards/s/SimicManipulator.java index decbf8fc60..40675ce879 100644 --- a/Mage.Sets/src/mage/cards/s/SimicManipulator.java +++ b/Mage.Sets/src/mage/cards/s/SimicManipulator.java @@ -30,26 +30,20 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.abilities.keyword.EvolveAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; +import mage.constants.TargetAdjustment; import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; /** @@ -69,7 +63,7 @@ public class SimicManipulator extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with power less than or equal to the number of +1/+1 counters removed this way"); public SimicManipulator(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); this.subtype.add(SubType.MUTANT); this.subtype.add(SubType.WIZARD); @@ -80,11 +74,10 @@ public class SimicManipulator extends CardImpl { this.addAbility(new EvolveAbility()); // {T}, Remove one or more +1/+1 counters from Simic Manipulator: Gain control of target creature with power less than or equal to the number of +1/+1 counters removed this way. - // TODO: Improve targeting, that only valid targets (power <= removed counters) can be choosen - // Disadvantage now is, that a creature can be targeted that couldn't be targeted by rules. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SimicManipulatorGainControlTargetEffect(Duration.Custom), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainControlTargetEffect(Duration.Custom, true), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(filter)); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.P1P1.createInstance(), 1, "Remove one or more +1/+1 counters from {this}")); + ability.setTargetAdjustment(TargetAdjustment.SIMIC_MANIPULATOR); this.addAbility(ability); } @@ -97,53 +90,3 @@ public class SimicManipulator extends CardImpl { return new SimicManipulator(this); } } - -class SimicManipulatorGainControlTargetEffect extends ContinuousEffectImpl { - - private boolean valid; - - public SimicManipulatorGainControlTargetEffect(Duration duration) { - super(duration, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); - } - - public SimicManipulatorGainControlTargetEffect(final SimicManipulatorGainControlTargetEffect effect) { - super(effect); - this.valid = effect.valid; - } - - @Override - public void init(Ability source, Game game) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - int maxPower = 0; - for (Cost cost : source.getCosts()) { - if (cost instanceof RemoveVariableCountersSourceCost) { - maxPower = ((RemoveVariableCountersSourceCost) cost).getAmount(); - break; - } - } - if (permanent.getPower().getValue() <= maxPower) { - valid = true; - } - } - } - - @Override - public SimicManipulatorGainControlTargetEffect copy() { - return new SimicManipulatorGainControlTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null && valid) { - return permanent.changeControllerId(source.getControllerId(), game); - } - return false; - } - - @Override - public String getText(Mode mode) { - return "Gain control of target " + mode.getTargets().get(0).getTargetName() + ' ' + duration.toString(); - } -} diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index 0e045b2153..22361e38d8 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -37,6 +37,8 @@ import mage.MageObjectImpl; import mage.Mana; import mage.ObjectColor; import mage.abilities.*; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.RemoveVariableCountersTargetCost; import mage.abilities.effects.common.NameACardEffect; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.repository.PluginClassloaderRegistery; @@ -417,6 +419,19 @@ public abstract class CardImpl extends MageObjectImpl implements Card { ability.getTargets().add(new TargetCreaturePermanent(filter2)); } break; + case SIMIC_MANIPULATOR: //Simic Manipulator only + xValue = 0; + for (Cost cost : ability.getCosts()) { + if (cost instanceof RemoveVariableCountersTargetCost) { + xValue = ((RemoveVariableCountersTargetCost) cost).getAmount(); + break; + } + } + ability.getTargets().clear(); + FilterCreaturePermanent newFilter = new FilterCreaturePermanent("creature with power less than or equal to " + xValue); + newFilter.add(new PowerPredicate(ComparisonType.FEWER_THAN, xValue + 1)); + ability.addTarget(new TargetCreaturePermanent(newFilter)); + break; } } diff --git a/Mage/src/main/java/mage/constants/TargetAdjustment.java b/Mage/src/main/java/mage/constants/TargetAdjustment.java index 0eb07153af..d6f3ab0469 100644 --- a/Mage/src/main/java/mage/constants/TargetAdjustment.java +++ b/Mage/src/main/java/mage/constants/TargetAdjustment.java @@ -12,5 +12,6 @@ public enum TargetAdjustment { X_POWER_LEQ, CHOSEN_NAME, CHOSEN_COLOR, VERSE_COUNTER_TARGETS, - TREASURE_COUNTER_POWER + TREASURE_COUNTER_POWER, + SIMIC_MANIPULATOR } From 27209494401685431965be30d43ed5cc247b22c1 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Tue, 21 Nov 2017 19:55:05 -0500 Subject: [PATCH 02/18] fixed Voidwalk implementation --- Mage.Sets/src/mage/cards/v/Voidwalk.java | 39 ++++++++++++------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/Voidwalk.java b/Mage.Sets/src/mage/cards/v/Voidwalk.java index 4398705271..5f8e83cd60 100644 --- a/Mage.Sets/src/mage/cards/v/Voidwalk.java +++ b/Mage.Sets/src/mage/cards/v/Voidwalk.java @@ -28,19 +28,23 @@ package mage.cards.v; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CipherEffect; -import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlSourceEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; /** * @@ -49,7 +53,7 @@ import mage.target.common.TargetCreaturePermanent; public class Voidwalk extends CardImpl { public Voidwalk(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); // Exile target creature. Return it to the battlefield under its owner's control at the beginning of the next end step. this.getSpellAbility().addEffect(new VoidwalkEffect()); @@ -71,32 +75,29 @@ public class Voidwalk extends CardImpl { class VoidwalkEffect extends OneShotEffect { - private static final String effectText = "Exile target creature. Return it to the battlefield under its owner's control at the beginning of the next end step"; - - VoidwalkEffect() { - super(Outcome.Benefit); - staticText = effectText; + public VoidwalkEffect() { + super(Outcome.Detriment); + staticText = "Exile target creature. Return it to the battlefield under its owner's control at the beginning of the next end step"; } - VoidwalkEffect(VoidwalkEffect effect) { + public VoidwalkEffect(final VoidwalkEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - if (getTargetPointer().getFirst(game, source) != null) { - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (permanent != null) { - int zcc = game.getState().getZoneChangeCounter(permanent.getId()); - if (permanent.moveToExile(null, "", source.getSourceId(), game)) { - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( - new ReturnToBattlefieldUnderOwnerControlSourceEffect(false, zcc + 1)), source); - } - } + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && permanent != null && sourceObject != null) { + if (controller.moveCardToExileWithInfo(permanent, source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) { + //create delayed triggered ability + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + effect.setText("Return that card to the battlefield under its owner's control at the beginning of the next end step"); + effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); + return true; } - return true; } return false; } From ca90f2dc45a0b9a1cdbbba80667ae06831273e9d Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 23 Nov 2017 06:30:49 +0400 Subject: [PATCH 03/18] Add more card's info to viewer (mage book) - full set name, card numbers and total stats --- .../collection/viewer/MageBook.java | 158 +++++++++++++++--- 1 file changed, 132 insertions(+), 26 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java index 2488110b58..6c5600abf3 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java @@ -27,9 +27,7 @@ */ package mage.client.deckeditor.collection.viewer; -import mage.cards.Card; -import mage.cards.CardDimensions; -import mage.cards.MageCard; +import mage.cards.*; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; @@ -38,9 +36,7 @@ import mage.client.MageFrame; import mage.client.cards.BigCard; import mage.client.components.HoverButton; import mage.client.plugins.impl.Plugins; -import mage.client.util.Config; -import mage.client.util.ImageHelper; -import mage.client.util.NaturalOrderCardNumberComparator; +import mage.client.util.*; import mage.client.util.audio.AudioManager; import mage.client.util.sets.ConstructedFormats; import mage.components.ImagePanel; @@ -48,7 +44,6 @@ import mage.components.ImagePanelStyle; import mage.constants.Rarity; import mage.view.CardView; import org.apache.log4j.Logger; -import org.mage.card.arcane.GlowText; import org.mage.card.arcane.ManaSymbols; import javax.imageio.ImageIO; @@ -59,17 +54,17 @@ import java.io.FileNotFoundException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; +import java.util.*; import java.util.List; -import java.util.UUID; -import java.util.logging.Level; -import mage.client.util.CardsViewUtil; + import mage.game.command.Emblem; import mage.game.permanent.PermanentToken; import mage.game.permanent.token.Token; import mage.view.EmblemView; import mage.view.PermanentView; import org.mage.plugins.card.images.CardDownloadData; + +import static java.lang.Math.min; import static org.mage.plugins.card.images.DownloadPictures.getTokenCardUrls; /** @@ -115,6 +110,7 @@ public class MageBook extends JComponent { Image image = ImageHelper.loadImage(LEFT_PAGE_BUTTON_IMAGE_PATH); pageLeft = new HoverButton(null, image, image, image, new Rectangle(64, 64)); + //pageLeft.setBorder(BorderFactory.createLineBorder(new Color(180, 50, 0), 3, true)); //debug pageLeft.setBounds(0, 0, 64, 64); pageLeft.setVisible(false); pageLeft.setObserver(() -> { @@ -146,12 +142,46 @@ public class MageBook extends JComponent { add(jPanelCenter, BorderLayout.CENTER); add(jPanelRight, BorderLayout.LINE_END); - cardDimensions = new CardDimensions(0.45d); - } + int captionHeight = Math.max(30, pageLeft.getHeight()); // caption size = next-prev images - private void addLeftRightPageButtons() { - jLayeredPane.add(pageLeft, JLayeredPane.DEFAULT_LAYER, 0); - jLayeredPane.add(pageRight, JLayeredPane.DEFAULT_LAYER, 1); + + // Top Panel (left page + (caption / stats) + right page + jPanelTop = new JPanel(); + jPanelTop.setLayout(new BorderLayout()); + // jPanelTop.setBorder(BorderFactory.createLineBorder(new Color(180, 50, 150), 3, true)); // debug + jPanelTop.setPreferredSize(new Dimension(captionHeight, captionHeight)); + jPanelCenter.add(jPanelTop, BorderLayout.NORTH); + + // page left + pageRight.setPreferredSize(new Dimension(pageRight.getWidth(), pageRight.getHeight())); + jPanelTop.add(pageRight, BorderLayout.EAST); + // page right + pageLeft.setPreferredSize(new Dimension(pageLeft.getWidth(), pageLeft.getHeight())); + jPanelTop.add(pageLeft, BorderLayout.WEST); + + // Caption Panel + jPanelCaption = new JPanel(); + jPanelCaption.setLayout(new BorderLayout()); + jPanelCaption.setOpaque(false); + jPanelTop.add(jPanelCaption, BorderLayout.CENTER); + + // set's caption + setCaption = new JLabel(); + setCaption.setHorizontalAlignment(SwingConstants.CENTER); + //setCaption.setBorder(BorderFactory.createLineBorder(new Color(180, 50, 150), 3, true)); // debug + setCaption.setFont(jLayeredPane.getFont().deriveFont(25f)); + setCaption.setText("EMPTY CAPTION"); + jPanelCaption.add(setCaption, BorderLayout.NORTH); + + // set's info + setInfo = new JLabel(); + setInfo.setHorizontalAlignment(SwingConstants.CENTER); + //setCaption.setBorder(BorderFactory.createLineBorder(new Color(180, 50, 150), 3, true)); // debug + setInfo.setFont(jLayeredPane.getFont().deriveFont(17f)); + setInfo.setText("EMPTY STATS"); + jPanelCaption.add(setInfo, BorderLayout.SOUTH); + + cardDimensions = new CardDimensions(0.45d); } private void addSetTabs() { @@ -174,7 +204,7 @@ public class MageBook extends JComponent { if (setImage != null) { tab.setOverlayImage(setImage); } else { - System.out.println("Couldn't find: " + "/plugins/images/sets/" + set + "-C.jpg"); + System.out.println("Couldn't find symbol image: " + "/plugins/images/sets/" + set + "-C.jpg"); } tab.setSet(set); tab.setBounds(0, y, 39, 120); @@ -217,8 +247,10 @@ public class MageBook extends JComponent { private void showCardsOrTokens() { stateChanged = false; if (showCardsOrTokens) { + updateCardStats(currentSet, true); showCards(); } else { + updateCardStats(currentSet, false); int numTokens = showTokens(); showEmblems(numTokens); } @@ -226,14 +258,16 @@ public class MageBook extends JComponent { public void showCards() { jLayeredPane.removeAll(); - addLeftRightPageButtons(); + + // stats info + updateCardStats(currentSet, true); List cards = getCards(currentPage, currentSet); int size = cards.size(); Rectangle rectangle = new Rectangle(); rectangle.translate(OFFSET_X, OFFSET_Y); - for (int i = 0; i < Math.min(conf.CARDS_PER_PAGE / 2, size); i++) { + for (int i = 0; i < min(conf.CARDS_PER_PAGE / 2, size); i++) { Card card = cards.get(i).getMockCard(); addCard(new CardView(card), bigCard, null, rectangle); @@ -245,7 +279,7 @@ public class MageBook extends JComponent { - (cardDimensions.frameWidth + CardPosition.GAP_X) * conf.CARD_COLUMNS + CardPosition.GAP_X - OFFSET_X; rectangle.setLocation(second_page_x, OFFSET_Y); - for (int i = conf.CARDS_PER_PAGE / 2; i < Math.min(conf.CARDS_PER_PAGE, size); i++) { + for (int i = conf.CARDS_PER_PAGE / 2; i < min(conf.CARDS_PER_PAGE, size); i++) { Card card = cards.get(i).getMockCard(); addCard(new CardView(card), bigCard, null, rectangle); rectangle = CardPosition.translatePosition(i - conf.CARDS_PER_PAGE / 2, rectangle, conf); @@ -256,7 +290,6 @@ public class MageBook extends JComponent { public int showTokens() { jLayeredPane.removeAll(); - addLeftRightPageButtons(); List tokens = getTokens(currentPage, currentSet); int size = tokens.size(); @@ -264,7 +297,7 @@ public class MageBook extends JComponent { if (tokens != null && tokens.size() > 0) { Rectangle rectangle = new Rectangle(); rectangle.translate(OFFSET_X, OFFSET_Y); - for (int i = 0; i < Math.min(conf.CARDS_PER_PAGE / 2, size); i++) { + for (int i = 0; i < min(conf.CARDS_PER_PAGE / 2, size); i++) { Token token = tokens.get(i); addToken(token, bigCard, null, rectangle); rectangle = CardPosition.translatePosition(i, rectangle, conf); @@ -275,7 +308,7 @@ public class MageBook extends JComponent { - (cardDimensions.frameWidth + CardPosition.GAP_X) * conf.CARD_COLUMNS + CardPosition.GAP_X - OFFSET_X; rectangle.setLocation(second_page_x, OFFSET_Y); - for (int i = conf.CARDS_PER_PAGE / 2; i < Math.min(conf.CARDS_PER_PAGE, size); i++) { + for (int i = conf.CARDS_PER_PAGE / 2; i < min(conf.CARDS_PER_PAGE, size); i++) { Token token = tokens.get(i); addToken(token, bigCard, null, rectangle); rectangle = CardPosition.translatePosition(i - conf.CARDS_PER_PAGE / 2, rectangle, conf); @@ -346,12 +379,26 @@ public class MageBook extends JComponent { boolean implemented = card.getRarity() != Rarity.NA; + // implemented label + // old code, nowadays app load only implemented cards (JayDi85, 23.11.2017) + /* GlowText label = new GlowText(); label.setGlow(implemented ? Color.green : NOT_IMPLEMENTED, 12, 0.0f); label.setText(implemented ? "Implemented" : "Not implemented"); int dx = implemented ? 15 : 5; label.setBounds(rectangle.x + dx, rectangle.y + cardDimensions.frameHeight + 7, 110, 30); jLayeredPane.add(label); + */ + + // card number label + JLabel cardNumber = new JLabel(); + int dy = -5; // image panel have empty space in bottom (bug?), need to move label up + cardNumber.setBounds(rectangle.x, rectangle.y + cardImg.getHeight() + dy, cardDimensions.frameWidth, 20); + cardNumber.setHorizontalAlignment(SwingConstants.CENTER); + //cardNumber.setBorder(BorderFactory.createLineBorder(new Color(180, 50, 150), 3, true)); + cardNumber.setFont(jLayeredPane.getFont().deriveFont(jLayeredPane.getFont().getStyle() | Font.BOLD)); + cardNumber.setText(card.getCardNumber()); + jLayeredPane.add(cardNumber); } private void addToken(Token token, BigCard bigCard, UUID gameId, Rectangle rectangle) { @@ -389,6 +436,60 @@ public class MageBook extends JComponent { return cards.subList(start, end); } + private void updateCardStats(String setCode, boolean isCardsShow){ + // sets do not have total cards number, it's a workaround + + ExpansionSet set = Sets.findSet(setCode); + if (set != null){ + setCaption.setText(set.getCode() + " - " + set.getName()); + }else{ + setCaption.setText("ERROR"); + setInfo.setText("ERROR"); + return; + } + + if (!isCardsShow){ + // tokens or emblems, stats not need + setInfo.setText(""); + return; + } + + // cards stats + + int startNumber = 9999; + int endNumber = 0; + + List cards = set.getSetCardInfo(); + + // first run for numbers list + LinkedList haveNumbers = new LinkedList<>(); + for (ExpansionSet.SetCardInfo card: cards){ + int cardNumber = Integer.parseInt(card.getCardNumber()); + startNumber = min(startNumber, cardNumber); + endNumber = Math.max(endNumber, cardNumber); + haveNumbers.add(cardNumber); + } + + // second run for empty numbers + int countHave = haveNumbers.size(); + int countNotHave = 0; + if (cards.size() > 0){ + for(int i = startNumber; i <= endNumber; i++){ + if(!haveNumbers.contains(i)){ + countNotHave++; + } + } + } + + // result + setInfo.setText(String.format("Have %d cards of %d", countHave, countHave + countNotHave)); + if (countNotHave > 0) { + setInfo.setForeground(new Color(150, 0, 0)); + }else{ + setInfo.setForeground(jLayeredPane.getForeground()); + } + } + private List getTokens(int page, String set) { ArrayList allTokens = getTokenCardUrls(); ArrayList tokens = new ArrayList<>(); @@ -576,7 +677,6 @@ public class MageBook extends JComponent { setSize(conf.WIDTH, conf.HEIGHT); setPreferredSize(new Dimension(conf.WIDTH, conf.HEIGHT)); setMinimumSize(new Dimension(conf.WIDTH, conf.HEIGHT)); - pageRight.setBounds(conf.WIDTH - 2 * LEFT_RIGHT_PAGES_WIDTH - 64, 0, 64, 64); addSetTabs(); showCards(); } @@ -616,7 +716,7 @@ public class MageBook extends JComponent { _3x3Configuration() { this.WIDTH = 950; - this.HEIGHT = 650; + this.HEIGHT = 650 + 50; // + for caption CARD_ROWS = 3; CARD_COLUMNS = 3; this.CARDS_PER_PAGE = 18; @@ -629,7 +729,7 @@ public class MageBook extends JComponent { _4x4Configuration() { this.WIDTH = 1250; - this.HEIGHT = 850; + this.HEIGHT = 850 + 50; // + for caption CARD_ROWS = 4; CARD_COLUMNS = 4; this.CARDS_PER_PAGE = 32; @@ -638,6 +738,10 @@ public class MageBook extends JComponent { } } + private JPanel jPanelTop; + private JPanel jPanelCaption; + private JLabel setCaption; + private JLabel setInfo; private JPanel jPanelLeft; private ImagePanel jPanelCenter; private JPanel jPanelRight; @@ -648,6 +752,8 @@ public class MageBook extends JComponent { private int currentPage = 0; private String currentSet = "RTR"; + private int currentCardsInSet = 0; + private int currentCardsNotInSet = 0; private static CardDimensions cardDimensions = new CardDimensions(1.2d); private static final Logger log = Logger.getLogger(MageBook.class); From 37fc07a2560ca68ed2b93d93f60cb35bf0a7076c Mon Sep 17 00:00:00 2001 From: spjspj Date: Thu, 23 Nov 2017 23:44:51 +1100 Subject: [PATCH 04/18] Add highlights to rendered card title and card type round boxes --- .../mage/card/arcane/CardRendererUtils.java | 5 +++++ .../mage/plugins/card/images/ImageCache.java | 19 +++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index 6e3921f099..c5f41b8217 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -68,6 +68,11 @@ public final class CardRendererUtils { g.fillOval(x + 2, y + 2, bevel * 2 - 4, h - 4); g.fillOval(x + 2 + w - bevel * 2, y + 2, bevel * 2 - 4, h - 4); g.fillRect(x + bevel, y + 2, w - 2 * bevel, h - 4); + g.setPaint(border); + g.setColor(g.getColor().brighter().brighter()); + g.drawLine(x + 1 + bevel, y + 1, x + 1 + bevel + w - 2 * bevel - 2, y + 1); + g.setColor(g.getColor().darker().darker()); + g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); } // Get the width of a mana cost rendered with ManaSymbols.draw diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java index 9e90b03f2a..e1f2c66513 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java @@ -124,7 +124,6 @@ public final class ImageCache { if (exists) { LOGGER.debug("loading thumbnail for " + key + ", path=" + thumbnailPath); BufferedImage thumbnailImage = loadImage(thumbnailFile); - if (thumbnailImage == null) { // thumbnail exists but broken for some reason LOGGER.warn("failed loading thumbnail for " + key + ", path=" + thumbnailPath + ", thumbnail file is probably broken, attempting to recreate it..."); @@ -361,6 +360,23 @@ public final class ImageCache { } } + public static boolean isFaceImagePresent(CardView card) { + String path; + path = CardImageUtils.generateFaceImagePath(card.getName(), card.getExpansionSetCode()); + + if (path == null) { + return false; + } + TFile file = getTFile(path); + if (file == null) { + return false; + } + if (file.exists()) { + return true; + } + return false; + } + public static BufferedImage getThumbnail(CardView card) { return getImage(getKey(card, card.getName(), "#thumb")); } @@ -464,7 +480,6 @@ public final class ImageCache { // return alternateName + "#" + card.getExpansionSetCode() + "#" +card.getType()+ "#" + card.getCardNumber() + "#" // + (card.getTokenSetCode() == null ? "":card.getTokenSetCode()); // } - /** * Load image from file * From 763b8d30fd89e8c7f05d62ef6d9945f1f031eb64 Mon Sep 17 00:00:00 2001 From: spjspj Date: Fri, 24 Nov 2017 21:59:11 +1100 Subject: [PATCH 05/18] Add highlights to rendered card title and card type round boxes --- .../mage/card/arcane/CardRendererUtils.java | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index c5f41b8217..e7a421def3 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -16,8 +16,8 @@ import java.util.regex.Pattern; /** * @author stravant@gmail.com - *

- * Various static utilities for use in the card renderer + *

+ * Various static utilities for use in the card renderer */ public final class CardRendererUtils { @@ -51,6 +51,38 @@ public final class CardRendererUtils { // Return the buffered image return bimage; } + + private static Color abitbrighter(Color c) { + int r = c.getRed(); + int g = c.getGreen(); + int b = c.getBlue(); + int alpha = c.getAlpha(); + + int plus_r = (int) ((255 - r) / 2); + int plus_g = (int) ((255 - g) / 2); + int plus_b = (int) ((255 - b) / 2); + + return new Color(r + plus_r, + g + plus_g, + b + plus_b, + alpha); + } + + private static Color abitdarker(Color c) { + int r = c.getRed(); + int g = c.getGreen(); + int b = c.getBlue(); + int alpha = c.getAlpha(); + + int plus_r = (int) ((255 - r) / 2); + int plus_g = (int) ((255 - g) / 2); + int plus_b = (int) ((255 - b) / 2); + + return new Color(r - plus_r, + g - plus_g, + b - plus_b, + alpha); + } // Draw a rounded box with a 2-pixel border // Used on various card parts. @@ -68,10 +100,11 @@ public final class CardRendererUtils { g.fillOval(x + 2, y + 2, bevel * 2 - 4, h - 4); g.fillOval(x + 2 + w - bevel * 2, y + 2, bevel * 2 - 4, h - 4); g.fillRect(x + bevel, y + 2, w - 2 * bevel, h - 4); - g.setPaint(border); - g.setColor(g.getColor().brighter().brighter()); + g.setPaint(fill); + g.setColor(abitbrighter(g.getColor())); g.drawLine(x + 1 + bevel, y + 1, x + 1 + bevel + w - 2 * bevel - 2, y + 1); - g.setColor(g.getColor().darker().darker()); + g.setPaint(fill); + g.setColor(abitdarker(g.getColor())); g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2); } From 53448e6dc4731e634ab03263acd87878f1c872cb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 24 Nov 2017 12:30:15 +0100 Subject: [PATCH 06/18] Ficed a problem with Bestow, adding multiple times "AURA" subtype. --- .../mage/abilities/keyword/BestowAbility.java | 6 +++-- Mage/src/main/java/mage/game/stack/Spell.java | 25 ++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/keyword/BestowAbility.java b/Mage/src/main/java/mage/abilities/keyword/BestowAbility.java index 0aab88b1f3..dd4cd886e0 100644 --- a/Mage/src/main/java/mage/abilities/keyword/BestowAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/BestowAbility.java @@ -190,8 +190,10 @@ class BestowEntersBattlefieldEffect extends ReplacementEffectImpl { if (bestowPermanent != null) { if (bestowPermanent.hasSubtype(SubType.AURA, game)) { MageObject basicObject = bestowPermanent.getBasicMageObject(game); - basicObject.getSubtype(null).add(SubType.AURA); - basicObject.getCardType().remove(CardType.CREATURE); + if (basicObject != null && !basicObject.getSubtype(null).contains(SubType.AURA)) { + basicObject.getSubtype(null).add(SubType.AURA); + basicObject.getCardType().remove(CardType.CREATURE); + } } } return false; diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index 38d664896e..0b76841eab 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -27,6 +27,10 @@ */ package mage.game.stack; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.Mana; @@ -60,11 +64,6 @@ import mage.players.Player; import mage.util.GameLog; import mage.util.SubTypeList; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; -import java.util.UUID; - /** * * @author BetaSteward_at_googlemail.com @@ -254,7 +253,9 @@ public class Spell extends StackObjImpl implements Card { // Must be removed first time, after that will be removed by continous effect // Otherwise effects like evolve trigger from creature comes into play event card.getCardType().remove(CardType.CREATURE); - card.getSubtype(game).add(SubType.AURA); + if (!card.getSubtype(game).contains(SubType.AURA)) { + card.getSubtype(game).add(SubType.AURA); + } } if (controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null)) { if (bestow) { @@ -263,7 +264,9 @@ public class Spell extends StackObjImpl implements Card { Permanent permanent = game.getPermanent(card.getId()); if (permanent != null && permanent instanceof PermanentCard) { permanent.setSpellAbility(ability); // otherwise spell ability without bestow will be set - card.addCardType(CardType.CREATURE); + if (!card.getCardType().contains(CardType.CREATURE)) { + card.addCardType(CardType.CREATURE); + } card.getSubtype(game).remove(SubType.AURA); } } @@ -483,7 +486,9 @@ public class Spell extends StackObjImpl implements Card { public SubTypeList getSubtype(Game game) { if (this.getSpellAbility() instanceof BestowAbility) { SubTypeList subtypes = card.getSubtype(game); - subtypes.add(SubType.AURA); + if (!subtypes.contains(SubType.AURA)) { // do it only once + subtypes.add(SubType.AURA); + } return subtypes; } return card.getSubtype(game); @@ -493,7 +498,9 @@ public class Spell extends StackObjImpl implements Card { public boolean hasSubtype(SubType subtype, Game game) { if (this.getSpellAbility() instanceof BestowAbility) { // workaround for Bestow (don't like it) SubTypeList subtypes = card.getSubtype(game); - subtypes.add(SubType.AURA); + if (!subtypes.contains(SubType.AURA)) { // do it only once + subtypes.add(SubType.AURA); + } if (subtypes.contains(subtype)) { return true; } From 5fae3ed7974544b2c1dd087e0da499cab66f058e Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 17:56:57 +0100 Subject: [PATCH 07/18] Fixed Alexi targetting bug (fixes #4197) --- Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java b/Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java index 3f55ec6cdb..e7e4824458 100644 --- a/Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java +++ b/Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java @@ -40,6 +40,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; +import mage.constants.TargetAdjustment; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.StaticFilters; @@ -65,6 +66,7 @@ public class AlexiZephyrMage extends CardImpl { ability.addCost(new TapSourceCost()); ability.addCost(new DiscardTargetCost(new TargetCardInHand(2, new FilterCard("two cards")))); ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_CREATURES)); + ability.setTargetAdjustment(TargetAdjustment.X_TARGETS); this.addAbility(ability); } From 142a2e6fbf995f6b7d44d285b075e3d764f8373f Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 17:58:17 +0100 Subject: [PATCH 08/18] Added missing target --- Mage.Sets/src/mage/cards/o/OrcishSpy.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/cards/o/OrcishSpy.java b/Mage.Sets/src/mage/cards/o/OrcishSpy.java index 77d23fe9b9..04cd9b87d5 100644 --- a/Mage.Sets/src/mage/cards/o/OrcishSpy.java +++ b/Mage.Sets/src/mage/cards/o/OrcishSpy.java @@ -54,6 +54,7 @@ public class OrcishSpy extends CardImpl { // {T}: Look at the top three cards of target player's library. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LookLibraryTopCardTargetPlayerEffect(3), new TapSourceCost()); + ability.addTarget(new TargetPlayer()); this.addAbility(ability); } From d14f8d7fbd250eca7a336e37d5fbe41a05fd269f Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 18:29:49 +0100 Subject: [PATCH 09/18] Text fix --- Mage.Sets/src/mage/cards/d/DazzlingBeauty.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/cards/d/DazzlingBeauty.java b/Mage.Sets/src/mage/cards/d/DazzlingBeauty.java index 02cbf8dcfd..0c82a483f8 100644 --- a/Mage.Sets/src/mage/cards/d/DazzlingBeauty.java +++ b/Mage.Sets/src/mage/cards/d/DazzlingBeauty.java @@ -110,6 +110,7 @@ class DazzlingBeautyEffect extends OneShotEffect { CombatGroup combatGroup = game.getCombat().findGroup(permanent.getId()); if (combatGroup != null) { combatGroup.setBlocked(true); + game.informPlayers(permanent.getLogName() + " has become blocked"); return true; } } From 3feca37d71fd4fd238cd16f8daf9ee44a4a5df8b Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 18:30:08 +0100 Subject: [PATCH 10/18] Text fix --- Mage.Sets/src/mage/cards/c/CurtainOfLight.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/cards/c/CurtainOfLight.java b/Mage.Sets/src/mage/cards/c/CurtainOfLight.java index f7a762d49e..bc3bcacfe8 100644 --- a/Mage.Sets/src/mage/cards/c/CurtainOfLight.java +++ b/Mage.Sets/src/mage/cards/c/CurtainOfLight.java @@ -109,6 +109,7 @@ class CurtainOfLightEffect extends OneShotEffect { CombatGroup combatGroup = game.getCombat().findGroup(permanent.getId()); if (combatGroup != null) { combatGroup.setBlocked(true); + game.informPlayers(permanent.getLogName() + " has become blocked"); return true; } } From 72cadf286c2861ac00fd63673f7ed19c93b26ad1 Mon Sep 17 00:00:00 2001 From: Zzooouhh Date: Fri, 24 Nov 2017 18:51:42 +0100 Subject: [PATCH 11/18] Added missing symbol --- Mage.Sets/src/mage/cards/o/OrcishSpy.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/cards/o/OrcishSpy.java b/Mage.Sets/src/mage/cards/o/OrcishSpy.java index 04cd9b87d5..c61b09b45b 100644 --- a/Mage.Sets/src/mage/cards/o/OrcishSpy.java +++ b/Mage.Sets/src/mage/cards/o/OrcishSpy.java @@ -38,6 +38,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; +import mage.target.TargetPlayer; /** * From 00eb4810048f7356defe26e3597e19c391284ff9 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sat, 25 Nov 2017 22:14:20 +1100 Subject: [PATCH 12/18] spjspj - Add a version of Freeform Commander (any creature or legendary permanent can be commander, no ban list) --- .../java/mage/client/table/TablesPanel.java | 2 +- .../mage/card/arcane/ModernCardRenderer.java | 32 ++-- .../src/mage/deck/FreeformCommander.java | 152 ++++++++++++++++++ .../pom.xml | 49 ++++++ .../game/FreeformCommanderFreeForAll.java | 77 +++++++++ .../FreeformCommanderFreeForAllMatch.java | 55 +++++++ .../game/FreeformCommanderFreeForAllType.java | 58 +++++++ Mage.Server.Plugins/pom.xml | 1 + Mage.Server/config/config.xml | 2 + Mage.Server/pom.xml | 7 + Mage.Server/release/config/config.xml | 2 + 11 files changed, 420 insertions(+), 17 deletions(-) create mode 100644 Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/FreeformCommander.java create mode 100644 Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml create mode 100644 Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAll.java create mode 100644 Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAllMatch.java create mode 100644 Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAllType.java diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index 4c50b6c8b3..e473497f6d 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -560,7 +560,7 @@ public class TablesPanel extends javax.swing.JPanel { formatFilterList.add(RowFilter.regexFilter("^Constructed - Vintage", TableTableModel.COLUMN_DECK_TYPE)); } if (btnFormatCommander.isSelected()) { - formatFilterList.add(RowFilter.regexFilter("^Commander|^Duel Commander|^Penny Dreadful Commander", TableTableModel.COLUMN_DECK_TYPE)); + formatFilterList.add(RowFilter.regexFilter("^Commander|^Duel Commander|^Penny Dreadful Commander|^Freeform Commander", TableTableModel.COLUMN_DECK_TYPE)); } if (btnFormatTinyLeader.isSelected()) { formatFilterList.add(RowFilter.regexFilter("^Tiny", TableTableModel.COLUMN_DECK_TYPE)); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index e603ac6d1e..312d232683 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -106,7 +106,7 @@ public class ModernCardRenderer extends CardRenderer { public static final Color BORDER_RED = new Color(201, 71, 58); public static final Color BORDER_GREEN = new Color(4, 136, 69); public static final Color BORDER_GOLD = new Color(255, 228, 124); - public static final Color BORDER_COLORLESS = new Color(238, 242, 242); + public static final Color BORDER_COLORLESS = new Color(208, 212, 212); public static final Color BORDER_LAND = new Color(190, 173, 115); public static final Color BOX_WHITE = new Color(244, 245, 239); @@ -115,7 +115,7 @@ public class ModernCardRenderer extends CardRenderer { public static final Color BOX_RED = new Color(246, 208, 185); public static final Color BOX_GREEN = new Color(205, 221, 213); public static final Color BOX_GOLD = new Color(223, 195, 136); - public static final Color BOX_COLORLESS = new Color(220, 228, 232); + public static final Color BOX_COLORLESS = new Color(200, 208, 212); public static final Color BOX_LAND = new Color(220, 215, 213); public static final Color BOX_INVENTION = new Color(209, 97, 33); public static final Color BOX_VEHICLE = new Color(155, 105, 60); @@ -128,21 +128,21 @@ public class ModernCardRenderer extends CardRenderer { public static final Color BOX_GOLD_NIGHT = new Color(171, 134, 70); public static final Color BOX_COLORLESS_NIGHT = new Color(118, 147, 158); - public static final Color LAND_TEXTBOX_WHITE = new Color(248, 232, 188, 244); - public static final Color LAND_TEXTBOX_BLUE = new Color(189, 212, 236, 244); - public static final Color LAND_TEXTBOX_BLACK = new Color(174, 164, 162, 244); - public static final Color LAND_TEXTBOX_RED = new Color(242, 168, 133, 244); - public static final Color LAND_TEXTBOX_GREEN = new Color(198, 220, 198, 244); - public static final Color LAND_TEXTBOX_GOLD = new Color(236, 229, 207, 244); + public static final Color LAND_TEXTBOX_WHITE = new Color(248, 232, 188, 234); + public static final Color LAND_TEXTBOX_BLUE = new Color(189, 212, 236, 234); + public static final Color LAND_TEXTBOX_BLACK = new Color(174, 164, 162, 234); + public static final Color LAND_TEXTBOX_RED = new Color(242, 168, 133, 234); + public static final Color LAND_TEXTBOX_GREEN = new Color(198, 220, 198, 234); + public static final Color LAND_TEXTBOX_GOLD = new Color(236, 229, 207, 234); - public static final Color TEXTBOX_WHITE = new Color(252, 249, 244, 244); - public static final Color TEXTBOX_BLUE = new Color(229, 238, 247, 244); - public static final Color TEXTBOX_BLACK = new Color(241, 241, 240, 244); - public static final Color TEXTBOX_RED = new Color(243, 224, 217, 244); - public static final Color TEXTBOX_GREEN = new Color(217, 232, 223, 244); - public static final Color TEXTBOX_GOLD = new Color(240, 234, 209, 244); - public static final Color TEXTBOX_COLORLESS = new Color(219, 229, 233, 244); - public static final Color TEXTBOX_LAND = new Color(218, 214, 212, 244); + public static final Color TEXTBOX_WHITE = new Color(252, 249, 244, 234); + public static final Color TEXTBOX_BLUE = new Color(229, 238, 247, 234); + public static final Color TEXTBOX_BLACK = new Color(241, 241, 240, 234); + public static final Color TEXTBOX_RED = new Color(243, 224, 217, 234); + public static final Color TEXTBOX_GREEN = new Color(217, 232, 223, 234); + public static final Color TEXTBOX_GOLD = new Color(240, 234, 209, 234); + public static final Color TEXTBOX_COLORLESS = new Color(199, 209, 213, 234); + public static final Color TEXTBOX_LAND = new Color(218, 214, 212, 234); public static final Color ERROR_COLOR = new Color(255, 0, 255); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/FreeformCommander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/FreeformCommander.java new file mode 100644 index 0000000000..570be2a80e --- /dev/null +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/FreeformCommander.java @@ -0,0 +1,152 @@ +/* + * Copyright 2011 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.deck; + +import java.util.*; +import java.util.Map.Entry; +import mage.abilities.common.CanBeYourCommanderAbility; +import mage.abilities.keyword.PartnerAbility; +import mage.cards.Card; +import mage.cards.ExpansionSet; +import mage.cards.Sets; +import mage.cards.decks.Constructed; +import mage.cards.decks.Deck; +import mage.constants.SetType; +import mage.filter.FilterMana; + +/** + * + * @author spjspj + */ +public class FreeformCommander extends Constructed { + + protected List bannedCommander = new ArrayList<>(); + private static final Map pdAllowed = new HashMap<>(); + private static boolean setupAllowed = false; + + public FreeformCommander() { + this("Freeform Commander"); + for (ExpansionSet set : Sets.getInstance().values()) { + setCodes.add(set.getCode()); + } + } + + public FreeformCommander(String name) { + super(name); + } + + @Override + public boolean validate(Deck deck) { + boolean valid = true; + FilterMana colorIdentity = new FilterMana(); + + if (deck.getCards().size() + deck.getSideboard().size() != 100) { + invalid.put("Deck", "Must contain 100 cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards"); + valid = false; + } + + List basicLandNames = new ArrayList<>(Arrays.asList("Forest", "Island", "Mountain", "Swamp", "Plains", "Wastes")); + Map counts = new HashMap<>(); + countCards(counts, deck.getCards()); + countCards(counts, deck.getSideboard()); + + for (Map.Entry entry : counts.entrySet()) { + if (entry.getValue() > 1) { + if (!basicLandNames.contains(entry.getKey())) { + invalid.put(entry.getKey(), "Too many: " + entry.getValue()); + valid = false; + } + } + } + + generateFreeformHash(); + + if (deck.getSideboard().size() < 1 || deck.getSideboard().size() > 2) { + invalid.put("Commander", "Sideboard must contain only the commander(s)"); + valid = false; + } else { + for (Card commander : deck.getSideboard()) { + if (!(commander.isCreature() || + commander.isLegendary())) { + invalid.put("Commander", "For Freeform Commander, the commander must be a creature or be legendary. Yours was: " + commander.getName()); + valid = false; + } + if (deck.getSideboard().size() == 2 && !commander.getAbilities().contains(PartnerAbility.getInstance())) { + invalid.put("Commander", "Commander without Partner (" + commander.getName() + ')'); + valid = false; + } + FilterMana commanderColor = commander.getColorIdentity(); + if (commanderColor.isWhite()) { + colorIdentity.setWhite(true); + } + if (commanderColor.isBlue()) { + colorIdentity.setBlue(true); + } + if (commanderColor.isBlack()) { + colorIdentity.setBlack(true); + } + if (commanderColor.isRed()) { + colorIdentity.setRed(true); + } + if (commanderColor.isGreen()) { + colorIdentity.setGreen(true); + } + } + } + + for (Card card : deck.getCards()) { + if (!cardHasValidColor(colorIdentity, card)) { + invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')'); + valid = false; + } + } + + for (Card card : deck.getSideboard()) { + if (!isSetAllowed(card.getExpansionSetCode())) { + if (!legalSets(card)) { + invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode()); + valid = false; + } + } + } + return valid; + } + + public boolean cardHasValidColor(FilterMana commander, Card card) { + FilterMana cardColor = card.getColorIdentity(); + return !(cardColor.isBlack() && !commander.isBlack() + || cardColor.isBlue() && !commander.isBlue() + || cardColor.isGreen() && !commander.isGreen() + || cardColor.isRed() && !commander.isRed() + || cardColor.isWhite() && !commander.isWhite()); + } + + public void generateFreeformHash() { + return; + } +} diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml new file mode 100644 index 0000000000..670a427de0 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + + org.mage + mage-server-plugins + 1.4.28 + + + mage-game-freeformcommanderfreeforall + jar + Mage Game Freeform Commander Free For All + + + + ${project.groupId} + mage + ${project.version} + + + + + src + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + maven-resources-plugin + + UTF-8 + + + + + + mage-game-freeforall + + + + + diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAll.java b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAll.java new file mode 100644 index 0000000000..e6e1aa0607 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAll.java @@ -0,0 +1,77 @@ +/* + * 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; + +import java.util.UUID; +import mage.constants.MultiplayerAttackOption; +import mage.constants.RangeOfInfluence; +import mage.game.match.MatchType; + +/** + * + * @author spjspj + */ +public class FreeformCommanderFreeForAll extends GameCommanderImpl { + + private int numPlayers; + + public FreeformCommanderFreeForAll(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { + super(attackOption, range, freeMulligans, startLife); + } + + public FreeformCommanderFreeForAll(final FreeformCommanderFreeForAll game) { + super(game); + this.numPlayers = game.numPlayers; + } + + @Override + protected void init(UUID choosingPlayerId) { + startingPlayerSkipsDraw = false; + super.init(choosingPlayerId); + } + + @Override + public MatchType getGameType() { + return new FreeformCommanderFreeForAllType(); + } + + @Override + public int getNumPlayers() { + return numPlayers; + } + + public void setNumPlayers(int numPlayers) { + this.numPlayers = numPlayers; + } + + @Override + public FreeformCommanderFreeForAll copy() { + return new FreeformCommanderFreeForAll(this); + } +} diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAllMatch.java b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAllMatch.java new file mode 100644 index 0000000000..71f2e24243 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAllMatch.java @@ -0,0 +1,55 @@ +/* + * 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; + +import mage.game.match.MatchImpl; +import mage.game.match.MatchOptions; + +/** + * + * @author spjspj + */ +public class FreeformCommanderFreeForAllMatch extends MatchImpl { + + public FreeformCommanderFreeForAllMatch(MatchOptions options) { + super(options); + } + + @Override + public void startGame() throws GameException { + int startLife = 40; + boolean alsoHand = true; + FreeformCommanderFreeForAll game = new FreeformCommanderFreeForAll(options.getAttackOption(), options.getRange(), options.getFreeMulligans(), startLife); + game.setStartMessage(this.createGameStartMessage()); + game.setAlsoHand(alsoHand); + game.setAlsoLibrary(true); + initGame(game); + games.add(game); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAllType.java b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAllType.java new file mode 100644 index 0000000000..27967352de --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/src/mage/game/FreeformCommanderFreeForAllType.java @@ -0,0 +1,58 @@ +/* + * 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; + +import mage.game.match.MatchType; + + +/** + * + * @author spjspj + */ +public class FreeformCommanderFreeForAllType extends MatchType { + + public FreeformCommanderFreeForAllType() { + this.name = "Freeform Commander Free For All"; + this.maxPlayers = 10; + this.minPlayers = 3; + this.numTeams = 0; + this.useAttackOption = true; + this.useRange = true; + this.sideboardingAllowed = false; + } + + protected FreeformCommanderFreeForAllType(final FreeformCommanderFreeForAllType matchType) { + super(matchType); + } + + @Override + public FreeformCommanderFreeForAllType copy() { + return new FreeformCommanderFreeForAllType(this); + } +} diff --git a/Mage.Server.Plugins/pom.xml b/Mage.Server.Plugins/pom.xml index 6088bbe80d..b244b900b5 100644 --- a/Mage.Server.Plugins/pom.xml +++ b/Mage.Server.Plugins/pom.xml @@ -25,6 +25,7 @@ Mage.Game.TinyLeadersDuel Mage.Game.CanadianHighlanderDuel Mage.Game.PennyDreadfulCommanderFreeForAll + Mage.Game.FreeformCommanderFreeForAll Mage.Game.TwoPlayerDuel Mage.Player.AI Mage.Player.AIMinimax diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index ba16d20cc3..bfb4e4436c 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -76,6 +76,7 @@ + @@ -151,6 +152,7 @@ + diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index 364875c081..9a04035ad2 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -154,6 +154,13 @@ ${project.version} runtime + + ${project.groupId} + mage-game-freeformcommanderfreeforall + ${project.version} + runtime + + ${project.groupId} mage-game-momirduel diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml index 45b1ed1e2f..b416ca297a 100644 --- a/Mage.Server/release/config/config.xml +++ b/Mage.Server/release/config/config.xml @@ -73,6 +73,7 @@ + @@ -147,6 +148,7 @@ + From 44b9d899740346b719dc2e45700874521fd088fc Mon Sep 17 00:00:00 2001 From: spjspj Date: Sat, 25 Nov 2017 22:35:47 +1100 Subject: [PATCH 13/18] spjspj - Add a version of Freeform Commander (any creature or legendary permanent can be commander, no ban list) --- .../Mage.Game.FreeformCommanderFreeForAll/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml index 670a427de0..a576e9c1e7 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml @@ -6,7 +6,7 @@ org.mage mage-server-plugins - 1.4.28 + 1.4.26 mage-game-freeformcommanderfreeforall From 46db9973c961f280aeb439f786fd25ca09629ac4 Mon Sep 17 00:00:00 2001 From: spjspj Date: Sat, 25 Nov 2017 22:36:56 +1100 Subject: [PATCH 14/18] spjspj - Add a version of Freeform Commander (any creature or legendary permanent can be commander, no ban list) --- .../Mage.Game.FreeformCommanderFreeForAll/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml index a576e9c1e7..8dc87f89fb 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml @@ -19,8 +19,8 @@ mage ${project.version} - - + + src From 603e625d8db58c494c61f740fe4e9f5ab31be57d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 25 Nov 2017 12:55:26 +0100 Subject: [PATCH 15/18] Fixed a problem with AI opponent selection, --- .../src/main/java/mage/player/ai/ComputerPlayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 84253863c6..b8515e19bf 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -2250,7 +2250,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { * @return */ private UUID getRandomOpponent(UUID abilityControllerId, Game game) { - UUID randomOpponentId = game.getOpponents(abilityControllerId).iterator().next(); + UUID randomOpponentId = null; Set opponents = game.getOpponents(abilityControllerId); if (opponents.size() > 1) { int rand = RandomUtil.nextInt(opponents.size()); From 830874bc502fca131a1cea8343cfd5e37dcff2fe Mon Sep 17 00:00:00 2001 From: spjspj Date: Sun, 26 Nov 2017 01:15:24 +1100 Subject: [PATCH 16/18] Fix exception --- .../main/java/org/mage/card/arcane/CardRendererUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index e7a421def3..eca040234f 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -74,9 +74,9 @@ public final class CardRendererUtils { int b = c.getBlue(); int alpha = c.getAlpha(); - int plus_r = (int) ((255 - r) / 2); - int plus_g = (int) ((255 - g) / 2); - int plus_b = (int) ((255 - b) / 2); + int plus_r = (int) (Math.min (255 - r, r) / 2); + int plus_g = (int) (Math.min (255 - g, g) / 2); + int plus_b = (int) (Math.min (255 - b, b) / 2); return new Color(r - plus_r, g - plus_g, From 82705d51b404ed4130d5ec1b36e163e4f9b47090 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sat, 25 Nov 2017 11:12:11 -0500 Subject: [PATCH 17/18] Implemented Deepwood Elder --- Mage.Sets/src/mage/cards/d/DeepwoodElder.java | 117 ++++++++++++++++++ Mage.Sets/src/mage/sets/MercadianMasques.java | 1 + 2 files changed, 118 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DeepwoodElder.java diff --git a/Mage.Sets/src/mage/cards/d/DeepwoodElder.java b/Mage.Sets/src/mage/cards/d/DeepwoodElder.java new file mode 100644 index 0000000000..14b5283995 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeepwoodElder.java @@ -0,0 +1,117 @@ +/* + * 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.cards.d; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetAdjustment; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author TheElk801 + */ +public class DeepwoodElder extends CardImpl { + + public DeepwoodElder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{G}"); + + this.subtype.add(SubType.DRYAD); + this.subtype.add(SubType.SPELLSHAPER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {X}{G}{G}, {tap}, Discard a card: X target lands become Forests until end of turn. + Ability ability = new SimpleActivatedAbility(new DeepwoodElderEffect(), new ManaCostsImpl("{X}{G}{G}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new DiscardCardCost()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_LANDS)); + ability.setTargetAdjustment(TargetAdjustment.X_TARGETS); + this.addAbility(ability); + } + + public DeepwoodElder(final DeepwoodElder card) { + super(card); + } + + @Override + public DeepwoodElder copy() { + return new DeepwoodElder(this); + } +} + +class DeepwoodElderEffect extends OneShotEffect { + + DeepwoodElderEffect() { + super(Outcome.LoseAbility); + this.staticText = "X target lands become Forests until end of turn"; + } + + DeepwoodElderEffect(final DeepwoodElderEffect effect) { + super(effect); + } + + @Override + public DeepwoodElderEffect copy() { + return new DeepwoodElderEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Target target : source.getTargets()) { + for (UUID targetId : target.getTargets()) { + Permanent permanent = game.getPermanent(targetId); + if (permanent != null) { + ContinuousEffect effect = new BecomesBasicLandTargetEffect(Duration.EndOfTurn, SubType.FOREST); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); + } + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/MercadianMasques.java b/Mage.Sets/src/mage/sets/MercadianMasques.java index ad0e9aa438..1a4b56768b 100644 --- a/Mage.Sets/src/mage/sets/MercadianMasques.java +++ b/Mage.Sets/src/mage/sets/MercadianMasques.java @@ -123,6 +123,7 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Deadly Insect", 238, Rarity.COMMON, mage.cards.d.DeadlyInsect.class)); cards.add(new SetCardInfo("Deathgazer", 130, Rarity.UNCOMMON, mage.cards.d.Deathgazer.class)); cards.add(new SetCardInfo("Deepwood Drummer", 239, Rarity.COMMON, mage.cards.d.DeepwoodDrummer.class)); + cards.add(new SetCardInfo("Deepwood Elder", 240, Rarity.RARE, mage.cards.d.DeepwoodElder.class)); cards.add(new SetCardInfo("Deepwood Ghoul", 131, Rarity.COMMON, mage.cards.d.DeepwoodGhoul.class)); cards.add(new SetCardInfo("Deepwood Legate", 132, Rarity.UNCOMMON, mage.cards.d.DeepwoodLegate.class)); cards.add(new SetCardInfo("Deepwood Tantiv", 241, Rarity.UNCOMMON, mage.cards.d.DeepwoodTantiv.class)); From ed123948aebffd5c2d8ad964df6ab08f79b82f05 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sat, 25 Nov 2017 13:48:39 -0500 Subject: [PATCH 18/18] Implemented Skyshroud War Beast --- .../src/mage/cards/s/SkyshroudWarBeast.java | 120 ++++++++++++++++++ Mage.Sets/src/mage/sets/Exodus.java | 1 + 2 files changed, 121 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java diff --git a/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java b/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java new file mode 100644 index 0000000000..25705180cd --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java @@ -0,0 +1,120 @@ +/* + * 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.cards.s; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.ChooseOpponentEffect; +import mage.constants.SubType; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.Zone; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author TheElk801 + */ +public class SkyshroudWarBeast extends CardImpl { + + public SkyshroudWarBeast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // As Skyshroud War Beast enters the battlefield, choose an opponent. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseOpponentEffect(Outcome.BoostCreature))); + + // Skyshroud War Beast's power and toughness are each equal to the number of nonbasic lands the chosen player controls. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SkyshroudWarBeastEffect())); + } + + public SkyshroudWarBeast(final SkyshroudWarBeast card) { + super(card); + } + + @Override + public SkyshroudWarBeast copy() { + return new SkyshroudWarBeast(this); + } +} + +class SkyshroudWarBeastEffect extends ContinuousEffectImpl { + + public SkyshroudWarBeastEffect() { + super(Duration.EndOfGame, Layer.PTChangingEffects_7, SubLayer.CharacteristicDefining_7a, Outcome.BoostCreature); + staticText = "{this}'s power and toughness are each equal to the number of nonbasic lands the chosen player controls"; + } + + public SkyshroudWarBeastEffect(final SkyshroudWarBeastEffect effect) { + super(effect); + } + + @Override + public SkyshroudWarBeastEffect copy() { + return new SkyshroudWarBeastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + MageObject target = game.getObject(source.getSourceId()); + if (target != null) { + UUID playerId = (UUID) game.getState().getValue(source.getSourceId().toString() + ChooseOpponentEffect.VALUE_KEY); + FilterLandPermanent filter = new FilterLandPermanent(); + filter.add(new ControllerIdPredicate(playerId)); + int number = new PermanentsOnBattlefieldCount(filter).calculate(game, source, this); + target.getPower().setValue(number); + target.getToughness().setValue(number); + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Exodus.java b/Mage.Sets/src/mage/sets/Exodus.java index 59c6cd1644..2f2ad4e4fd 100644 --- a/Mage.Sets/src/mage/sets/Exodus.java +++ b/Mage.Sets/src/mage/sets/Exodus.java @@ -150,6 +150,7 @@ public class Exodus extends ExpansionSet { cards.add(new SetCardInfo("Shield Mate", 19, Rarity.COMMON, mage.cards.s.ShieldMate.class)); cards.add(new SetCardInfo("Skyshaper", 137, Rarity.UNCOMMON, mage.cards.s.Skyshaper.class)); cards.add(new SetCardInfo("Skyshroud Elite", 123, Rarity.UNCOMMON, mage.cards.s.SkyshroudElite.class)); + cards.add(new SetCardInfo("Skyshroud War Beast", 124, Rarity.RARE, mage.cards.s.SkyshroudWarBeast.class)); cards.add(new SetCardInfo("Slaughter", 74, Rarity.UNCOMMON, mage.cards.s.Slaughter.class)); cards.add(new SetCardInfo("Soltari Visionary", 20, Rarity.COMMON, mage.cards.s.SoltariVisionary.class)); cards.add(new SetCardInfo("Song of Serenity", 125, Rarity.UNCOMMON, mage.cards.s.SongOfSerenity.class));