Merge pull request #8013 from weirddan455/wish

[AFR] Implemented Wish
This commit is contained in:
Oleg Agafonov 2021-07-21 20:53:41 +04:00 committed by GitHub
commit 81193148e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 673 additions and 233 deletions

View file

@ -20,7 +20,7 @@
import java.util.*; import java.util.*;
/** /**
* Panel for stack and hand zones * Panel for stack and hand zones, component for lookAt and reveal windows (CardInfoWindowDialog)
* *
* @author BetaSteward_at_googlemail.com, JayDi85 * @author BetaSteward_at_googlemail.com, JayDi85
*/ */
@ -386,4 +386,14 @@
public void setZone(Zone zone) { public void setZone(Zone zone) {
this.zone = zone; this.zone = zone;
} }
/**
* For GUI: get mage card components for update (example: change playable status)
* Warning, do not change the list
*
* @return
*/
public Map<UUID, MageCard> getMageCardsForUpdate() {
return this.cards;
}
} }

View file

@ -4,15 +4,13 @@ import java.awt.*;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.beans.PropertyVetoException; import java.beans.PropertyVetoException;
import java.util.EnumSet; import java.util.*;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent; import javax.swing.event.InternalFrameEvent;
import javax.swing.plaf.basic.BasicInternalFrameUI; import javax.swing.plaf.basic.BasicInternalFrameUI;
import mage.cards.MageCard;
import mage.client.cards.BigCard; import mage.client.cards.BigCard;
import mage.client.util.GUISizeHelper; import mage.client.util.GUISizeHelper;
import mage.client.util.ImageHelper; import mage.client.util.ImageHelper;
@ -36,7 +34,7 @@ public class CardInfoWindowDialog extends MageDialog {
private static final Logger LOGGER = Logger.getLogger(CardInfoWindowDialog.class); private static final Logger LOGGER = Logger.getLogger(CardInfoWindowDialog.class);
public enum ShowType { public enum ShowType {
REVEAL, REVEAL_TOP_LIBRARY, LOOKED_AT, EXILE, GRAVEYARD, COMPANION, OTHER REVEAL, REVEAL_TOP_LIBRARY, LOOKED_AT, EXILE, GRAVEYARD, COMPANION, SIDEBOARD, OTHER
} }
private final ShowType showType; private final ShowType showType;
@ -91,6 +89,17 @@ public class CardInfoWindowDialog extends MageDialog {
} }
}); });
break; break;
case SIDEBOARD:
this.setFrameIcon(new ImageIcon(ImageHelper.getImageFromResources("/info/library.png")));
this.setClosable(true);
this.setDefaultCloseOperation(HIDE_ON_CLOSE);
this.addInternalFrameListener(new InternalFrameAdapter() {
@Override
public void internalFrameClosing(InternalFrameEvent e) {
CardInfoWindowDialog.this.hideDialog();
}
});
break;
case EXILE: case EXILE:
this.setFrameIcon(new ImageIcon(ImageManagerImpl.instance.getExileImage())); this.setFrameIcon(new ImageIcon(ImageManagerImpl.instance.getExileImage()));
break; break;
@ -98,6 +107,7 @@ public class CardInfoWindowDialog extends MageDialog {
this.setFrameIcon(new ImageIcon(ImageManagerImpl.instance.getTokenIconImage())); this.setFrameIcon(new ImageIcon(ImageManagerImpl.instance.getTokenIconImage()));
this.setClosable(false); this.setClosable(false);
break; break;
case OTHER:
default: default:
// no icon yet // no icon yet
} }
@ -151,15 +161,35 @@ public class CardInfoWindowDialog extends MageDialog {
public void loadCards(CardsView showCards, BigCard bigCard, UUID gameId, boolean revertOrder) { public void loadCards(CardsView showCards, BigCard bigCard, UUID gameId, boolean revertOrder) {
cards.loadCards(showCards, bigCard, gameId, revertOrder); cards.loadCards(showCards, bigCard, gameId, revertOrder);
// additional info for grave windows
if (showType == ShowType.GRAVEYARD) { if (showType == ShowType.GRAVEYARD) {
int qty = qtyCardTypes(showCards); int qty = qtyCardTypes(showCards);
String titel = name + "'s Graveyard (" + showCards.size() + ") - " + qty + ((qty == 1) ? " Card Type" : " Card Types"); String newTitle = name + "'s graveyard (" + showCards.size() + ") - " + qty + ((qty == 1) ? " card type" : " card types");
setTitle(titel); setTitle(newTitle);
this.setTitelBarToolTip(titel); this.setTitelBarToolTip(newTitle);
} }
// additional info for sideboard window
if (showType == ShowType.SIDEBOARD) {
String newTitle = name + "'s sideboard";
setTitle(newTitle);
this.setTitelBarToolTip(newTitle);
}
showAndPositionWindow(); showAndPositionWindow();
} }
/**
* For GUI: get mage card components for update (example: change playable status)
* Warning, do not change the list
*
* @return
*/
public Map<UUID, MageCard> getMageCardsForUpdate() {
return this.cards.getMageCardsForUpdate();
}
@Override @Override
public void show() { public void show() {
if (showType == ShowType.EXILE) { if (showType == ShowType.EXILE) {

View file

@ -26,6 +26,7 @@ import mage.client.util.gui.ArrowBuilder;
import mage.client.util.gui.MageDialogState; import mage.client.util.gui.MageDialogState;
import mage.constants.*; import mage.constants.*;
import mage.game.events.PlayerQueryEvent; import mage.game.events.PlayerQueryEvent;
import mage.players.PlayableObjectStats;
import mage.players.PlayableObjectsList; import mage.players.PlayableObjectsList;
import mage.view.*; import mage.view.*;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -76,9 +77,11 @@ public final class GamePanel extends javax.swing.JPanel {
private final Map<UUID, CardInfoWindowDialog> exiles = new HashMap<>(); private final Map<UUID, CardInfoWindowDialog> exiles = new HashMap<>();
private final Map<String, CardInfoWindowDialog> revealed = new HashMap<>(); private final Map<String, CardInfoWindowDialog> revealed = new HashMap<>();
private final Map<String, CardInfoWindowDialog> lookedAt = new HashMap<>(); private final Map<String, CardInfoWindowDialog> lookedAt = new HashMap<>();
private final Map<String, CardsView> graveyards = new HashMap<>(); // need to sync selection
private final Map<String, CardInfoWindowDialog> graveyardWindows = new HashMap<>(); private final Map<String, CardInfoWindowDialog> graveyardWindows = new HashMap<>();
private final Map<String, CardInfoWindowDialog> companion = new HashMap<>(); private final Map<String, CardInfoWindowDialog> companion = new HashMap<>();
private final Map<String, CardsView> graveyards = new HashMap<>(); private final Map<String, CardsView> sideboards = new HashMap<>(); // need to sync selection
private final Map<String, CardInfoWindowDialog> sideboardWindows = new HashMap<>();
private final ArrayList<ShowCardsDialog> pickTarget = new ArrayList<>(); private final ArrayList<ShowCardsDialog> pickTarget = new ArrayList<>();
private final ArrayList<PickPileDialog> pickPile = new ArrayList<>(); private final ArrayList<PickPileDialog> pickPile = new ArrayList<>();
private UUID gameId; private UUID gameId;
@ -245,25 +248,29 @@ public final class GamePanel extends javax.swing.JPanel {
if (pickMultiNumber != null) { if (pickMultiNumber != null) {
pickMultiNumber.removeDialog(); pickMultiNumber.removeDialog();
} }
for (CardInfoWindowDialog exileDialog : exiles.values()) { for (CardInfoWindowDialog windowDialog : exiles.values()) {
exileDialog.cleanUp(); windowDialog.cleanUp();
exileDialog.removeDialog(); windowDialog.removeDialog();
} }
for (CardInfoWindowDialog graveyardDialog : graveyardWindows.values()) { for (CardInfoWindowDialog windowDialog : graveyardWindows.values()) {
graveyardDialog.cleanUp(); windowDialog.cleanUp();
graveyardDialog.removeDialog(); windowDialog.removeDialog();
} }
for (CardInfoWindowDialog revealDialog : revealed.values()) { for (CardInfoWindowDialog windowDialog : sideboardWindows.values()) {
revealDialog.cleanUp(); windowDialog.cleanUp();
revealDialog.removeDialog(); windowDialog.removeDialog();
} }
for (CardInfoWindowDialog lookedAtDialog : lookedAt.values()) { for (CardInfoWindowDialog windowDialog : revealed.values()) {
lookedAtDialog.cleanUp(); windowDialog.cleanUp();
lookedAtDialog.removeDialog(); windowDialog.removeDialog();
} }
for (CardInfoWindowDialog companionDialog : companion.values()) { for (CardInfoWindowDialog windowDialog : lookedAt.values()) {
companionDialog.cleanUp(); windowDialog.cleanUp();
companionDialog.removeDialog(); windowDialog.removeDialog();
}
for (CardInfoWindowDialog windowDialog : companion.values()) {
windowDialog.cleanUp();
windowDialog.removeDialog();
} }
clearPickTargetDialogs(); clearPickTargetDialogs();
@ -307,26 +314,29 @@ public final class GamePanel extends javax.swing.JPanel {
playAreaPanel.changeGUISize(); playAreaPanel.changeGUISize();
} }
for (CardInfoWindowDialog cardInfoWindowDialog : exiles.values()) { for (CardInfoWindowDialog windowDialog : exiles.values()) {
cardInfoWindowDialog.changeGUISize(); windowDialog.changeGUISize();
} }
for (CardInfoWindowDialog cardInfoWindowDialog : revealed.values()) { for (CardInfoWindowDialog windowDialog : revealed.values()) {
cardInfoWindowDialog.changeGUISize(); windowDialog.changeGUISize();
} }
for (CardInfoWindowDialog cardInfoWindowDialog : lookedAt.values()) { for (CardInfoWindowDialog windowDialog : lookedAt.values()) {
cardInfoWindowDialog.changeGUISize(); windowDialog.changeGUISize();
} }
for (CardInfoWindowDialog cardInfoWindowDialog : companion.values()) { for (CardInfoWindowDialog windowDialog : companion.values()) {
cardInfoWindowDialog.changeGUISize(); windowDialog.changeGUISize();
} }
for (CardInfoWindowDialog cardInfoWindowDialog : graveyardWindows.values()) { for (CardInfoWindowDialog windowDialog : graveyardWindows.values()) {
cardInfoWindowDialog.changeGUISize(); windowDialog.changeGUISize();
} }
for (ShowCardsDialog showCardsDialog : pickTarget) { for (CardInfoWindowDialog windowDialog : sideboardWindows.values()) {
showCardsDialog.changeGUISize(); windowDialog.changeGUISize();
} }
for (PickPileDialog pickPileDialog : pickPile) { for (ShowCardsDialog windowDialog : pickTarget) {
pickPileDialog.changeGUISize(); windowDialog.changeGUISize();
}
for (PickPileDialog windowDialog : pickPile) {
windowDialog.changeGUISize();
} }
this.revalidate(); this.revalidate();
@ -574,7 +584,7 @@ public final class GamePanel extends javax.swing.JPanel {
} }
PlayerView player = game.getPlayers().get(playerSeat); PlayerView player = game.getPlayers().get(playerSeat);
PlayAreaPanel playAreaPanel = new PlayAreaPanel(player, bigCard, gameId, game.getPriorityTime(), this, PlayAreaPanel playAreaPanel = new PlayAreaPanel(player, bigCard, gameId, game.getPriorityTime(), this,
new PlayAreaPanelOptions(game.isPlayer(), game.isPlayer(), game.isRollbackTurnsAllowed(), row == 0)); new PlayAreaPanelOptions(game.isPlayer(), player.isHuman(), game.isPlayer(), game.isRollbackTurnsAllowed(), row == 0));
players.put(player.getPlayerId(), playAreaPanel); players.put(player.getPlayerId(), playAreaPanel);
playersWhoLeft.put(player.getPlayerId(), false); playersWhoLeft.put(player.getPlayerId(), false);
GridBagConstraints c = new GridBagConstraints(); GridBagConstraints c = new GridBagConstraints();
@ -618,7 +628,7 @@ public final class GamePanel extends javax.swing.JPanel {
} }
player = game.getPlayers().get(playerNum); player = game.getPlayers().get(playerNum);
PlayAreaPanel playerPanel = new PlayAreaPanel(player, bigCard, gameId, game.getPriorityTime(), this, PlayAreaPanel playerPanel = new PlayAreaPanel(player, bigCard, gameId, game.getPriorityTime(), this,
new PlayAreaPanelOptions(game.isPlayer(), false, game.isRollbackTurnsAllowed(), row == 0)); new PlayAreaPanelOptions(game.isPlayer(), player.isHuman(), false, game.isRollbackTurnsAllowed(), row == 0));
players.put(player.getPlayerId(), playerPanel); players.put(player.getPlayerId(), playerPanel);
playersWhoLeft.put(player.getPlayerId(), false); playersWhoLeft.put(player.getPlayerId(), false);
c = new GridBagConstraints(); c = new GridBagConstraints();
@ -789,16 +799,29 @@ public final class GamePanel extends javax.swing.JPanel {
if (player.getPlayerId().equals(playerId)) { if (player.getPlayerId().equals(playerId)) {
skipButtons.updateFromPlayer(player); skipButtons.updateFromPlayer(player);
} }
// update open or remove closed graveyard windows // update open or remove closed graveyard windows
graveyards.put(player.getName(), player.getGraveyard()); graveyards.put(player.getName(), player.getGraveyard());
if (graveyardWindows.containsKey(player.getName())) { if (graveyardWindows.containsKey(player.getName())) {
CardInfoWindowDialog cardInfoWindowDialog = graveyardWindows.get(player.getName()); CardInfoWindowDialog windowDialog = graveyardWindows.get(player.getName());
if (cardInfoWindowDialog.isClosed()) { if (windowDialog.isClosed()) {
graveyardWindows.remove(player.getName()); graveyardWindows.remove(player.getName());
} else { } else {
cardInfoWindowDialog.loadCards(player.getGraveyard(), bigCard, gameId, false); windowDialog.loadCards(player.getGraveyard(), bigCard, gameId, false);
} }
} }
// update open or remove closed sideboard windows
sideboards.put(player.getName(), player.getSideboard());
if (sideboardWindows.containsKey(player.getName())) {
CardInfoWindowDialog windowDialog = sideboardWindows.get(player.getName());
if (windowDialog.isClosed()) {
sideboardWindows.remove(player.getName());
} else {
windowDialog.loadCards(player.getSideboard(), bigCard, gameId, false);
}
}
// show top card window // show top card window
if (player.getTopCard() != null) { if (player.getTopCard() != null) {
CardsView cardsView = new CardsView(); CardsView cardsView = new CardsView();
@ -851,8 +874,12 @@ public final class GamePanel extends javax.swing.JPanel {
exiles.get(exile.getId()).loadCards(exile, bigCard, gameId); exiles.get(exile.getId()).loadCards(exile, bigCard, gameId);
} }
// reveal and look at dialogs can unattached, so windows opened by game doesn't have it
showRevealed(lastGameData.game); showRevealed(lastGameData.game);
showLookedAt(lastGameData.game); showLookedAt(lastGameData.game);
// sideboard dialogs is unattached all the time -- user opens it by command
showCompanion(lastGameData.game); showCompanion(lastGameData.game);
if (!lastGameData.game.getCombat().isEmpty()) { if (!lastGameData.game.getCombat().isEmpty()) {
CombatManager.instance.showCombat(lastGameData.game.getCombat(), gameId); CombatManager.instance.showCombat(lastGameData.game.getCombat(), gameId);
@ -1146,40 +1173,46 @@ public final class GamePanel extends javax.swing.JPanel {
// Called if the game frame is deactivated because the tabled the deck editor or other frames go to foreground // Called if the game frame is deactivated because the tabled the deck editor or other frames go to foreground
public void deactivated() { public void deactivated() {
// hide the non modal windows (because otherwise they are shown on top of the new active pane) // hide the non modal windows (because otherwise they are shown on top of the new active pane)
for (CardInfoWindowDialog exileDialog : exiles.values()) { for (CardInfoWindowDialog windowDialog : exiles.values()) {
exileDialog.hideDialog(); windowDialog.hideDialog();
} }
for (CardInfoWindowDialog graveyardDialog : graveyardWindows.values()) { for (CardInfoWindowDialog windowDialog : graveyardWindows.values()) {
graveyardDialog.hideDialog(); windowDialog.hideDialog();
} }
for (CardInfoWindowDialog revealDialog : revealed.values()) { for (CardInfoWindowDialog windowDialog : revealed.values()) {
revealDialog.hideDialog(); windowDialog.hideDialog();
} }
for (CardInfoWindowDialog lookedAtDialog : lookedAt.values()) { for (CardInfoWindowDialog windowDialog : lookedAt.values()) {
lookedAtDialog.hideDialog(); windowDialog.hideDialog();
} }
for (CardInfoWindowDialog companionDialog : companion.values()) { for (CardInfoWindowDialog windowDialog : companion.values()) {
companionDialog.hideDialog(); windowDialog.hideDialog();
}
for (CardInfoWindowDialog windowDialog : sideboardWindows.values()) {
windowDialog.hideDialog();
} }
} }
// Called if the game frame comes to front again // Called if the game frame comes to front again
public void activated() { public void activated() {
// hide the non modal windows (because otherwise they are shown on top of the new active pane) // hide the non modal windows (because otherwise they are shown on top of the new active pane)
for (CardInfoWindowDialog exileDialog : exiles.values()) { for (CardInfoWindowDialog windowDialog : exiles.values()) {
exileDialog.show(); windowDialog.show();
} }
for (CardInfoWindowDialog graveyardDialog : graveyardWindows.values()) { for (CardInfoWindowDialog windowDialog : graveyardWindows.values()) {
graveyardDialog.show(); windowDialog.show();
} }
for (CardInfoWindowDialog revealDialog : revealed.values()) { for (CardInfoWindowDialog windowDialog : revealed.values()) {
revealDialog.show(); windowDialog.show();
} }
for (CardInfoWindowDialog lookedAtDialog : lookedAt.values()) { for (CardInfoWindowDialog windowDialog : lookedAt.values()) {
lookedAtDialog.show(); windowDialog.show();
} }
for (CardInfoWindowDialog companionDialog : companion.values()) { for (CardInfoWindowDialog windowDialog : companion.values()) {
companionDialog.show(); windowDialog.show();
}
for (CardInfoWindowDialog windowDialog : sideboardWindows.values()) {
windowDialog.show();
} }
} }
@ -1196,9 +1229,40 @@ public final class GamePanel extends javax.swing.JPanel {
CardInfoWindowDialog newGraveyard = new CardInfoWindowDialog(ShowType.GRAVEYARD, playerName); CardInfoWindowDialog newGraveyard = new CardInfoWindowDialog(ShowType.GRAVEYARD, playerName);
graveyardWindows.put(playerName, newGraveyard); graveyardWindows.put(playerName, newGraveyard);
MageFrame.getDesktop().add(newGraveyard, JLayeredPane.PALETTE_LAYER); MageFrame.getDesktop().add(newGraveyard, JLayeredPane.PALETTE_LAYER);
// use graveyards to sync selection (don't use player data here)
newGraveyard.loadCards(graveyards.get(playerName), bigCard, gameId, false); newGraveyard.loadCards(graveyards.get(playerName), bigCard, gameId, false);
} }
public void openSideboardWindow(UUID playerId) {
if (lastGameData == null) {
return;
}
PlayerView playerView = lastGameData.game.getPlayers().stream()
.filter(p -> p.getPlayerId().equals(playerId))
.findFirst()
.orElse(null);
if (playerView == null) {
return;
}
if (sideboardWindows.containsKey(playerView.getName())) {
CardInfoWindowDialog windowDialog = sideboardWindows.get(playerView.getName());
if (windowDialog.isVisible()) {
windowDialog.hideDialog();
} else {
windowDialog.show();
}
return;
}
CardInfoWindowDialog windowDialog = new CardInfoWindowDialog(ShowType.SIDEBOARD, playerView.getName());
sideboardWindows.put(playerView.getName(), windowDialog);
MageFrame.getDesktop().add(windowDialog, JLayeredPane.PALETTE_LAYER);
// use sideboards to sync selection (don't use player data here)
windowDialog.loadCards(sideboards.get(playerView.getName()), bigCard, gameId, false);
}
public void openTopLibraryWindow(String playerName) { public void openTopLibraryWindow(String playerName) {
String title = playerName + "'s top library card"; String title = playerName + "'s top library card";
if (revealed.containsKey(title)) { if (revealed.containsKey(title)) {
@ -1386,6 +1450,25 @@ public final class GamePanel extends javax.swing.JPanel {
} }
} }
// sideboard
if (needZone == Zone.OUTSIDE || needZone == Zone.ALL) {
for (PlayerView player : lastGameData.game.getPlayers()) {
for (Map.Entry<UUID, CardView> card : player.getSideboard().entrySet()) {
if (needSelectable.contains(card.getKey())) {
card.getValue().setChoosable(true);
}
if (needChoosen.contains(card.getKey())) {
card.getValue().setSelected(true);
}
if (needPlayable.containsObject(card.getKey())) {
card.getValue().setPlayableStats(needPlayable.getStats(card.getKey()));
}
}
}
}
// sideboards (old windows all the time, e.g. unattached from game data)
prepareSelectableWindows(sideboardWindows.values(), needSelectable, needChoosen, needPlayable);
// exile // exile
if (needZone == Zone.EXILED || needZone == Zone.ALL) { if (needZone == Zone.EXILED || needZone == Zone.ALL) {
// exile from player panel // exile from player panel
@ -1402,6 +1485,7 @@ public final class GamePanel extends javax.swing.JPanel {
} }
} }
} }
// exile from windows // exile from windows
for (ExileView exile : lastGameData.game.getExile()) { for (ExileView exile : lastGameData.game.getExile()) {
for (Map.Entry<UUID, CardView> card : exile.entrySet()) { for (Map.Entry<UUID, CardView> card : exile.entrySet()) {
@ -1435,21 +1519,6 @@ public final class GamePanel extends javax.swing.JPanel {
} }
} }
// revealed
for (RevealedView rev : lastGameData.game.getRevealed()) {
for (Map.Entry<UUID, CardView> card : rev.getCards().entrySet()) {
if (needSelectable.contains(card.getKey())) {
card.getValue().setChoosable(true);
}
if (needChoosen.contains(card.getKey())) {
card.getValue().setSelected(true);
}
if (needPlayable.containsObject(card.getKey())) {
card.getValue().setPlayableStats(needPlayable.getStats(card.getKey()));
}
}
}
// companion // companion
for (RevealedView rev : lastGameData.game.getCompanion()) { for (RevealedView rev : lastGameData.game.getCompanion()) {
for (Map.Entry<UUID, CardView> card : rev.getCards().entrySet()) { for (Map.Entry<UUID, CardView> card : rev.getCards().entrySet()) {
@ -1465,7 +1534,24 @@ public final class GamePanel extends javax.swing.JPanel {
} }
} }
// looked at // revealed (current cards)
for (RevealedView rev : lastGameData.game.getRevealed()) {
for (Map.Entry<UUID, CardView> card : rev.getCards().entrySet()) {
if (needSelectable.contains(card.getKey())) {
card.getValue().setChoosable(true);
}
if (needChoosen.contains(card.getKey())) {
card.getValue().setSelected(true);
}
if (needPlayable.containsObject(card.getKey())) {
card.getValue().setPlayableStats(needPlayable.getStats(card.getKey()));
}
}
}
// revealed (old windows)
prepareSelectableWindows(revealed.values(), needSelectable, needChoosen, needPlayable);
// looked at (current cards)
for (LookedAtView look : lastGameData.game.getLookedAt()) { for (LookedAtView look : lastGameData.game.getLookedAt()) {
for (Map.Entry<UUID, SimpleCardView> card : look.getCards().entrySet()) { for (Map.Entry<UUID, SimpleCardView> card : look.getCards().entrySet()) {
if (needPlayable.containsObject(card.getKey())) { if (needPlayable.containsObject(card.getKey())) {
@ -1473,6 +1559,32 @@ public final class GamePanel extends javax.swing.JPanel {
} }
} }
} }
// looked at (old windows)
prepareSelectableWindows(lookedAt.values(), needSelectable, needChoosen, needPlayable);
}
private void prepareSelectableWindows(
Collection<CardInfoWindowDialog> windows,
Set<UUID> needSelectable,
List<UUID> needChoosen,
PlayableObjectsList needPlayable
) {
// lookAt or reveals windows clean up on next priority, so users can see dialogs, but xmage can't restore it
// so it must be updated manually (it's ok to keep outdated cards in dialog, but not ok to show wrong selections)
for (CardInfoWindowDialog window : windows) {
for (MageCard mageCard : window.getMageCardsForUpdate().values()) {
CardView cardView = mageCard.getOriginal();
cardView.setChoosable(needSelectable.contains(cardView.getId()));
cardView.setSelected(needChoosen.contains(cardView.getId()));
if (needPlayable.containsObject(cardView.getId())) {
cardView.setPlayableStats(needPlayable.getStats(cardView.getId()));
} else {
cardView.setPlayableStats(new PlayableObjectStats());
}
// TODO: little bug with toggled night card after update/clicks, but that's ok (can't click on second side)
mageCard.update(cardView);
}
}
} }
/** /**

View file

@ -1,89 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="jPanel1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="357" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" alignment="1" pref="278" max="32767" attributes="0"/>
<Component id="jPanel1" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="jPanel1">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.BevelBorderInfo">
<BevelBorder/>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="manaPool" alignment="0" pref="116" max="32767" attributes="1"/>
<Component id="playerPanel" alignment="0" max="32767" attributes="1"/>
<Component id="btnCheat" alignment="0" pref="116" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="playerPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="manaPool" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="17" max="32767" attributes="0"/>
<Component id="btnCheat" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="mage.client.game.PlayerPanel" name="playerPanel">
</Component>
<Component class="mage.client.game.ManaPool" name="manaPool">
</Component>
<Component class="javax.swing.JButton" name="btnCheat">
<Properties>
<Property name="text" type="java.lang.String" value="Cheat"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnCheatActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="mage.client.game.BattlefieldPanel" name="battlefieldPanel">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JLayeredPaneSupportLayout"/>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View file

@ -24,6 +24,8 @@ import java.util.UUID;
import static mage.client.dialog.PreferencesDialog.*; import static mage.client.dialog.PreferencesDialog.*;
/** /**
* GUI: play area panel (player with avatar/mana panel + battlefield panel)
*
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
public class PlayAreaPanel extends javax.swing.JPanel { public class PlayAreaPanel extends javax.swing.JPanel {
@ -441,14 +443,28 @@ public class PlayAreaPanel extends javax.swing.JPanel {
popupMenu.addSeparator(); popupMenu.addSeparator();
menuItem = new JMenuItem("<html>View current deck"); // view deck
menuItem.setMnemonic(KeyEvent.VK_V); menuItem = new JMenuItem("<html>View player's deck");
menuItem.setMnemonic(KeyEvent.VK_D);
popupMenu.add(menuItem); popupMenu.add(menuItem);
// View limited deck
menuItem.addActionListener(e -> { menuItem.addActionListener(e -> {
SessionHandler.sendPlayerAction(PlayerAction.VIEW_LIMITED_DECK, gameId, null); SessionHandler.sendPlayerAction(PlayerAction.VIEW_LIMITED_DECK, gameId, null);
}); });
// view sideboard (allows to view only own sideboard or computer)
// it's a client side checks... same checks must be on server side too (see PlayerView)
if (options.playerItself || !options.isHuman) {
String menuCaption = "<html>View my sideboard";
if (!options.isHuman) {
menuCaption = "<html>View computer's sideboard";
}
menuItem = new JMenuItem(menuCaption);
menuItem.setMnemonic(KeyEvent.VK_S);
popupMenu.add(menuItem);
menuItem.addActionListener(e -> {
SessionHandler.sendPlayerAction(PlayerAction.VIEW_SIDEBOARD, gameId, playerId);
});
}
} }
private void addPopupMenuWatcher() { private void addPopupMenuWatcher() {

View file

@ -8,8 +8,9 @@ package mage.client.game;
*/ */
public class PlayAreaPanelOptions { public class PlayAreaPanelOptions {
public PlayAreaPanelOptions(boolean isPlayer, boolean playerItself, boolean rollbackTurnsAllowed, boolean topRow) { public PlayAreaPanelOptions(boolean isPlayer, boolean isHuman, boolean playerItself, boolean rollbackTurnsAllowed, boolean topRow) {
this.isPlayer = isPlayer; this.isPlayer = isPlayer;
this.isHuman = isHuman;
this.playerItself = playerItself; this.playerItself = playerItself;
this.rollbackTurnsAllowed = rollbackTurnsAllowed; this.rollbackTurnsAllowed = rollbackTurnsAllowed;
this.topRow = topRow; this.topRow = topRow;
@ -18,22 +19,27 @@ public class PlayAreaPanelOptions {
/** /**
* true if the client is a player / false if the client is a watcher * true if the client is a player / false if the client is a watcher
*/ */
public boolean isPlayer = false; public boolean isPlayer;
/**
* true if the player is the human, not computer
*/
public boolean isHuman;
/** /**
* true if the player is the client player itself, false if the player is * true if the player is the client player itself, false if the player is
* another player playing with the clinet player * another player playing with the client player
*/ */
public boolean playerItself = false; public boolean playerItself;
/** /**
* true if the player can rollback turns if all players agree * true if the player can rollback turns if all players agree
*/ */
public boolean rollbackTurnsAllowed = false; public boolean rollbackTurnsAllowed;
/** /**
* true if the battlefield is on the top row of player areas * true if the battlefield is on the top row of player areas
*/ */
public boolean topRow = false; public boolean topRow;
} }

View file

@ -391,6 +391,12 @@ public class CallbackClientImpl implements CallbackClient {
break; break;
} }
case VIEW_SIDEBOARD: {
TableClientMessage message = (TableClientMessage) callback.getData();
viewSideboard(message.getGameId(), message.getPlayerId());
break;
}
case CONSTRUCT: { case CONSTRUCT: {
TableClientMessage message = (TableClientMessage) callback.getData(); TableClientMessage message = (TableClientMessage) callback.getData();
DeckView deckView = message.getDeck(); DeckView deckView = message.getDeck();
@ -616,6 +622,15 @@ public class CallbackClientImpl implements CallbackClient {
frame.showDeckEditor(DeckEditorMode.VIEW_LIMITED_DECK, deck, tableId, time); frame.showDeckEditor(DeckEditorMode.VIEW_LIMITED_DECK, deck, tableId, time);
} }
protected void viewSideboard(UUID gameId, UUID playerId) {
SwingUtilities.invokeLater(() -> {
GamePanel panel = MageFrame.getGame(gameId);
if (panel != null) {
panel.openSideboardWindow(playerId);
}
});
}
private void handleException(Exception ex) { private void handleException(Exception ex) {
logger.fatal("Client error\n", ex); logger.fatal("Client error\n", ex);
String errorMessage = ex.getMessage(); String errorMessage = ex.getMessage();

View file

@ -14,6 +14,7 @@ public enum ClientCallbackMethod {
START_TOURNAMENT("startTournament"), START_TOURNAMENT("startTournament"),
SIDEBOARD("sideboard"), SIDEBOARD("sideboard"),
VIEW_LIMITED_DECK("viewLimitedDeck"), VIEW_LIMITED_DECK("viewLimitedDeck"),
VIEW_SIDEBOARD("viewSideboard"),
CONSTRUCT("construct"), CONSTRUCT("construct"),
SHOW_USERMESSAGE("showUserMessage"), SHOW_USERMESSAGE("showUserMessage"),
WATCHGAME("watchGame"), WATCHGAME("watchGame"),

View file

@ -24,6 +24,7 @@ public class PlayerView implements Serializable {
private final UUID playerId; private final UUID playerId;
private final String name; private final String name;
private final boolean controlled; // gui: player is current user private final boolean controlled; // gui: player is current user
private final boolean isHuman; // human or computer
private final int life; private final int life;
private final Counters counters; private final Counters counters;
private final int wins; private final int wins;
@ -38,6 +39,7 @@ public class PlayerView implements Serializable {
private final ManaPoolView manaPool; private final ManaPoolView manaPool;
private final CardsView graveyard = new CardsView(); private final CardsView graveyard = new CardsView();
private final CardsView exile = new CardsView(); private final CardsView exile = new CardsView();
private final CardsView sideboard = new CardsView();
private final Map<UUID, PermanentView> battlefield = new LinkedHashMap<>(); private final Map<UUID, PermanentView> battlefield = new LinkedHashMap<>();
private final CardView topCard; private final CardView topCard;
private final UserData userData; private final UserData userData;
@ -58,6 +60,7 @@ public class PlayerView implements Serializable {
this.playerId = player.getId(); this.playerId = player.getId();
this.name = player.getName(); this.name = player.getName();
this.controlled = player.getId().equals(createdForPlayerId); this.controlled = player.getId().equals(createdForPlayerId);
this.isHuman = player.isHuman();
this.life = player.getLife(); this.life = player.getLife();
this.counters = player.getCounters(); this.counters = player.getCounters();
this.wins = player.getMatchPlayer().getWins(); this.wins = player.getMatchPlayer().getWins();
@ -85,6 +88,13 @@ public class PlayerView implements Serializable {
} }
} }
} }
if (this.controlled || !player.isHuman()) {
// sideboard available for itself or for computer only
for (Card card : player.getSideboard().getCards(game)) {
sideboard.put(card.getId(), new CardView(card, game, false));
}
}
try { try {
for (Permanent permanent : state.getBattlefield().getAllPermanents()) { for (Permanent permanent : state.getBattlefield().getAllPermanents()) {
if (showInBattlefield(permanent, state)) { if (showInBattlefield(permanent, state)) {
@ -167,6 +177,10 @@ public class PlayerView implements Serializable {
return this.controlled; return this.controlled;
} }
public boolean isHuman() {
return this.isHuman;
}
public int getLife() { public int getLife() {
return this.life; return this.life;
} }
@ -207,6 +221,10 @@ public class PlayerView implements Serializable {
return exile; return exile;
} }
public CardsView getSideboard() {
return this.sideboard;
}
public Map<UUID, PermanentView> getBattlefield() { public Map<UUID, PermanentView> getBattlefield() {
return this.battlefield; return this.battlefield;
} }

View file

@ -259,6 +259,10 @@ public class User {
fireCallback(new ClientCallback(ClientCallbackMethod.VIEW_LIMITED_DECK, tableId, new TableClientMessage(deck, tableId, time, limited))); fireCallback(new ClientCallback(ClientCallbackMethod.VIEW_LIMITED_DECK, tableId, new TableClientMessage(deck, tableId, time, limited)));
} }
public void ccViewSideboard(final UUID tableId, final UUID gameId, final UUID targetPlayerId) {
fireCallback(new ClientCallback(ClientCallbackMethod.VIEW_SIDEBOARD, tableId, new TableClientMessage(gameId, targetPlayerId)));
}
public void ccConstruct(final Deck deck, final UUID tableId, final int time) { public void ccConstruct(final Deck deck, final UUID tableId, final int time) {
fireCallback(new ClientCallback(ClientCallbackMethod.CONSTRUCT, tableId, new TableClientMessage(deck, tableId, time))); fireCallback(new ClientCallback(ClientCallbackMethod.CONSTRUCT, tableId, new TableClientMessage(deck, tableId, time)));
} }

View file

@ -598,6 +598,12 @@ public class GameController implements GameCallback {
case VIEW_LIMITED_DECK: case VIEW_LIMITED_DECK:
viewLimitedDeck(getPlayerId(userId), userId); viewLimitedDeck(getPlayerId(userId), userId);
break; break;
case VIEW_SIDEBOARD:
if (data instanceof UUID) {
UUID targetPlayerId = (UUID) data;
viewSideboard(getPlayerId(userId), userId, targetPlayerId);
}
break;
default: default:
game.sendPlayerAction(playerAction, getPlayerId(userId), data); game.sendPlayerAction(playerAction, getPlayerId(userId), data);
} }
@ -656,13 +662,13 @@ public class GameController implements GameCallback {
} }
} }
private void viewLimitedDeck(UUID userIdRequester, UUID origId) { private void viewLimitedDeck(UUID playerId, UUID userId) {
Player viewLimitedDeckPlayer = game.getPlayer(userIdRequester); Player viewLimitedDeckPlayer = game.getPlayer(playerId);
if (viewLimitedDeckPlayer != null) { if (viewLimitedDeckPlayer != null) {
if (viewLimitedDeckPlayer.isHuman()) { if (viewLimitedDeckPlayer.isHuman()) {
for (MatchPlayer p : managerFactory.tableManager().getTable(tableId).getMatch().getPlayers()) { for (MatchPlayer p : managerFactory.tableManager().getTable(tableId).getMatch().getPlayers()) {
if (p.getPlayer().getId().equals(userIdRequester)) { if (p.getPlayer().getId().equals(playerId)) {
Optional<User> u = managerFactory.userManager().getUser(origId); Optional<User> u = managerFactory.userManager().getUser(userId);
if (u.isPresent() && p.getDeck() != null) { if (u.isPresent() && p.getDeck() != null) {
u.get().ccViewLimitedDeck(p.getDeck(), tableId, requestsOpen, true); u.get().ccViewLimitedDeck(p.getDeck(), tableId, requestsOpen, true);
} }
@ -672,6 +678,18 @@ public class GameController implements GameCallback {
} }
} }
private void viewSideboard(UUID playerId, UUID userId, UUID targetPlayerId) {
Player needPlayer = game.getPlayer(playerId);
if (needPlayer != null && needPlayer.isHuman()) {
for (MatchPlayer p : managerFactory.tableManager().getTable(tableId).getMatch().getPlayers()) {
if (p.getPlayer().getId().equals(playerId)) {
Optional<User> u = managerFactory.userManager().getUser(userId);
u.ifPresent(user -> user.ccViewSideboard(tableId, game.getId(), targetPlayerId));
}
}
}
}
public void cheat(UUID userId, UUID playerId, DeckCardLists deckList) { public void cheat(UUID userId, UUID playerId, DeckCardLists deckList) {
try { try {
Deck deck = Deck.load(deckList, false, false); Deck deck = Deck.load(deckList, false, false);

View file

@ -7,6 +7,7 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.effects.common.combat.BlocksIfAbleTargetEffect; import mage.abilities.effects.common.combat.BlocksIfAbleTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.abilities.keyword.ReachAbility; import mage.abilities.keyword.ReachAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -34,6 +35,7 @@ public final class AcademicDispute extends CardImpl {
// Learn. // Learn.
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private AcademicDispute(final AcademicDispute card) { private AcademicDispute(final AcademicDispute card) {

View file

@ -2,6 +2,7 @@ package mage.cards.a;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -23,6 +24,7 @@ public final class ArcaneSubtraction extends CardImpl {
// Learn. // Learn.
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private ArcaneSubtraction(final ArcaneSubtraction card) { private ArcaneSubtraction(final ArcaneSubtraction card) {

View file

@ -4,6 +4,7 @@ package mage.cards.b;
import java.util.UUID; import java.util.UUID;
import mage.abilities.effects.common.ExileSpellEffect; import mage.abilities.effects.common.ExileSpellEffect;
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -26,6 +27,7 @@ public final class BurningWish extends CardImpl {
// You may choose a sorcery card you own from outside the game, reveal that card, and put it into your hand. // You may choose a sorcery card you own from outside the game, reveal that card, and put it into your hand.
this.getSpellAbility().addEffect(new WishEffect(filter)); this.getSpellAbility().addEffect(new WishEffect(filter));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
// Exile Burning Wish. // Exile Burning Wish.
this.getSpellAbility().addEffect(new ExileSpellEffect()); this.getSpellAbility().addEffect(new ExileSpellEffect());

View file

@ -3,6 +3,7 @@ package mage.cards.c;
import java.util.UUID; import java.util.UUID;
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -26,6 +27,7 @@ public final class CoaxFromTheBlindEternities extends CardImpl {
// You may choose an Eldrazi card you own from outside the game or in exile, reveal that card, and put it into your hand. // You may choose an Eldrazi card you own from outside the game or in exile, reveal that card, and put it into your hand.
this.getSpellAbility().addEffect(new WishEffect(filter, true, true)); this.getSpellAbility().addEffect(new WishEffect(filter, true, true));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private CoaxFromTheBlindEternities(final CoaxFromTheBlindEternities card) { private CoaxFromTheBlindEternities(final CoaxFromTheBlindEternities card) {

View file

@ -2,6 +2,7 @@ package mage.cards.c;
import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -21,6 +22,7 @@ public final class CramSession extends CardImpl {
// Learn. // Learn.
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private CramSession(final CramSession card) { private CramSession(final CramSession card) {

View file

@ -4,6 +4,7 @@ package mage.cards.c;
import java.util.UUID; import java.util.UUID;
import mage.abilities.effects.common.ExileSpellEffect; import mage.abilities.effects.common.ExileSpellEffect;
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -26,6 +27,7 @@ public final class CunningWish extends CardImpl {
// You may choose an instant card you own from outside the game, reveal that card, and put it into your hand. // You may choose an instant card you own from outside the game, reveal that card, and put it into your hand.
this.getSpellAbility().addEffect(new WishEffect(filter)); this.getSpellAbility().addEffect(new WishEffect(filter));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
// Exile Cunning Wish. // Exile Cunning Wish.
this.getSpellAbility().addEffect(new ExileSpellEffect()); this.getSpellAbility().addEffect(new ExileSpellEffect());

View file

@ -3,6 +3,7 @@ package mage.cards.d;
import mage.abilities.effects.common.ExileSpellEffect; import mage.abilities.effects.common.ExileSpellEffect;
import mage.abilities.effects.common.LoseHalfLifeEffect; import mage.abilities.effects.common.LoseHalfLifeEffect;
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -20,6 +21,7 @@ public final class DeathWish extends CardImpl {
// You may choose a card you own from outside the game and put it into your hand. // You may choose a card you own from outside the game and put it into your hand.
this.getSpellAbility().addEffect(new WishEffect(StaticFilters.FILTER_CARD_A, false)); this.getSpellAbility().addEffect(new WishEffect(StaticFilters.FILTER_CARD_A, false));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
// You lose half your life, rounded up. // You lose half your life, rounded up.
this.getSpellAbility().addEffect(new LoseHalfLifeEffect()); this.getSpellAbility().addEffect(new LoseHalfLifeEffect());

View file

@ -2,6 +2,7 @@ package mage.cards.d;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -37,6 +38,7 @@ public final class DivideByZero extends CardImpl {
// Learn. // Learn.
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private DivideByZero(final DivideByZero card) { private DivideByZero(final DivideByZero card) {

View file

@ -6,6 +6,7 @@ import mage.abilities.common.BecomesTargetTriggeredAbility;
import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.SacrificeSourceEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.constants.SubType; import mage.constants.SubType;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -36,7 +37,8 @@ public final class DreamStrix extends CardImpl {
)); ));
// When Dream Strix dies, learn. // When Dream Strix dies, learn.
this.addAbility(new DiesSourceTriggeredAbility(new LearnEffect())); this.addAbility(new DiesSourceTriggeredAbility(new LearnEffect())
.addHint(OpenSideboardHint.instance));
} }
private DreamStrix(final DreamStrix card) { private DreamStrix(final DreamStrix card) {

View file

@ -3,6 +3,7 @@ package mage.cards.e;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -31,6 +32,7 @@ public final class EnthusiasticStudy extends CardImpl {
// Learn. // Learn.
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private EnthusiasticStudy(final EnthusiasticStudy card) { private EnthusiasticStudy(final EnthusiasticStudy card) {

View file

@ -3,6 +3,7 @@ package mage.cards.e;
import mage.MageInt; import mage.MageInt;
import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -28,7 +29,8 @@ public final class Eyetwitch extends CardImpl {
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
// When Eyetwitch dies, learn. // When Eyetwitch dies, learn.
this.addAbility(new DiesSourceTriggeredAbility(new LearnEffect())); this.addAbility(new DiesSourceTriggeredAbility(new LearnEffect())
.addHint(OpenSideboardHint.instance));
} }
private Eyetwitch(final Eyetwitch card) { private Eyetwitch(final Eyetwitch card) {

View file

@ -7,6 +7,7 @@ import mage.abilities.costs.common.DiscardTargetCost;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect;
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.AdventureCard; import mage.cards.AdventureCard;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -46,6 +47,7 @@ public final class FaeOfWishes extends AdventureCard {
// Granted // Granted
// You may choose a noncreature card you own from outside the game, reveal it, and put it into your hand. // You may choose a noncreature card you own from outside the game, reveal it, and put it into your hand.
this.getSpellCard().getSpellAbility().addEffect(new WishEffect(StaticFilters.FILTER_CARD_A_NON_CREATURE)); this.getSpellCard().getSpellAbility().addEffect(new WishEffect(StaticFilters.FILTER_CARD_A_NON_CREATURE));
this.getSpellCard().getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private FaeOfWishes(final FaeOfWishes card) { private FaeOfWishes(final FaeOfWishes card) {

View file

@ -2,6 +2,7 @@ package mage.cards.f;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -32,6 +33,7 @@ public final class FieldTrip extends CardImpl {
// Learn. // Learn.
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private FieldTrip(final FieldTrip card) { private FieldTrip(final FieldTrip card) {

View file

@ -5,6 +5,7 @@ import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.HasteAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -32,6 +33,7 @@ public final class FirstDayOfClass extends CardImpl {
// Learn. // Learn.
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private FirstDayOfClass(final FirstDayOfClass card) { private FirstDayOfClass(final FirstDayOfClass card) {

View file

@ -4,6 +4,7 @@ package mage.cards.g;
import java.util.UUID; import java.util.UUID;
import mage.abilities.effects.common.ExileSpellEffect; import mage.abilities.effects.common.ExileSpellEffect;
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -27,6 +28,7 @@ public final class GlitteringWish extends CardImpl {
// You may choose a multicolored card you own from outside the game, reveal that card, and put it into your hand. // You may choose a multicolored card you own from outside the game, reveal that card, and put it into your hand.
this.getSpellAbility().addEffect(new WishEffect(filter)); this.getSpellAbility().addEffect(new WishEffect(filter));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
// Exile Glittering Wish. // Exile Glittering Wish.
this.getSpellAbility().addEffect(new ExileSpellEffect()); this.getSpellAbility().addEffect(new ExileSpellEffect());

View file

@ -3,6 +3,7 @@ package mage.cards.g;
import mage.MageInt; import mage.MageInt;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -28,7 +29,8 @@ public final class GnarledProfessor extends CardImpl {
this.addAbility(TrampleAbility.getInstance()); this.addAbility(TrampleAbility.getInstance());
// When Gnarled Professor enters the battlefield, learn. // When Gnarled Professor enters the battlefield, learn.
this.addAbility(new EntersBattlefieldTriggeredAbility(new LearnEffect())); this.addAbility(new EntersBattlefieldTriggeredAbility(new LearnEffect())
.addHint(OpenSideboardHint.instance));
} }
private GnarledProfessor(final GnarledProfessor card) { private GnarledProfessor(final GnarledProfessor card) {

View file

@ -4,6 +4,7 @@ package mage.cards.g;
import java.util.UUID; import java.util.UUID;
import mage.abilities.effects.common.ExileSpellEffect; import mage.abilities.effects.common.ExileSpellEffect;
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -29,6 +30,7 @@ public final class GoldenWish extends CardImpl {
// You may choose an artifact or enchantment card you own from outside the game, reveal that card, and put it into your hand. // You may choose an artifact or enchantment card you own from outside the game, reveal that card, and put it into your hand.
this.getSpellAbility().addEffect(new WishEffect(filter)); this.getSpellAbility().addEffect(new WishEffect(filter));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
// Exile Golden Wish. // Exile Golden Wish.
this.getSpellAbility().addEffect(new ExileSpellEffect()); this.getSpellAbility().addEffect(new ExileSpellEffect());

View file

@ -2,6 +2,7 @@ package mage.cards.g;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -22,8 +23,9 @@ public final class GuidingVoice extends CardImpl {
this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent());
// Learn (You may reveal a Lesson card you own from outside the game and p // Learn (You may reveal a Lesson card you own from outside the game and put it into your hand, or discard a card to draw a card.)
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private GuidingVoice(final GuidingVoice card) { private GuidingVoice(final GuidingVoice card) {

View file

@ -2,6 +2,7 @@ package mage.cards.h;
import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -22,6 +23,7 @@ public final class HuntForSpecimens extends CardImpl {
// Learn. // Learn.
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private HuntForSpecimens(final HuntForSpecimens card) { private HuntForSpecimens(final HuntForSpecimens card) {

View file

@ -2,6 +2,7 @@ package mage.cards.i;
import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -23,6 +24,7 @@ public final class IgneousInspiration extends CardImpl {
// Learn. // Learn.
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private IgneousInspiration(final IgneousInspiration card) { private IgneousInspiration(final IgneousInspiration card) {

View file

@ -7,6 +7,7 @@ import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.RestrictionEffect;
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.*;
@ -50,7 +51,7 @@ public final class KarnTheGreatCreator extends CardImpl {
// -2: You may choose an artifact card you own from outside the game or in exile, reveal that card, and put it into your hand. // -2: You may choose an artifact card you own from outside the game or in exile, reveal that card, and put it into your hand.
this.addAbility(new LoyaltyAbility(new WishEffect( this.addAbility(new LoyaltyAbility(new WishEffect(
StaticFilters.FILTER_CARD_ARTIFACT_AN, true, true StaticFilters.FILTER_CARD_ARTIFACT_AN, true, true
), -2)); ), -2).addHint(OpenSideboardHint.instance));
} }
private KarnTheGreatCreator(final KarnTheGreatCreator card) { private KarnTheGreatCreator(final KarnTheGreatCreator card) {

View file

@ -5,6 +5,7 @@ import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.constants.SubType; import mage.constants.SubType;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -37,7 +38,8 @@ public final class LegionAngel extends CardImpl {
// When Legion Angel enters the battlefield, you may reveal a card you own named Legion Angel from outside the game and put it into your hand. // When Legion Angel enters the battlefield, you may reveal a card you own named Legion Angel from outside the game and put it into your hand.
this.addAbility(new EntersBattlefieldTriggeredAbility(new WishEffect(filter, true, false) this.addAbility(new EntersBattlefieldTriggeredAbility(new WishEffect(filter, true, false)
.setText("you may reveal a card you own named Legion Angel from outside the game and put it into your hand"))); .setText("you may reveal a card you own named Legion Angel from outside the game and put it into your hand"))
.addHint(OpenSideboardHint.instance));
} }
private LegionAngel(final LegionAngel card) { private LegionAngel(final LegionAngel card) {

View file

@ -4,6 +4,7 @@ package mage.cards.l;
import java.util.UUID; import java.util.UUID;
import mage.abilities.effects.common.ExileSpellEffect; import mage.abilities.effects.common.ExileSpellEffect;
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -29,6 +30,7 @@ public final class LivingWish extends CardImpl {
// You may choose a creature or land card you own from outside the game, reveal that card, and put it into your hand. // You may choose a creature or land card you own from outside the game, reveal that card, and put it into your hand.
this.getSpellAbility().addEffect(new WishEffect(filter)); this.getSpellAbility().addEffect(new WishEffect(filter));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
// Exile Living Wish. // Exile Living Wish.
this.getSpellAbility().addEffect(new ExileSpellEffect()); this.getSpellAbility().addEffect(new ExileSpellEffect());

View file

@ -3,6 +3,7 @@ package mage.cards.m;
import mage.abilities.Mode; import mage.abilities.Mode;
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -27,6 +28,7 @@ public final class MastermindsAcquisition extends CardImpl {
Mode mode = new Mode(new WishEffect(StaticFilters.FILTER_CARD_A, false) Mode mode = new Mode(new WishEffect(StaticFilters.FILTER_CARD_A, false)
.setText("Put a card you own from outside the game into your hand")); .setText("Put a card you own from outside the game into your hand"));
this.getSpellAbility().addMode(mode); this.getSpellAbility().addMode(mode);
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private MastermindsAcquisition(final MastermindsAcquisition card) { private MastermindsAcquisition(final MastermindsAcquisition card) {

View file

@ -8,6 +8,7 @@ import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.abilities.keyword.DefenderAbility; import mage.abilities.keyword.DefenderAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -38,6 +39,7 @@ public final class OvergrownArch extends CardImpl {
// {2}, Sacrifice Overgrown Arch: Learn. // {2}, Sacrifice Overgrown Arch: Learn.
Ability ability = new SimpleActivatedAbility(new LearnEffect(), new GenericManaCost(2)); Ability ability = new SimpleActivatedAbility(new LearnEffect(), new GenericManaCost(2));
ability.addCost(new SacrificeSourceCost()); ability.addCost(new SacrificeSourceCost());
ability.addHint(OpenSideboardHint.instance);
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -7,6 +7,7 @@ import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect;
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.abilities.keyword.EquipAbility; import mage.abilities.keyword.EquipAbility;
import mage.abilities.keyword.LifelinkAbility; import mage.abilities.keyword.LifelinkAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -29,7 +30,8 @@ public final class PoetsQuill extends CardImpl {
this.subtype.add(SubType.EQUIPMENT); this.subtype.add(SubType.EQUIPMENT);
// When Poet's Quill enters the battlefield, learn. // When Poet's Quill enters the battlefield, learn.
this.addAbility(new EntersBattlefieldTriggeredAbility(new LearnEffect())); this.addAbility(new EntersBattlefieldTriggeredAbility(new LearnEffect())
.addHint(OpenSideboardHint.instance));
// Equipped creature gets +1/+1 and has lifelink. // Equipped creature gets +1/+1 and has lifelink.
Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 1)); Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 1));

View file

@ -2,6 +2,7 @@ package mage.cards.p;
import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -21,6 +22,7 @@ public final class PopQuiz extends CardImpl {
// Learn. // Learn.
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private PopQuiz(final PopQuiz card) { private PopQuiz(final PopQuiz card) {

View file

@ -3,6 +3,7 @@ package mage.cards.p;
import mage.MageInt; import mage.MageInt;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -24,7 +25,8 @@ public final class ProfessorOfSymbology extends CardImpl {
this.toughness = new MageInt(1); this.toughness = new MageInt(1);
// When Professor of Symbology enters the battlefield, learn. // When Professor of Symbology enters the battlefield, learn.
this.addAbility(new EntersBattlefieldTriggeredAbility(new LearnEffect())); this.addAbility(new EntersBattlefieldTriggeredAbility(new LearnEffect())
.addHint(OpenSideboardHint.instance));
} }
private ProfessorOfSymbology(final ProfessorOfSymbology card) { private ProfessorOfSymbology(final ProfessorOfSymbology card) {

View file

@ -5,6 +5,7 @@ import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.*; import mage.cards.*;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.Outcome;
@ -26,6 +27,7 @@ public final class ResearchDevelopment extends SplitCard {
// Choose up to four cards you own from outside the game and shuffle them into your library. // Choose up to four cards you own from outside the game and shuffle them into your library.
getLeftHalfCard().getSpellAbility().addEffect(new ResearchEffect()); getLeftHalfCard().getSpellAbility().addEffect(new ResearchEffect());
getLeftHalfCard().getSpellAbility().addHint(OpenSideboardHint.instance);
// Create a 3/1 red Elemental creature token unless any opponent has you draw a card. Repeat this process two more times. // Create a 3/1 red Elemental creature token unless any opponent has you draw a card. Repeat this process two more times.
getRightHalfCard().getSpellAbility().addEffect(new DevelopmentEffect()); getRightHalfCard().getSpellAbility().addEffect(new DevelopmentEffect());

View file

@ -9,6 +9,7 @@ import mage.abilities.condition.common.CastFromEverywhereSourceCondition;
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.HasteAbility;
import mage.cards.Card; import mage.cards.Card;
@ -43,7 +44,7 @@ public final class RetrieverPhoenix extends CardImpl {
this.addAbility(new ConditionalInterveningIfTriggeredAbility( this.addAbility(new ConditionalInterveningIfTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new LearnEffect()), CastFromEverywhereSourceCondition.instance, new EntersBattlefieldTriggeredAbility(new LearnEffect()), CastFromEverywhereSourceCondition.instance,
"When {this} enters the battlefield, if you cast it, " + LearnEffect.getDefaultText() "When {this} enters the battlefield, if you cast it, " + LearnEffect.getDefaultText()
)); ).addHint(OpenSideboardHint.instance));
// As long as Retriever Phoenix is in your graveyard, if you would learn, you may instead return Retriever Phoenix to the battlefield. // As long as Retriever Phoenix is in your graveyard, if you would learn, you may instead return Retriever Phoenix to the battlefield.
this.addAbility(new SimpleStaticAbility(Zone.GRAVEYARD, new RetrieverPhoenixEffect())); this.addAbility(new SimpleStaticAbility(Zone.GRAVEYARD, new RetrieverPhoenixEffect()));

View file

@ -9,6 +9,7 @@ import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -33,6 +34,7 @@ public final class RingOfMaruf extends CardImpl {
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RingOfMarufEffect(), new ManaCostsImpl("{5}")); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RingOfMarufEffect(), new ManaCostsImpl("{5}"));
ability.addCost(new TapSourceCost()); ability.addCost(new TapSourceCost());
ability.addCost(new ExileSourceCost()); ability.addCost(new ExileSourceCost());
ability.addHint(OpenSideboardHint.instance);
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -2,6 +2,7 @@ package mage.cards.r;
import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -29,6 +30,7 @@ public final class RiseOfExtus extends CardImpl {
// Learn. // Learn.
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private RiseOfExtus(final RiseOfExtus card) { private RiseOfExtus(final RiseOfExtus card) {

View file

@ -6,6 +6,7 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.UntapTargetEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -23,7 +24,8 @@ public final class SparringRegimen extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}");
// When Sparring Regimen enters the battlefield, learn. // When Sparring Regimen enters the battlefield, learn.
this.addAbility(new EntersBattlefieldTriggeredAbility(new LearnEffect())); this.addAbility(new EntersBattlefieldTriggeredAbility(new LearnEffect())
.addHint(OpenSideboardHint.instance));
// Whenever you attack, put a +1/+1 counter on target attacking creature and untap it. // Whenever you attack, put a +1/+1 counter on target attacking creature and untap it.
Ability ability = new AttacksWithCreaturesTriggeredAbility( Ability ability = new AttacksWithCreaturesTriggeredAbility(

View file

@ -7,6 +7,7 @@ import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.common.CastCardFromOutsideTheGameEffect; import mage.abilities.effects.common.CastCardFromOutsideTheGameEffect;
import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.abilities.keyword.AnnihilatorAbility; import mage.abilities.keyword.AnnihilatorAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -43,7 +44,9 @@ public final class SpawnsireOfUlamog extends CardImpl {
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new EldraziSpawnToken(), 2), new GenericManaCost(4))); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new EldraziSpawnToken(), 2), new GenericManaCost(4)));
// {20}: Cast any number of Eldrazi cards you own from outside the game without paying their mana costs. // {20}: Cast any number of Eldrazi cards you own from outside the game without paying their mana costs.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CastCardFromOutsideTheGameEffect(filter, ruleText), new GenericManaCost(20))); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,
new CastCardFromOutsideTheGameEffect(filter, ruleText), new GenericManaCost(20)
).addHint(OpenSideboardHint.instance));
} }
private SpawnsireOfUlamog(final SpawnsireOfUlamog card) { private SpawnsireOfUlamog(final SpawnsireOfUlamog card) {

View file

@ -2,6 +2,7 @@ package mage.cards.s;
import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.LearnEffect;
import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.effects.common.TapTargetEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -23,6 +24,7 @@ public final class StudyBreak extends CardImpl {
// Learn. // Learn.
this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>")); this.getSpellAbility().addEffect(new LearnEffect().concatBy("<br>"));
this.getSpellAbility().addHint(OpenSideboardHint.instance);
} }
private StudyBreak(final StudyBreak card) { private StudyBreak(final StudyBreak card) {

View file

@ -7,6 +7,7 @@ import java.util.UUID;
import mage.abilities.DelayedTriggeredAbility; import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.SagaAbility; import mage.abilities.common.SagaAbility;
import mage.abilities.effects.common.*; import mage.abilities.effects.common.*;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.SagaChapter; import mage.constants.SagaChapter;
@ -51,6 +52,7 @@ public final class TheRavensWarning extends CardImpl {
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III,
new WishEffect(StaticFilters.FILTER_CARD_A, false, false, true) new WishEffect(StaticFilters.FILTER_CARD_A, false, false, true)
); );
sagaAbility.addHint(OpenSideboardHint.instance);
this.addAbility(sagaAbility); this.addAbility(sagaAbility);
} }

View file

@ -7,6 +7,7 @@ import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect
import mage.abilities.effects.common.WishEffect; import mage.abilities.effects.common.WishEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.effects.common.counter.DistributeCountersEffect; import mage.abilities.effects.common.counter.DistributeCountersEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -58,7 +59,8 @@ public final class VivienArkbowRanger extends CardImpl {
this.addAbility(ability); this.addAbility(ability);
// 5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand. // 5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand.
this.addAbility(new LoyaltyAbility(new WishEffect(StaticFilters.FILTER_CARD_CREATURE_A), -5)); this.addAbility(new LoyaltyAbility(new WishEffect(StaticFilters.FILTER_CARD_CREATURE_A), -5)
.addHint(OpenSideboardHint.instance));
} }
private VivienArkbowRanger(final VivienArkbowRanger card) { private VivienArkbowRanger(final VivienArkbowRanger card) {

View file

@ -0,0 +1,137 @@
package mage.cards.w;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageIdentifier;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.hint.common.OpenSideboardHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import mage.util.CardUtil;
import mage.watchers.Watcher;
/**
*
* @author weirddan455
*/
public final class Wish extends CardImpl {
public Wish(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}");
// You may play a card you own from outside the game this turn.
this.getSpellAbility().addEffect(new WishEffect());
this.getSpellAbility().setIdentifier(MageIdentifier.WishWatcher);
this.getSpellAbility().addWatcher(new WishWatcher());
this.getSpellAbility().addHint(OpenSideboardHint.instance);
}
private Wish(final Wish card) {
super(card);
}
@Override
public Wish copy() {
return new Wish(this);
}
}
class WishEffect extends OneShotEffect {
public WishEffect() {
super(Outcome.Benefit);
this.staticText = "You may play a card you own from outside the game this turn";
}
private WishEffect(final WishEffect effect) {
super(effect);
}
@Override
public WishEffect copy() {
return new WishEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null && !controller.getSideboard().isEmpty()) {
controller.lookAtCards(source, "Sideboard", controller.getSideboard(), game);
game.addEffect(new WishPlayFromSideboardEffect(), source);
return true;
}
return false;
}
}
class WishPlayFromSideboardEffect extends AsThoughEffectImpl {
public WishPlayFromSideboardEffect() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit, true);
}
private WishPlayFromSideboardEffect(final WishPlayFromSideboardEffect effect) {
super(effect);
}
@Override
public WishPlayFromSideboardEffect copy() {
return new WishPlayFromSideboardEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (source.getControllerId().equals(affectedControllerId)) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
UUID mainCardId = CardUtil.getMainCardId(game, objectId);
if (controller != null && sourceObject != null && controller.getSideboard().contains(mainCardId)) {
WishWatcher watcher = game.getState().getWatcher(WishWatcher.class);
return watcher != null && !watcher.isAbilityUsed(new MageObjectReference(sourceObject, game));
}
}
return false;
}
}
class WishWatcher extends Watcher {
private final Set<MageObjectReference> usedFrom = new HashSet<>();
WishWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if ((GameEvent.EventType.SPELL_CAST.equals(event.getType()) || GameEvent.EventType.LAND_PLAYED.equals(event.getType()))
&& event.hasApprovingIdentifier(MageIdentifier.WishWatcher)) {
usedFrom.add(event.getAdditionalReference().getApprovingMageObjectReference());
}
}
@Override
public void reset() {
super.reset();
usedFrom.clear();
}
boolean isAbilityUsed(MageObjectReference mor) {
return usedFrom.contains(mor);
}
}

View file

@ -263,6 +263,7 @@ public final class AdventuresInTheForgottenRealms extends ExpansionSet {
cards.add(new SetCardInfo("White Dragon", 41, Rarity.UNCOMMON, mage.cards.w.WhiteDragon.class)); cards.add(new SetCardInfo("White Dragon", 41, Rarity.UNCOMMON, mage.cards.w.WhiteDragon.class));
cards.add(new SetCardInfo("Wight", 127, Rarity.RARE, mage.cards.w.Wight.class)); cards.add(new SetCardInfo("Wight", 127, Rarity.RARE, mage.cards.w.Wight.class));
cards.add(new SetCardInfo("Wild Shape", 212, Rarity.UNCOMMON, mage.cards.w.WildShape.class)); cards.add(new SetCardInfo("Wild Shape", 212, Rarity.UNCOMMON, mage.cards.w.WildShape.class));
cards.add(new SetCardInfo("Wish", 166, Rarity.RARE, mage.cards.w.Wish.class));
cards.add(new SetCardInfo("Wizard Class", 81, Rarity.UNCOMMON, mage.cards.w.WizardClass.class)); cards.add(new SetCardInfo("Wizard Class", 81, Rarity.UNCOMMON, mage.cards.w.WizardClass.class));
cards.add(new SetCardInfo("Wizard's Spellbook", 82, Rarity.RARE, mage.cards.w.WizardsSpellbook.class)); cards.add(new SetCardInfo("Wizard's Spellbook", 82, Rarity.RARE, mage.cards.w.WizardsSpellbook.class));
cards.add(new SetCardInfo("Xorn", 167, Rarity.RARE, mage.cards.x.Xorn.class)); cards.add(new SetCardInfo("Xorn", 167, Rarity.RARE, mage.cards.x.Xorn.class));

View file

@ -94,47 +94,48 @@ public class KaradorGhostChieftainTest extends CardTestPlayerBase {
} }
@Test @Test
// @Ignore // It's not possible yet to select which ability to use to allow a asThoughtAs effect
public void test_castFromGraveyardWithDifferentApprovers() { public void test_castFromGraveyardWithDifferentApprovers() {
setStrictChooseMode(true); skipInitShuffling();
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
addCard(Zone.BATTLEFIELD, playerA, "Island", 3); addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
//
// {1}{B}: Target attacking Zombie gains indestructible until end of turn. // {1}{B}: Target attacking Zombie gains indestructible until end of turn.
addCard(Zone.LIBRARY, playerA, "Accursed Horde", 1); // Creature Zombie {3}{B} addCard(Zone.LIBRARY, playerA, "Accursed Horde", 1); // Creature Zombie {3}{B}
skipInitShuffling(); //
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 5); // Creature {1}{W}
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 5); //
// Karador, Ghost Chieftain costs {1} less to cast for each creature card in your graveyard. // Karador, Ghost Chieftain costs {1} less to cast for each creature card in your graveyard.
// During each of your turns, you may cast one creature card from your graveyard. // During each of your turns, you may cast one creature card from your graveyard.
addCard(Zone.HAND, playerA, "Karador, Ghost Chieftain");// {5}{B}{G}{W} addCard(Zone.HAND, playerA, "Karador, Ghost Chieftain");// {5}{B}{G}{W}
//
// When Gisa and Geralf enters the battlefield, put the top four cards of your library into your graveyard. // When Gisa and Geralf enters the battlefield, put the top four cards of your library into your graveyard.
// During each of your turns, you may cast a Zombie creature card from your graveyard. // During each of your turns, you may cast a Zombie creature card from your graveyard.
addCard(Zone.HAND, playerA, "Gisa and Geralf"); // CREATURE {2}{U}{B} (4/4) addCard(Zone.HAND, playerA, "Gisa and Geralf"); // CREATURE {2}{U}{B} (4/4)
// prepare spels with same AsThough effects and puts creature to graveyard
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Karador, Ghost Chieftain"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Karador, Ghost Chieftain");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gisa and Geralf"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gisa and Geralf");
// you play any creatures due to two approve objects
checkPlayableAbility("before", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Silvercoat Lion", true);
checkPlayableAbility("before", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Accursed Horde", true);
// cast zombie creature and approves by Karagar
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Accursed Horde"); castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Accursed Horde");
setChoice(playerA, "During each of your turns, you may cast a Zombie creature card from your graveyard"); // Choose the permitting object setChoice(playerA, "Karador, Ghost Chieftain"); // choose the permitting object
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); // you can't cast lion due to approving object (Gisa needs zombie)
checkPlayableAbility("after", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Silvercoat Lion", false);
checkPlayableAbility("after", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Accursed Horde", false);
setStrictChooseMode(true);
setStopAt(3, PhaseStep.BEGIN_COMBAT); setStopAt(3, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertPermanentCount(playerA, "Karador, Ghost Chieftain", 1);
assertPermanentCount(playerA, "Gisa and Geralf", 1);
assertPermanentCount(playerA, "Silvercoat Lion", 1);
assertPermanentCount(playerA, "Accursed Horde", 1);
} }
} }

View file

@ -1977,7 +1977,14 @@ public class TestPlayer implements Player {
//Assert.fail("Wrong choice"); //Assert.fail("Wrong choice");
} }
this.chooseStrictModeFailed("choice", game, choice.getMessage()); String choicesInfo;
if (choice.isKeyChoice()) {
choicesInfo = String.join("\n", choice.getKeyChoices().values());
} else {
choicesInfo = String.join("\n", choice.getChoices());
}
this.chooseStrictModeFailed("choice", game,
"Message: " + choice.getMessage() + "\nPossible choices:\n" + choicesInfo);
return computerPlayer.choose(outcome, choice, game); return computerPlayer.choose(outcome, choice, game);
} }

View file

@ -11,5 +11,6 @@ public enum MageIdentifier {
KaradorGhostChieftainWatcher, KaradorGhostChieftainWatcher,
KessDissidentMageWatcher, KessDissidentMageWatcher,
LurrusOfTheDreamDenWatcher, LurrusOfTheDreamDenWatcher,
MuldrothaTheGravetideWatcher MuldrothaTheGravetideWatcher,
WishWatcher
} }

View file

@ -0,0 +1,39 @@
package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.game.Game;
import mage.players.Player;
/**
* @author JayDi85
*/
public enum SideboardCardsYouControlCount implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
Player player = game.getPlayer(sourceAbility.getControllerId());
if (player == null) {
return 0;
}
return player.getSideboard().size();
}
@Override
public SideboardCardsYouControlCount copy() {
return instance;
}
@Override
public String toString() {
return "1";
}
@Override
public String getMessage() {
return "cards in your sideboard";
}
}

View file

@ -592,9 +592,14 @@ public class ContinuousEffects implements Serializable {
Map<String, String> keyChoices = new HashMap<>(); Map<String, String> keyChoices = new HashMap<>();
for (ApprovingObject approvingObject : possibleApprovingObjects) { for (ApprovingObject approvingObject : possibleApprovingObjects) {
MageObject mageObject = game.getObject(approvingObject.getApprovingAbility().getSourceId()); MageObject mageObject = game.getObject(approvingObject.getApprovingAbility().getSourceId());
keyChoices.put(approvingObject.getApprovingAbility().getId().toString(), String choiceKey = approvingObject.getApprovingAbility().getId().toString();
(approvingObject.getApprovingAbility().getRule(mageObject == null ? "" : mageObject.getName())) String choiceValue;
+ (mageObject == null ? "" : " (" + mageObject.getIdName() + ")")); if (mageObject == null) {
choiceValue = approvingObject.getApprovingAbility().getRule();
} else {
choiceValue = mageObject.getIdName() + ": " + approvingObject.getApprovingAbility().getRule(mageObject.getName());
}
keyChoices.put(choiceKey, choiceValue);
} }
Choice choicePermitting = new ChoiceImpl(true); Choice choicePermitting = new ChoiceImpl(true);
choicePermitting.setMessage("Choose the permitting object"); choicePermitting.setMessage("Choose the permitting object");

View file

@ -1,4 +1,3 @@
package mage.abilities.effects.common; package mage.abilities.effects.common;
import java.util.Set; import java.util.Set;

View file

@ -0,0 +1,26 @@
package mage.abilities.hint.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.common.SideboardCardsYouControlCount;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.game.Game;
/**
* @author JayDi85
*/
public enum OpenSideboardHint implements Hint {
instance;
private static final Hint hint = new ValueHint("Cards in your sideboard", SideboardCardsYouControlCount.instance);
@Override
public String getText(Game game, Ability ability) {
return hint.getText(game, ability) + " (<i>Right click on battlefield to open player's sideboard at any time</i>)";
}
@Override
public Hint copy() {
return instance;
}
}

View file

@ -59,5 +59,6 @@ public enum PlayerAction {
HOLD_PRIORITY, HOLD_PRIORITY,
UNHOLD_PRIORITY, UNHOLD_PRIORITY,
VIEW_LIMITED_DECK, VIEW_LIMITED_DECK,
VIEW_SIDEBOARD,
TOGGLE_RECORD_MACRO TOGGLE_RECORD_MACRO
} }

View file

@ -3646,13 +3646,22 @@ public abstract class PlayerImpl implements Player, Serializable {
} }
} }
// check to play companion cards // outside cards
if (fromAll || fromZone == Zone.OUTSIDE) { if (fromAll || fromZone == Zone.OUTSIDE) {
// companion cards
for (Cards companionCards : game.getState().getCompanion().values()) { for (Cards companionCards : game.getState().getCompanion().values()) {
for (Card card : companionCards.getCards(game)) { for (Card card : companionCards.getCards(game)) {
getPlayableFromObjectAll(game, Zone.OUTSIDE, card, availableMana, playable); getPlayableFromObjectAll(game, Zone.OUTSIDE, card, availableMana, playable);
} }
} }
// sideboard cards (example: Wish)
for (UUID sideboardCardId : this.getSideboard()) {
Card sideboardCard = game.getCard(sideboardCardId);
if (sideboardCard != null) {
getPlayableFromObjectAll(game, Zone.OUTSIDE, sideboardCard, availableMana, playable);
}
}
} }
// check if it's possible to play the top card of a library // check if it's possible to play the top card of a library