diff --git a/Mage.Client/src/main/java/mage/client/cards/Cards.java b/Mage.Client/src/main/java/mage/client/cards/Cards.java index 8387066fe3..831a53e0c1 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Cards.java +++ b/Mage.Client/src/main/java/mage/client/cards/Cards.java @@ -310,7 +310,7 @@ public class Cards extends javax.swing.JPanel { Collections.sort(cards, new Comparator() { @Override public int compare(CardPanel cp1, CardPanel cp2) { - return Integer.valueOf(cp1.getLocation().x).compareTo(Integer.valueOf(cp2.getLocation().x)); + return Integer.valueOf(cp1.getLocation().x).compareTo(cp2.getLocation().x); } }); diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index f2358331cb..e4e31cdd70 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -27,47 +27,6 @@ */ package mage.client.game; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.ComponentListener; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import javax.swing.AbstractAction; -import javax.swing.GroupLayout; -import javax.swing.GroupLayout.Alignment; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JLayeredPane; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.KeyStroke; -import javax.swing.SwingWorker; -import javax.swing.border.LineBorder; -import javax.swing.plaf.basic.BasicSplitPaneDivider; -import javax.swing.plaf.basic.BasicSplitPaneUI; import mage.cards.Card; import mage.cards.action.ActionCallback; import mage.client.MageFrame; @@ -78,12 +37,7 @@ import mage.client.components.HoverButton; import mage.client.components.MageComponents; import mage.client.components.ext.dlg.DialogManager; import mage.client.components.layout.RelativeLayout; -import mage.client.dialog.ExileZoneDialog; -import mage.client.dialog.PickChoiceDialog; -import mage.client.dialog.PickNumberDialog; -import mage.client.dialog.PickPileDialog; -import mage.client.dialog.PreferencesDialog; -import mage.client.dialog.ShowCardsDialog; +import mage.client.dialog.*; import mage.client.game.FeedbackPanel.FeedbackMode; import mage.client.plugins.adapters.MageActionCallback; import mage.client.plugins.impl.Plugins; @@ -94,29 +48,23 @@ import mage.client.util.PhaseManager; import mage.client.util.gui.ArrowBuilder; import mage.constants.EnlargeMode; import mage.constants.PhaseStep; -import static mage.constants.PhaseStep.BEGIN_COMBAT; -import static mage.constants.PhaseStep.COMBAT_DAMAGE; -import static mage.constants.PhaseStep.DECLARE_ATTACKERS; -import static mage.constants.PhaseStep.DECLARE_BLOCKERS; -import static mage.constants.PhaseStep.DRAW; -import static mage.constants.PhaseStep.END_COMBAT; -import static mage.constants.PhaseStep.END_TURN; -import static mage.constants.PhaseStep.FIRST_COMBAT_DAMAGE; -import static mage.constants.PhaseStep.UNTAP; -import static mage.constants.PhaseStep.UPKEEP; import mage.remote.Session; -import mage.view.AbilityPickerView; -import mage.view.CardsView; -import mage.view.ExileView; -import mage.view.GameView; -import mage.view.LookedAtView; -import mage.view.MatchView; -import mage.view.PlayerView; -import mage.view.RevealedView; -import mage.view.SimpleCardsView; +import mage.view.*; import org.apache.log4j.Logger; import org.mage.plugins.card.utils.impl.ImageManagerImpl; +import javax.swing.*; +import javax.swing.GroupLayout.Alignment; +import javax.swing.border.LineBorder; +import javax.swing.plaf.basic.BasicSplitPaneDivider; +import javax.swing.plaf.basic.BasicSplitPaneUI; +import java.awt.*; +import java.awt.event.*; +import java.io.Serializable; +import java.util.*; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; + /** * * @author BetaSteward_at_googlemail.com, nantuko8 @@ -503,6 +451,15 @@ public final class GamePanel extends javax.swing.JPanel { handCards.clear(); handCards.put(YOUR_HAND, CardsViewUtil.convertSimple(game.getHand(), loadedCards)); + // Mark playable + if (game.getCanPlayInHand() != null) { + for (CardView card : handCards.get(YOUR_HAND).values()) { + if (game.getCanPlayInHand().contains(card.getId())) { + card.setPlayable(true); + } + } + } + // Get opponents hand cards if available if (game.getOpponentHands() != null) { for (Map.Entry hand: game.getOpponentHands().entrySet()) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java index 6488527386..2ac7f7f54b 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java @@ -80,6 +80,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti public int cardXOffset, cardYOffset, cardWidth, cardHeight; private boolean isSelected; + private boolean isPlayable; private boolean showCastingCost; private boolean hasImage = false; private float alpha = 1.0f; @@ -420,6 +421,11 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); } + if (isPlayable) { + g2d.setColor(new Color(250, 250, 0, 200)); + g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); + } + //TODO:uncomment /* if (gameCard.isAttacking()) { @@ -708,6 +714,8 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti } setText(card); + this.isPlayable = card.isPlayable(); + boolean updateImage = !gameCard.getName().equals(card.getName()) || gameCard.isFaceDown() != card.isFaceDown(); // update after e.g. turning a night/day card this.gameCard = card; diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index a555a2ce6a..513bfe14a1 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -28,17 +28,11 @@ package mage.view; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageObject; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.Modes; import mage.abilities.SpellAbility; -import mage.abilities.common.TurnFaceUpAbility; import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.keyword.MorphAbility; import mage.cards.Card; import mage.cards.SplitCard; import mage.constants.CardType; @@ -49,7 +43,6 @@ import mage.counters.Counter; import mage.counters.CounterType; import mage.game.command.Emblem; import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentToken; import mage.game.permanent.token.Token; import mage.game.stack.Spell; @@ -57,6 +50,10 @@ import mage.game.stack.StackAbility; import mage.target.Target; import mage.target.Targets; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** * @author BetaSteward_at_googlemail.com */ @@ -114,6 +111,8 @@ public class CardView extends SimpleCardView { protected boolean rotate; protected boolean hideInfo; // controlls if the tooltip window is shown (eg. controlled face down morph card) + protected boolean isPlayable; + public CardView(Card card) { this(card, null, false); } @@ -673,5 +672,12 @@ public class CardView extends SimpleCardView { public boolean hideInfo() { return hideInfo; } - + + public boolean isPlayable() { + return isPlayable; + } + + public void setPlayable(boolean isPlayable) { + this.isPlayable = isPlayable; + } } diff --git a/Mage.Common/src/mage/view/GameView.java b/Mage.Common/src/mage/view/GameView.java index 8e833eb459..bdf6779a6b 100644 --- a/Mage.Common/src/mage/view/GameView.java +++ b/Mage.Common/src/mage/view/GameView.java @@ -28,11 +28,6 @@ package mage.view; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; import mage.MageObject; import mage.abilities.costs.Cost; import mage.cards.Card; @@ -51,6 +46,9 @@ import mage.game.stack.StackAbility; import mage.game.stack.StackObject; import mage.players.Player; +import java.io.Serializable; +import java.util.*; + /** * @@ -62,6 +60,7 @@ public class GameView implements Serializable { private final int priorityTime; private final List players = new ArrayList<>(); private SimpleCardsView hand; + private Set canPlayInHand; private Map opponentHands; private final CardsView stack = new CardsView(); private final List exiles = new ArrayList<>(); @@ -280,4 +279,11 @@ public class GameView implements Serializable { return isPlayer; } + public Set getCanPlayInHand() { + return canPlayInHand; + } + + public void setCanPlayInHand(Set canPlayInHand) { + this.canPlayInHand = canPlayInHand; + } } diff --git a/Mage.Server/src/main/java/mage/server/game/GameSession.java b/Mage.Server/src/main/java/mage/server/game/GameSession.java index fd59bb8c32..63d9f13cc4 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameSession.java +++ b/Mage.Server/src/main/java/mage/server/game/GameSession.java @@ -28,17 +28,6 @@ package mage.server.game; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import mage.cards.Cards; import mage.constants.ManaType; import mage.game.Game; @@ -50,14 +39,16 @@ import mage.server.User; import mage.server.UserManager; import mage.server.util.ConfigSettings; import mage.server.util.ThreadExecutor; -import mage.view.AbilityPickerView; -import mage.view.CardsView; -import mage.view.GameClientMessage; -import mage.view.GameView; -import mage.view.LookedAtView; -import mage.view.SimpleCardsView; +import mage.view.*; import org.apache.log4j.Logger; +import java.io.Serializable; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + /** * * @author BetaSteward_at_googlemail.com @@ -245,15 +236,9 @@ public class GameSession extends GameWatcher { player.setUserData(this.userData); GameView gameView = new GameView(game.getState(), game, playerId); gameView.setHand(new SimpleCardsView(player.getHand().getCards(game))); + gameView.setCanPlayInHand(player.getPlayableInHand(game)); - if (player.getPlayersUnderYourControl().size() > 0) { - Map handCards = new HashMap<>(); - for (UUID controlledPlayerId : player.getPlayersUnderYourControl()) { - Player opponent = game.getPlayer(controlledPlayerId); - handCards.put(opponent.getName(), new SimpleCardsView(opponent.getHand().getCards(game))); - } - gameView.setOpponentHands(handCards); - } + processControlledPlayers(player, gameView); //TODO: should player who controls another player's turn be able to look at all these cards? @@ -267,6 +252,17 @@ public class GameSession extends GameWatcher { return gameView; } + private void processControlledPlayers(Player player, GameView gameView) { + if (player.getPlayersUnderYourControl().size() > 0) { + Map handCards = new HashMap<>(); + for (UUID controlledPlayerId : player.getPlayersUnderYourControl()) { + Player opponent = game.getPlayer(controlledPlayerId); + handCards.put(opponent.getName(), new SimpleCardsView(opponent.getHand().getCards(game))); + } + gameView.setOpponentHands(handCards); + } + } + public void removeGame() { User user = UserManager.getInstance().getUser(userId); if (user != null) { diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index db09699d25..71b0dcddff 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -28,23 +28,6 @@ package mage.game; -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; -import java.util.Stack; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; @@ -68,13 +51,7 @@ import mage.cards.CardsImpl; import mage.cards.SplitCard; import mage.cards.decks.Deck; import mage.choices.Choice; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.MultiplayerAttackOption; -import mage.constants.Outcome; -import mage.constants.PhaseStep; -import mage.constants.RangeOfInfluence; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.Filter; import mage.filter.FilterPermanent; @@ -89,14 +66,8 @@ import mage.game.combat.Combat; import mage.game.command.CommandObject; import mage.game.command.Commander; import mage.game.command.Emblem; -import mage.game.events.DamageEvent; -import mage.game.events.GameEvent; -import mage.game.events.Listener; -import mage.game.events.PlayerQueryEvent; -import mage.game.events.PlayerQueryEventSource; -import mage.game.events.TableEvent; +import mage.game.events.*; import mage.game.events.TableEvent.EventType; -import mage.game.events.TableEventSource; import mage.game.permanent.Battlefield; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; @@ -114,14 +85,14 @@ import mage.target.Target; import mage.target.TargetPermanent; import mage.target.TargetPlayer; import mage.util.functions.ApplyToPermanent; -import mage.watchers.common.CastSpellLastTurnWatcher; -import mage.watchers.common.MiracleWatcher; -import mage.watchers.common.MorbidWatcher; -import mage.watchers.common.PlayerDamagedBySourceWatcher; -import mage.watchers.common.PlayerLostLifeWatcher; -import mage.watchers.common.SoulbondWatcher; +import mage.watchers.common.*; import org.apache.log4j.Logger; +import java.io.IOException; +import java.io.Serializable; +import java.util.*; +import java.util.Map.Entry; + public abstract class GameImpl implements Game, Serializable { private static final transient Logger logger = Logger.getLogger(GameImpl.class); @@ -1846,7 +1817,7 @@ public abstract class GameImpl implements Game, Serializable { @Override public boolean canPlaySorcery(UUID playerId) { - return getActivePlayerId().equals(playerId) && getStack().isEmpty() && isMainPhase(); + return isMainPhase() && getActivePlayerId().equals(playerId) && getStack().isEmpty(); } /** diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index 2df689ddfe..c32f3406a1 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -28,21 +28,9 @@ package mage.players; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; import mage.MageItem; import mage.MageObject; -import mage.abilities.Abilities; -import mage.abilities.Ability; -import mage.abilities.ActivatedAbility; -import mage.abilities.Mode; -import mage.abilities.Modes; -import mage.abilities.SpellAbility; -import mage.abilities.TriggeredAbility; +import mage.abilities.*; import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.ManaCost; @@ -69,6 +57,9 @@ import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; import mage.util.Copyable; +import java.io.Serializable; +import java.util.*; + /** * * @author BetaSteward_at_googlemail.com @@ -340,6 +331,8 @@ public interface Player extends MageItem, Copyable { List getPlayable(Game game, boolean hidden); List getPlayableOptions(Ability ability, Game game); + Set getPlayableInHand(Game game); + void addCounters(Counter counter, Game game); List getAttachments(); boolean addAttachment(UUID permanentId, Game game); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index dfdfb02b55..f0215aaec0 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -28,31 +28,9 @@ package mage.players; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.Mana; -import mage.abilities.Abilities; -import mage.abilities.AbilitiesImpl; -import mage.abilities.Ability; -import mage.abilities.ActivatedAbility; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.Mode; -import mage.abilities.PlayLandAbility; -import mage.abilities.SpecialAction; -import mage.abilities.SpellAbility; -import mage.abilities.TriggeredAbility; +import mage.abilities.*; import mage.abilities.common.PassAbility; import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility; import mage.abilities.costs.AdjustingSourceCosts; @@ -62,12 +40,7 @@ import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.RestrictionUntapNotMoreThanEffect; import mage.abilities.effects.common.LoseControlOnOtherPlayersControllerEffect; -import mage.abilities.keyword.FlashbackAbility; -import mage.abilities.keyword.HexproofAbility; -import mage.abilities.keyword.InfectAbility; -import mage.abilities.keyword.LifelinkAbility; -import mage.abilities.keyword.ProtectionAbility; -import mage.abilities.keyword.ShroudAbility; +import mage.abilities.keyword.*; import mage.abilities.mana.ManaAbility; import mage.abilities.mana.ManaOptions; import mage.actions.MageDrawAction; @@ -76,14 +49,7 @@ import mage.cards.Cards; import mage.cards.CardsImpl; import mage.cards.SplitCard; import mage.cards.decks.Deck; -import mage.constants.AsThoughEffectType; -import mage.constants.CardType; -import mage.constants.ManaType; -import mage.constants.Outcome; -import mage.constants.RangeOfInfluence; -import mage.constants.SpellAbilityType; -import mage.constants.TimingRule; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.Counter; import mage.counters.CounterType; import mage.counters.Counters; @@ -116,6 +82,10 @@ import mage.target.common.TargetDiscard; import mage.watchers.common.BloodthirstWatcher; import org.apache.log4j.Logger; +import java.io.Serializable; +import java.util.*; +import java.util.Map.Entry; + public abstract class PlayerImpl implements Player, Serializable { private static final transient Logger log = Logger.getLogger(PlayerImpl.class); @@ -1973,6 +1943,25 @@ public abstract class PlayerImpl implements Player, Serializable { return playable; } + @Override + public Set getPlayableInHand(Game game) { + Set playable = new HashSet<>(); + + ManaOptions available = getManaAvailable(game); + available.addMana(manaPool.getMana()); + + for (Card card: hand.getCards(game)) { + for (ActivatedAbility ability: card.getAbilities().getPlayableAbilities(Zone.HAND)) { + if (canPlay(ability, available, game)) { + playable.add(card.getId()); + break; + } + } + } + + return playable; + } + /** * Only used for AIs *