diff --git a/Mage.Client/src/main/java/mage/client/components/MageEditorPane.java b/Mage.Client/src/main/java/mage/client/components/MageEditorPane.java index 5fc6b0f33dd..d72fe8f5573 100644 --- a/Mage.Client/src/main/java/mage/client/components/MageEditorPane.java +++ b/Mage.Client/src/main/java/mage/client/components/MageEditorPane.java @@ -152,7 +152,7 @@ public class MageEditorPane extends JEditorPane { if (cardView == null) { Plane plane = Plane.createPlaneByFullName(cardName); if (plane != null) { - cardView = new CardView(new PlaneView(plane)); + cardView = new CardView(new PlaneView(plane, null)); } } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java index fe3ab7605d4..5d0267a646d 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java @@ -423,7 +423,7 @@ public class MageBook extends JComponent { } private void addEmblem(Emblem emblem, BigCard bigCard, UUID gameId, Rectangle rectangle) { - CardView cardView = new CardView(new EmblemView(emblem)); + CardView cardView = new CardView(new EmblemView(emblem, null)); addCard(cardView, bigCard, gameId, rectangle, false); } @@ -433,7 +433,7 @@ public class MageBook extends JComponent { } private void addPlane(Plane plane, BigCard bigCard, UUID gameId, Rectangle rectangle) { - CardView cardView = new CardView(new PlaneView(plane)); + CardView cardView = new CardView(new PlaneView(plane, null)); addCard(cardView, bigCard, gameId, rectangle, false); } diff --git a/Mage.Client/src/main/java/mage/client/dialog/CardHintsHelperDialog.java b/Mage.Client/src/main/java/mage/client/dialog/CardHintsHelperDialog.java index 22e67fe54d5..bccce8fa2a6 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/CardHintsHelperDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/CardHintsHelperDialog.java @@ -163,7 +163,7 @@ public class CardHintsHelperDialog extends MageDialog implements MageDesktopIcon settings.add(this.currentGroup.toString()); // from search - if (this.currentSearch.length() > 0 && !this.currentSearch.equals(SEARCH_EMPTY_TEXT)) { + if (!this.currentSearch.isEmpty() && !this.currentSearch.equals(SEARCH_EMPTY_TEXT)) { settings.add(this.currentSearch); } @@ -235,6 +235,11 @@ public class CardHintsHelperDialog extends MageDialog implements MageDesktopIcon this.lastHints.add(new CardHintInfo(currentPlayer, "hand", card)); }); + // helper emblems for better UX + this.lastGameView.getMyHelperEmblems().values().forEach(card -> { + this.lastHints.add(new CardHintInfo(currentPlayer, "xmage", card)); + }); + // stack this.lastGameView.getStack().values().forEach(card -> { this.lastHints.add(new CardHintInfo(currentPlayer, "stack", card)); diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java index a720107b3b3..a79316a1e46 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -223,20 +223,20 @@ public class TestCardRenderDialog extends MageDialog { return cardView; } - private AbilityView createEmblem(Emblem emblem) { - AbilityView emblemView = new AbilityView(emblem.getAbilities().get(0), emblem.getName(), new CardView(new EmblemView(emblem))); + private AbilityView createEmblem(Game game, Emblem emblem) { + AbilityView emblemView = new AbilityView(emblem.getAbilities().get(0), emblem.getName(), new CardView(new EmblemView(emblem, game))); emblemView.setName(emblem.getName()); return emblemView; } - private AbilityView createDungeon(Dungeon dungeon) { + private AbilityView createDungeon(Game game, Dungeon dungeon) { AbilityView emblemView = new AbilityView(dungeon.getAbilities().get(0), dungeon.getName(), new CardView(new DungeonView(dungeon))); emblemView.setName(dungeon.getName()); return emblemView; } - private AbilityView createPlane(Plane plane) { - AbilityView planeView = new AbilityView(plane.getAbilities().get(0), plane.getName(), new CardView(new PlaneView(plane))); + private AbilityView createPlane(Game game, Plane plane) { + AbilityView planeView = new AbilityView(plane.getAbilities().get(0), plane.getName(), new CardView(new PlaneView(plane, game))); planeView.setName(plane.getName()); return planeView; } diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 65d0361e3d6..442c771a816 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -145,6 +145,7 @@ public final class GamePanel extends javax.swing.JPanel { } this.game.getMyHand().values().forEach(c -> this.allCardsIndex.put(c.getId(), c)); + this.game.getMyHelperEmblems().values().forEach(c -> this.allCardsIndex.put(c.getId(), c)); this.game.getStack().values().forEach(c -> this.allCardsIndex.put(c.getId(), c)); this.game.getExile() .stream() diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index ed63fd19e4a..09cfdcc096a 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -821,7 +821,7 @@ public class CardView extends SimpleCardView { this.mageObjectType = MageObjectType.EMBLEM; Emblem emblem = (Emblem) object; this.rarity = Rarity.SPECIAL; - this.rules = new ArrayList<>(emblem.getAbilities().getRules(emblem.getName())); + this.rules = new ArrayList<>(emblem.getAbilities().getRules(game, emblem)); } else if (object instanceof Dungeon) { this.mageObjectType = MageObjectType.DUNGEON; Dungeon dungeon = (Dungeon) object; @@ -834,14 +834,14 @@ public class CardView extends SimpleCardView { this.frameStyle = FrameStyle.M15_NORMAL; // Display in landscape/rotated/on its side this.rotate = true; - this.rules = new ArrayList<>(plane.getAbilities().getRules(plane.getName())); + this.rules = new ArrayList<>(plane.getAbilities().getRules(game, plane)); } else if (object instanceof Designation) { this.mageObjectType = MageObjectType.DESIGNATION; Designation designation = (Designation) object; this.rarity = Rarity.SPECIAL; this.frameStyle = FrameStyle.M15_NORMAL; // Display in landscape/rotated/on its side - this.rules = new ArrayList<>(designation.getAbilities().getRules(designation.getName())); + this.rules = new ArrayList<>(designation.getAbilities().getRules(game, designation)); } if (this.rarity == null && object instanceof StackAbility) { StackAbility stackAbility = (StackAbility) object; @@ -1106,7 +1106,7 @@ public class CardView extends SimpleCardView { this.name = token.getName(); this.displayName = token.getName(); this.displayFullName = token.getName(); - this.rules = new ArrayList<>(token.getAbilities().getRules(this.name)); + this.rules = new ArrayList<>(token.getAbilities().getRules(game, token)); this.power = token.getPower().toString(); this.toughness = token.getToughness().toString(); this.loyalty = ""; diff --git a/Mage.Common/src/main/java/mage/view/CardsView.java b/Mage.Common/src/main/java/mage/view/CardsView.java index c60e1a3c81e..6c25c70d9cb 100644 --- a/Mage.Common/src/main/java/mage/view/CardsView.java +++ b/Mage.Common/src/main/java/mage/view/CardsView.java @@ -107,26 +107,22 @@ public class CardsView extends LinkedHashMap { isCard = true; } if (sourceObject instanceof Emblem) { - // Emblems are not normally OUTSIDE, except the special Radiation Emblem from rad counters. - abilityView = new AbilityView(ability, sourceObject.getName(), new CardView(new EmblemView((Emblem) sourceObject))); + // emblems are not normally OUTSIDE, except the helper emblems like Radiation + abilityView = new AbilityView(ability, sourceObject.getName(), new CardView(new EmblemView((Emblem) sourceObject, game))); abilityView.setName(sourceObject.getName()); } break; case COMMAND: sourceObject = game.getObject(ability.getSourceId()); if (sourceObject instanceof Emblem) { -// Card sourceCard = (Card) ((Emblem) sourceObject).getSourceObject(); -// if (sourceCard == null) { -// throw new IllegalArgumentException("Source card for emblem not found."); -// } - abilityView = new AbilityView(ability, sourceObject.getName(), new CardView(new EmblemView((Emblem) sourceObject))); + abilityView = new AbilityView(ability, sourceObject.getName(), new CardView(new EmblemView((Emblem) sourceObject, game))); abilityView.setName(sourceObject.getName()); // abilityView.setExpansionSetCode(sourceCard.getExpansionSetCode()); } else if (sourceObject instanceof Dungeon) { abilityView = new AbilityView(ability, sourceObject.getName(), new CardView(new DungeonView((Dungeon) sourceObject))); abilityView.setName(sourceObject.getName()); } else if (sourceObject instanceof Plane) { - abilityView = new AbilityView(ability, sourceObject.getName(), new CardView(new PlaneView((Plane) sourceObject))); + abilityView = new AbilityView(ability, sourceObject.getName(), new CardView(new PlaneView((Plane) sourceObject, game))); abilityView.setName(sourceObject.getName()); } break; diff --git a/Mage.Common/src/main/java/mage/view/EmblemView.java b/Mage.Common/src/main/java/mage/view/EmblemView.java index 7f360d3823d..1cdb29f85e9 100644 --- a/Mage.Common/src/main/java/mage/view/EmblemView.java +++ b/Mage.Common/src/main/java/mage/view/EmblemView.java @@ -1,5 +1,6 @@ package mage.view; +import mage.game.Game; import mage.game.command.Emblem; import mage.game.command.emblems.EmblemOfCard; import mage.players.PlayableObjectStats; @@ -23,13 +24,13 @@ public class EmblemView implements CommandObjectView, Serializable { protected List rules; protected PlayableObjectStats playableStats = new PlayableObjectStats(); - public EmblemView(Emblem emblem) { + public EmblemView(Emblem emblem, Game game) { this.id = emblem.getId(); this.name = emblem.getName(); this.imageFileName = emblem.getImageFileName(); this.imageNumber = emblem.getImageNumber(); this.expansionSetCode = emblem.getExpansionSetCode(); - this.rules = emblem.getAbilities().getRules(emblem.getName()); + this.rules = emblem.getAbilities().getRules(game, emblem); if (emblem instanceof EmblemOfCard) { cardNumber = emblem.getCardNumber(); usesVariousArt = ((EmblemOfCard) emblem).getUsesVariousArt(); diff --git a/Mage.Common/src/main/java/mage/view/GameView.java b/Mage.Common/src/main/java/mage/view/GameView.java index 46701309e56..b55f4ea8c37 100644 --- a/Mage.Common/src/main/java/mage/view/GameView.java +++ b/Mage.Common/src/main/java/mage/view/GameView.java @@ -44,6 +44,7 @@ public class GameView implements Serializable { private final List players = new ArrayList<>(); private UUID myPlayerId = null; // null for watcher private final CardsView myHand = new CardsView(); + private final CardsView myHelperEmblems = new CardsView(); private PlayableObjectsList canPlayObjects; private final Map opponentHands = new HashMap<>(); private final Map watchedHands = new HashMap<>(); @@ -75,6 +76,11 @@ public class GameView implements Serializable { createdForPlayer = player; this.myPlayerId = player.getId(); this.myHand.putAll(new CardsView(game, player.getHand().getCards(game), createdForPlayerId)); + state.getHelperEmblems().stream() + .filter(emblem -> emblem.isControlledBy(player.getId())) + .forEach(emblem -> { + this.myHelperEmblems.put(emblem.getId(), new CardView(new EmblemView(emblem, game))); + }); } } for (StackObject stackObject : state.getStack()) { @@ -112,7 +118,7 @@ public class GameView implements Serializable { stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, token.getName(), token, new CardView(token, game))); checkPaid(stackObject.getId(), (StackAbility) stackObject); } else if (object instanceof Emblem) { - CardView cardView = new CardView(new EmblemView((Emblem) object)); + CardView cardView = new CardView(new EmblemView((Emblem) object, game)); // Card sourceCard = (Card) ((Emblem) object).getSourceObject(); stackObject.setName(object.getName()); // ((StackAbility) stackObject).setExpansionSetCode(sourceCard.getExpansionSetCode()); @@ -126,7 +132,7 @@ public class GameView implements Serializable { new StackAbilityView(game, (StackAbility) stackObject, object.getName(), object, cardView)); checkPaid(stackObject.getId(), ((StackAbility) stackObject)); } else if (object instanceof Plane) { - CardView cardView = new CardView(new PlaneView((Plane) object)); + CardView cardView = new CardView(new PlaneView((Plane) object, game)); stackObject.setName(object.getName()); stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, object.getName(), object, cardView)); @@ -239,6 +245,10 @@ public class GameView implements Serializable { return myHand; } + public CardsView getMyHelperEmblems() { + return myHelperEmblems; + } + public PlayerView getMyPlayer() { if (this.myPlayerId == null) { return null; diff --git a/Mage.Common/src/main/java/mage/view/PlaneView.java b/Mage.Common/src/main/java/mage/view/PlaneView.java index d8511132994..5914be79850 100644 --- a/Mage.Common/src/main/java/mage/view/PlaneView.java +++ b/Mage.Common/src/main/java/mage/view/PlaneView.java @@ -1,5 +1,6 @@ package mage.view; +import mage.game.Game; import mage.game.command.Plane; import mage.players.PlayableObjectStats; @@ -20,13 +21,13 @@ public class PlaneView implements CommandObjectView, Serializable { protected List rules; protected PlayableObjectStats playableStats = new PlayableObjectStats(); - public PlaneView(Plane plane) { + public PlaneView(Plane plane, Game game) { this.id = plane.getId(); this.name = plane.getName(); this.imageFileName = plane.getImageFileName(); this.imageNumber = plane.getImageNumber(); this.expansionSetCode = plane.getExpansionSetCode(); - this.rules = plane.getAbilities().getRules(plane.getName()); + this.rules = plane.getAbilities().getRules(game, plane); } @Override diff --git a/Mage.Common/src/main/java/mage/view/PlayerView.java b/Mage.Common/src/main/java/mage/view/PlayerView.java index 3c3ee15d7f1..41101fa5a35 100644 --- a/Mage.Common/src/main/java/mage/view/PlayerView.java +++ b/Mage.Common/src/main/java/mage/view/PlayerView.java @@ -40,6 +40,7 @@ public class PlayerView implements Serializable { private final CardsView graveyard = new CardsView(); private final CardsView exile = new CardsView(); private final CardsView sideboard = new CardsView(); + private final CardsView helperCards = new CardsView(); private final Map battlefield = new LinkedHashMap<>(); private final CardView topCard; private final UserData userData; @@ -120,7 +121,7 @@ public class PlayerView implements Serializable { if (commandObject instanceof Emblem) { Emblem emblem = (Emblem) commandObject; if (emblem.getControllerId().equals(this.playerId)) { - commandList.add(new EmblemView(emblem)); + commandList.add(new EmblemView(emblem, game)); } } else if (commandObject instanceof Dungeon) { Dungeon dungeon = (Dungeon) commandObject; @@ -130,7 +131,7 @@ public class PlayerView implements Serializable { } else if (commandObject instanceof Plane) { Plane plane = (Plane) commandObject; // Planes are universal and all players can see them. - commandList.add(new PlaneView(plane)); + commandList.add(new PlaneView(plane, game)); } else if (commandObject instanceof Commander) { Commander commander = (Commander) commandObject; if (commander.getControllerId().equals(this.playerId)) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/TappedForManaFromMultipleEffects.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/TappedForManaFromMultipleEffects.java index f4b260a15a5..2bfbb2945eb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/TappedForManaFromMultipleEffects.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/TappedForManaFromMultipleEffects.java @@ -156,9 +156,7 @@ public class TappedForManaFromMultipleEffects extends CardTestPlayerBase { activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice a Fo"); setChoice(playerA, "Forest"); // sacrifice - setChoice(playerA, "Green"); - setChoice(playerA, "Green"); - setChoice(playerA, "Green"); + setChoiceAmount(playerA, 0, 3); // x0 red, x3 green checkManaPool("must produce green", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "G", 3); setStrictChooseMode(true); @@ -177,9 +175,7 @@ public class TappedForManaFromMultipleEffects extends CardTestPlayerBase { activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice a Fo"); setChoice(playerA, "Forest"); // sacrifice - setChoice(playerA, "Green"); - setChoice(playerA, "Green"); - setChoice(playerA, "Green"); + setChoiceAmount(playerA, 0, 3); // x0 red, x3 green checkManaPool("must produce green", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "G", 3 * 2); // double by mana reflect setStrictChooseMode(true); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index dd1d10744d8..7b656cf461e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -2892,7 +2892,7 @@ public class TestPlayer implements Player { break; } } - Assert.fail(String.format("Missing choice in multi amount: %s (pos %d - %s)", type.getHeader(), i + 1, messages.get(i))); + Assert.fail(String.format("Missing choice in multi amount: %s (pos %d - %s)", type.getHeader(), i, messages)); } // extra check diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index 6e3921309b2..014eaa5a1e3 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -2258,7 +2258,7 @@ public class VerifyCardDataTest { System.out.println(); System.out.println(card.getName() + " " + card.getManaCost().getText()); if (card instanceof SplitCard || card instanceof ModalDoubleFacedCard) { - card.getAbilities().getRules(card.getName()).forEach(this::printAbilityText); + card.getAbilities().getRules().forEach(this::printAbilityText); } else { card.getRules().forEach(this::printAbilityText); } diff --git a/Mage/src/main/java/mage/abilities/Abilities.java b/Mage/src/main/java/mage/abilities/Abilities.java index ffcb0874032..88cefd0f9b1 100644 --- a/Mage/src/main/java/mage/abilities/Abilities.java +++ b/Mage/src/main/java/mage/abilities/Abilities.java @@ -1,6 +1,7 @@ package mage.abilities; +import mage.MageObject; import mage.abilities.keyword.ProtectionAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.constants.Zone; @@ -18,7 +19,6 @@ import java.util.function.Predicate; * interface for this. * * @param The ability type this collection will hold. - * * @see mage.abilities.AbilitiesImpl * @see mage.abilities.DelayedTriggeredAbilities * @see mage.abilities.SpecialActions @@ -27,25 +27,27 @@ import java.util.function.Predicate; public interface Abilities extends List, Serializable { /** - * Retrieves a {@link List}<{@link String}> of ability texts for the - * given source. - * - * @param source The source to retrieve ability texts. - * @return the {@link List}<{@link String}> of ability texts. - * - * @see mage.cards.CardImpl#getRules() - * @see mage.abilities.keyword.LevelAbility#getRule() + * Return rules as part of another rules. Use it for text generation. */ - List getRules(String source); + List getRules(); - List getRules(String source, boolean capitalize); + /** + * Return rules as part of another rules. Use it for text generation. + */ + List getRules(boolean capitalize); + + /** + * Return full rules with card hints. Use it for user's data like GameView + * + * @param game on null will ignore card hints + */ + List getRules(Game game, MageObject object); /** * Retrieves all activated abilities for the given {@link Zone}. * * @param zone The {@link Zone} for which abilities should be retrieved. * @return All abilities for the given {@link Zone} - * * @see mage.cards.CardImpl#getSpellAbility() */ Abilities getActivatedAbilities(Zone zone); @@ -55,7 +57,6 @@ public interface Abilities extends List, Serializable { * * @param zone The {@link Zone} for which abilities should be retrieved. * @return All abilities for the given {@link Zone} - * * @see mage.cards.CardImpl#getSpellAbility() */ Abilities getPlayableAbilities(Zone zone); @@ -65,10 +66,9 @@ public interface Abilities extends List, Serializable { * given {@link Zone}. * * @param zone The {@link Zone} to search for - * {@link ActivatedManaAbilityImpl mana abilities}. + * {@link ActivatedManaAbilityImpl mana abilities}. * @return All {@link ActivatedManaAbilityImpl mana abilities} for the given * {@link Zone}. - * * @see mage.cards.CardImpl#getMana() * @see mage.players.PlayerImpl#getManaAvailable(mage.game.Game) * @see mage.players.PlayerImpl#getAvailableManaProducers(mage.game.Game) @@ -80,12 +80,11 @@ public interface Abilities extends List, Serializable { * Retrieves all {@link ActivatedManaAbilityImpl mana abilities} in the * given {@link Zone} that can be used. * - * @param zone The {@link Zone} to search for - * {@link ActivatedManaAbilityImpl mana abilities}. + * @param zone The {@link Zone} to search for + * {@link ActivatedManaAbilityImpl mana abilities}. * @param playerId The id of the player to check availability for * @return All {@link ActivatedManaAbilityImpl mana abilities} for the given * {@link Zone} that can be used. - * * @see mage.cards.CardImpl#getMana() * @see mage.players.PlayerImpl#getManaAvailable(mage.game.Game) * @see mage.players.PlayerImpl#getAvailableManaProducers(mage.game.Game) @@ -99,22 +98,16 @@ public interface Abilities extends List, Serializable { * @param zone The {@link Zone} to search for {@link StaticAbility} * @return All {@link StaticAbility static abilities} in the given * {@link Zone} - * - * @see - * mage.abilities.effects.ContinuousEffects#getLayeredEffects(mage.game.Game) - * @see - * mage.abilities.effects.ContinuousEffects#getApplicableRequirementEffects(mage.game.permanent.Permanent, + * @see mage.abilities.effects.ContinuousEffects#getLayeredEffects(mage.game.Game) + * @see mage.abilities.effects.ContinuousEffects#getApplicableRequirementEffects(mage.game.permanent.Permanent, * mage.game.Game) - * @see - * mage.abilities.effects.ContinuousEffects#getApplicableRestrictionEffects(mage.game.permanent.Permanent, + * @see mage.abilities.effects.ContinuousEffects#getApplicableRestrictionEffects(mage.game.permanent.Permanent, * mage.game.Game) - * @see - * mage.abilities.effects.ContinuousEffects#getApplicableReplacementEffects(mage.game.events.GameEvent, + * @see mage.abilities.effects.ContinuousEffects#getApplicableReplacementEffects(mage.game.events.GameEvent, * mage.game.Game) * @see mage.abilities.effects.ContinuousEffects#asThough(java.util.UUID, * mage.constants.AsThoughEffectType, mage.game.Game) - * @see - * mage.abilities.effects.ContinuousEffects#costModification(mage.abilities.Ability, + * @see mage.abilities.effects.ContinuousEffects#costModification(mage.abilities.Ability, * mage.game.Game) */ Abilities getStaticAbilities(Zone zone); @@ -131,16 +124,13 @@ public interface Abilities extends List, Serializable { * {@link Zone}. * * @param zone The {@link Zone} to search for - * {@link TriggeredAbility triggered abilities} + * {@link TriggeredAbility triggered abilities} * @return All found {@link TriggeredAbility triggered abilities}. - * * @see mage.cards.CardImpl#checkTriggers(mage.constants.Zone, * mage.game.events.GameEvent, mage.game.Game) - * @see - * mage.game.permanent.PermanentImpl#checkTriggers(mage.game.events.GameEvent, + * @see mage.game.permanent.PermanentImpl#checkTriggers(mage.game.events.GameEvent, * mage.game.Game) - * @see - * mage.game.permanent.PermanentCard#checkPermanentOnlyTriggers(mage.game.events.ZoneChangeEvent, + * @see mage.game.permanent.PermanentCard#checkPermanentOnlyTriggers(mage.game.events.ZoneChangeEvent, * mage.game.Game) */ Abilities getTriggeredAbilities(Zone zone); @@ -149,16 +139,17 @@ public interface Abilities extends List, Serializable { * Retrieves all {@link ProtectionAbility protection abilities}. * * @return All found {@link ProtectionAbility protection abilities}. - * * @see mage.game.permanent.PermanentImpl#hasProtectionFrom(mage.MageObject) * @see mage.players.PlayerImpl#hasProtectionFrom(mage.MageObject) * @see mage.players.PlayerImpl#canDamage(mage.MageObject) */ Abilities getProtectionAbilities(); + Abilities getAllAbilities(); + /** * TODO Method is unused, keep it around? - * + *

* The only implementation seems to want to use this for totally a set of * abilities by some arbitrary numeral value. Possibly a good method to be * used by the AI's? @@ -172,7 +163,6 @@ public interface Abilities extends List, Serializable { * Sets the controller of this set of abilities. * * @param controllerId - * * @see mage.cards.CardImpl#setControllerId(java.util.UUID) * @see mage.cards.CardImpl#setOwnerId(java.util.UUID) * @see mage.game.permanent.PermanentImpl#changeControllerId(java.util.UUID, @@ -185,7 +175,6 @@ public interface Abilities extends List, Serializable { * Sets the source of this set of abilities. * * @param sourceId - * * @see mage.cards.CardImpl#assignNewId() */ void setSourceId(UUID sourceId); @@ -212,7 +201,7 @@ public interface Abilities extends List, Serializable { /** * TODO Method is unused, keep it around? - * + *

* Gets the ability represented by the given abilityId. * * @param abilityId @@ -224,7 +213,7 @@ public interface Abilities extends List, Serializable { * TODO The usage of this method seems redundant to that of * {@link #containsKey(java.util.UUID)} minus the fact that it searches for * exact instances instead of id's of singleton Abilities. - * + *

* Searches for the exact instance of the passed in ability. * * @param ability @@ -261,10 +250,11 @@ public interface Abilities extends List, Serializable { /** * Returns true if one or more of the abilities are activated mana abilities with the pollDependant flag set to true. - * @return + * + * @return */ boolean hasPoolDependantAbilities(); - + /** * Copies this set of abilities. This copy should be new instances of all * the contained abilities. @@ -275,12 +265,15 @@ public interface Abilities extends List, Serializable { String getValue(); - @Deprecated // use permanent.removeAbility instead + @Deprecated + // use permanent.removeAbility instead boolean remove(Object o); - @Deprecated // use permanent.removeAbility instead + @Deprecated + // use permanent.removeAbility instead boolean removeAll(Collection c); - @Deprecated // use permanent.removeAbility instead + @Deprecated + // use permanent.removeAbility instead boolean removeIf(Predicate filter); } diff --git a/Mage/src/main/java/mage/abilities/AbilitiesImpl.java b/Mage/src/main/java/mage/abilities/AbilitiesImpl.java index 4329c62976b..87758f7fc21 100644 --- a/Mage/src/main/java/mage/abilities/AbilitiesImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilitiesImpl.java @@ -1,5 +1,6 @@ package mage.abilities; +import mage.MageObject; import mage.abilities.common.ZoneChangeTriggeredAbility; import mage.abilities.costs.Cost; import mage.abilities.keyword.ProtectionAbility; @@ -7,6 +8,7 @@ import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.abilities.mana.ManaAbility; import mage.constants.Zone; import mage.game.Game; +import mage.util.CardUtil; import mage.util.ThreadLocalStringBuilder; import org.apache.log4j.Logger; @@ -44,12 +46,12 @@ public class AbilitiesImpl extends ArrayList implements Ab } @Override - public List getRules(String source) { - return getRules(source, true); + public List getRules() { + return getRules(true); } @Override - public List getRules(String source, boolean capitalize) { + public List getRules(boolean capitalize) { List rules = new ArrayList<>(); for (T ability : this) { @@ -102,6 +104,12 @@ public class AbilitiesImpl extends ArrayList implements Ab return rules; } + @Override + public List getRules(Game game, MageObject object) { + Abilities sourceAbilities = this.getAllAbilities(); + return CardUtil.getCardRulesWithAdditionalInfo(game, object, sourceAbilities, sourceAbilities); + } + /** * Activated Ability in the engine are broader than in the rules. * Notably SpellAbility & PlayLandAbility are ActivatedAbility, @@ -193,6 +201,11 @@ public class AbilitiesImpl extends ArrayList implements Ab .collect(Collectors.toCollection(AbilitiesImpl::new)); } + @Override + public Abilities getAllAbilities() { + return stream().collect(Collectors.toCollection(AbilitiesImpl::new)); + } + @Override public void setControllerId(UUID controllerId) { for (Ability ability : this) { diff --git a/Mage/src/main/java/mage/abilities/CompoundAbility.java b/Mage/src/main/java/mage/abilities/CompoundAbility.java index ed0bf13ba61..6d0d6d75d99 100644 --- a/Mage/src/main/java/mage/abilities/CompoundAbility.java +++ b/Mage/src/main/java/mage/abilities/CompoundAbility.java @@ -31,7 +31,7 @@ public class CompoundAbility extends AbilitiesImpl { } StringBuilder sb = new StringBuilder(); - List rules = super.getRules(null, false); + List rules = super.getRules(false); for (int index = 0; index < rules.size(); index++) { if (index > 0) { if (index < rules.size() - 1) { diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java index 83bde214870..9d8ab33cef6 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java @@ -309,7 +309,7 @@ public class TriggeredAbilities extends LinkedHashMap public void removeAbilitiesOfNonExistingSources(Game game) { // e.g. Token that had triggered abilities entrySet().removeIf(entry -> game.getObject(entry.getValue().getSourceId()) == null - && game.getState().getInherentEmblems().stream().noneMatch(emblem -> emblem.getId().equals(entry.getValue().getSourceId())) + && game.getState().getHelperEmblems().stream().noneMatch(emblem -> emblem.getId().equals(entry.getValue().getSourceId())) && game.getState().getDesignations().stream().noneMatch(designation -> designation.getId().equals(entry.getValue().getSourceId()))); } diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java index 2e385f22d8c..1d979a1931a 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java @@ -1338,7 +1338,7 @@ public class ContinuousEffects implements Serializable { effectsMap.put(key, object.getIdName() + ": " + ability.getRule(object.getName())); objectsMap.put(key, object); } else { - effectsMap.put(key, entry.getKey().getText(null)); + effectsMap.put(key, entry.getKey().getText(ability.getModes().getMode())); objectsMap.put(key, null); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/GetEmblemEffect.java b/Mage/src/main/java/mage/abilities/effects/common/GetEmblemEffect.java index 3ff87f971ca..034506609de 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/GetEmblemEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/GetEmblemEffect.java @@ -46,7 +46,7 @@ public class GetEmblemEffect extends OneShotEffect { public String getText() { StringBuilder sb = new StringBuilder(); sb.append("you get an emblem with \""); - List rules = emblem.getAbilities().getRules(null); + List rules = emblem.getAbilities().getRules(); sb.append(rules.get(0)); if (rules.size() == 2) { sb.append("\" and \""); diff --git a/Mage/src/main/java/mage/abilities/effects/common/GetEmblemTargetPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/GetEmblemTargetPlayerEffect.java index 57cb5f0d2f6..f9ea3910981 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/GetEmblemTargetPlayerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/GetEmblemTargetPlayerEffect.java @@ -9,6 +9,7 @@ import mage.game.Game; import mage.game.command.Emblem; import mage.players.Player; +import java.util.List; import java.util.stream.Collectors; /** @@ -54,6 +55,6 @@ public class GetEmblemTargetPlayerEffect extends OneShotEffect { return staticText; } return getTargetPointer().describeTargets(mode.getTargets(), "that player") - + " gets an emblem with \"" + String.join("; ", emblem.getAbilities().getRules(null)) + "\""; + + " gets an emblem with \"" + String.join("; ", emblem.getAbilities().getRules()) + "\""; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/LevelerCardBuilder.java b/Mage/src/main/java/mage/abilities/keyword/LevelerCardBuilder.java index e84a97f6d4a..933f6783a64 100644 --- a/Mage/src/main/java/mage/abilities/keyword/LevelerCardBuilder.java +++ b/Mage/src/main/java/mage/abilities/keyword/LevelerCardBuilder.java @@ -192,7 +192,7 @@ public class LevelerCardBuilder { sb.append(power); sb.append('/'); sb.append(toughness); - List abilityText = abilities.getRules("{this}"); + List abilityText = abilities.getRules(); if (!abilityText.isEmpty()) { sb.append("
"); sb.append(abilityText.stream().collect(Collectors.joining("
"))); diff --git a/Mage/src/main/java/mage/cards/AdventureCard.java b/Mage/src/main/java/mage/cards/AdventureCard.java index 70cc1f4e7e0..413d9d6e026 100644 --- a/Mage/src/main/java/mage/cards/AdventureCard.java +++ b/Mage/src/main/java/mage/cards/AdventureCard.java @@ -134,7 +134,7 @@ public abstract class AdventureCard extends CardImpl { public List getSharedRules(Game game) { // rules without spellcard Abilities sourceAbilities = this.getSharedAbilities(game); - return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(), sourceAbilities, sourceAbilities); + return CardUtil.getCardRulesWithAdditionalInfo(game, this, sourceAbilities, sourceAbilities); } @Override diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index 10aad261a1d..651baee0613 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -230,13 +230,13 @@ public abstract class CardImpl extends MageObjectImpl implements Card { @Override public List getRules() { Abilities sourceAbilities = this.getAbilities(); - return CardUtil.getCardRulesWithAdditionalInfo(this.getId(), this.getName(), sourceAbilities, sourceAbilities); + return CardUtil.getCardRulesWithAdditionalInfo(this, sourceAbilities, sourceAbilities); } @Override public List getRules(Game game) { Abilities sourceAbilities = this.getAbilities(game); - return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(), sourceAbilities, sourceAbilities); + return CardUtil.getCardRulesWithAdditionalInfo(game, this, sourceAbilities, sourceAbilities); } /** diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacedCard.java b/Mage/src/main/java/mage/cards/ModalDoubleFacedCard.java index 41466a0e156..eb8f3fcd19f 100644 --- a/Mage/src/main/java/mage/cards/ModalDoubleFacedCard.java +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacedCard.java @@ -358,8 +358,7 @@ public abstract class ModalDoubleFacedCard extends CardImpl implements CardWithH // rules must show only main side (another side visible by toggle/transform button in GUI) // card hints from both sides return CardUtil.getCardRulesWithAdditionalInfo( - this.getId(), - this.getName(), + this, this.getInnerAbilities(true, false), this.getInnerAbilities(true, true) ); @@ -371,8 +370,7 @@ public abstract class ModalDoubleFacedCard extends CardImpl implements CardWithH // card hints from both sides return CardUtil.getCardRulesWithAdditionalInfo( game, - this.getId(), - this.getName(), + this, this.getInnerAbilities(game, true, false), this.getInnerAbilities(game, true, true) ); diff --git a/Mage/src/main/java/mage/cards/SplitCard.java b/Mage/src/main/java/mage/cards/SplitCard.java index ce2b387d7c0..aafccf957b2 100644 --- a/Mage/src/main/java/mage/cards/SplitCard.java +++ b/Mage/src/main/java/mage/cards/SplitCard.java @@ -189,8 +189,7 @@ public abstract class SplitCard extends CardImpl implements CardWithHalves { public List getRules() { Abilities sourceAbilities = this.getAbilities(); List res = CardUtil.getCardRulesWithAdditionalInfo( - this.getId(), - this.getName(), + this, sourceAbilities, sourceAbilities ); @@ -205,8 +204,7 @@ public abstract class SplitCard extends CardImpl implements CardWithHalves { Abilities sourceAbilities = this.getAbilities(game); List res = CardUtil.getCardRulesWithAdditionalInfo( game, - this.getId(), - this.getName(), + this, sourceAbilities, sourceAbilities ); diff --git a/Mage/src/main/java/mage/cards/mock/MockSplitCard.java b/Mage/src/main/java/mage/cards/mock/MockSplitCard.java index c623c5eb21b..8841914407b 100644 --- a/Mage/src/main/java/mage/cards/mock/MockSplitCard.java +++ b/Mage/src/main/java/mage/cards/mock/MockSplitCard.java @@ -119,8 +119,7 @@ public class MockSplitCard extends SplitCard implements MockableCard { // so a MockSplitCard must ignore it (duplicate fix) Abilities sourceAbilities = this.getAbilities(); return CardUtil.getCardRulesWithAdditionalInfo( - this.getId(), - this.getName(), + this, sourceAbilities, sourceAbilities ); diff --git a/Mage/src/main/java/mage/cards/repository/TokenRepository.java b/Mage/src/main/java/mage/cards/repository/TokenRepository.java index 8c845da2f4e..eeed1273346 100644 --- a/Mage/src/main/java/mage/cards/repository/TokenRepository.java +++ b/Mage/src/main/java/mage/cards/repository/TokenRepository.java @@ -33,6 +33,7 @@ public enum TokenRepository { public static final String XMAGE_IMAGE_NAME_NIGHT = "Night"; public static final String XMAGE_IMAGE_NAME_THE_MONARCH = "The Monarch"; public static final String XMAGE_IMAGE_NAME_RADIATION = "Radiation"; + public static final String XMAGE_IMAGE_NAME_HELPER_EMBLEM = "Helper Emblem"; private static final Logger logger = Logger.getLogger(TokenRepository.class); @@ -305,6 +306,10 @@ public enum TokenRepository { // Radiation (for trigger) res.add(createXmageToken(XMAGE_IMAGE_NAME_RADIATION, 1, "https://api.scryfall.com/cards/tpip/22/en?format=image")); + // Helper emblem (for global card hints) + // use backface for it + res.add(createXmageToken(XMAGE_IMAGE_NAME_HELPER_EMBLEM, 1, "https://upload.wikimedia.org/wikipedia/en/a/aa/Magic_the_gathering-card_back.jpg")); + return res; } diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 2aee96a3875..4402a485a42 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -48,6 +48,7 @@ import mage.game.command.dungeons.UndercityDungeon; import mage.game.command.emblems.EmblemOfCard; import mage.game.command.emblems.RadiationEmblem; import mage.game.command.emblems.TheRingEmblem; +import mage.game.command.emblems.XmageHelperEmblem; import mage.game.events.*; import mage.game.events.TableEvent.EventType; import mage.game.mulligan.Mulligan; @@ -444,7 +445,7 @@ public abstract class GameImpl implements Game { return designation; } } - for (Emblem emblem : state.getInherentEmblems()) { + for (Emblem emblem : state.getHelperEmblems()) { if (emblem.getId().equals(objectId)) { return emblem; } @@ -1400,13 +1401,7 @@ public abstract class GameImpl implements Game { } } - // Rad counter mechanic for every player - for (UUID playerId : state.getPlayerList(startingPlayerId)) { - // This is not a real emblem. Just a fake source for the - // inherent trigger ability related to Rad counters - // Faking a source just to display something on the stack ability. - state.addInherentEmblem(new RadiationEmblem(), playerId); - } + initGameDefaultHelperEmblems(); } public void initGameDefaultWatchers() { @@ -1447,6 +1442,22 @@ public abstract class GameImpl implements Game { getState().addWatcher(bloodthirstWatcher); } + public void initGameDefaultHelperEmblems() { + + // Rad Counter's trigger source + for (UUID playerId : state.getPlayerList(startingPlayerId)) { + // This is not a real emblem. Just a fake source for the + // inherent trigger ability related to Rad counters + // Faking a source just to display something on the stack ability. + state.addHelperEmblem(new RadiationEmblem(), playerId); + } + + // global card hints for better UX + for (UUID playerId : state.getPlayerList(startingPlayerId)) { + state.addHelperEmblem(new XmageHelperEmblem().withCardHint("storm counter", StormAbility.getHint()), playerId); + } + } + protected void sendStartMessage(Player choosingPlayer, Player startingPlayer) { StringBuilder message = new StringBuilder(); if (choosingPlayer != null) { diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 3f8ef44ff42..bc23042c5a7 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -85,7 +85,7 @@ public class GameState implements Serializable, Copyable { private boolean isPlaneChase; private List seenPlanes = new ArrayList<>(); private List designations = new ArrayList<>(); - private List inherentEmblems = new ArrayList<>(); + private List helperEmblems = new ArrayList<>(); // fake emblems for inner usage like better UX private Exile exile; private Battlefield battlefield; private int turnNum = 1; @@ -158,7 +158,7 @@ public class GameState implements Serializable, Copyable { this.isPlaneChase = state.isPlaneChase; this.seenPlanes.addAll(state.seenPlanes); this.designations.addAll(state.designations); - this.inherentEmblems = CardUtil.deepCopyObject(state.inherentEmblems); + this.helperEmblems = CardUtil.deepCopyObject(state.helperEmblems); this.exile = state.exile.copy(); this.battlefield = state.battlefield.copy(); this.turnNum = state.turnNum; @@ -206,7 +206,7 @@ public class GameState implements Serializable, Copyable { exile.clear(); command.clear(); designations.clear(); - inherentEmblems.clear(); + helperEmblems.clear(); seenPlanes.clear(); isPlaneChase = false; revealed.clear(); @@ -248,7 +248,7 @@ public class GameState implements Serializable, Copyable { this.isPlaneChase = state.isPlaneChase; this.seenPlanes = state.seenPlanes; this.designations = state.designations; - this.inherentEmblems = state.inherentEmblems; + this.helperEmblems = state.helperEmblems; this.exile = state.exile; this.battlefield = state.battlefield; this.turnNum = state.turnNum; @@ -519,12 +519,12 @@ public class GameState implements Serializable, Copyable { return designations; } - public List getInherentEmblems() { - return inherentEmblems; + public List getHelperEmblems() { + return helperEmblems; } public Plane getCurrentPlane() { - if (command != null && command.size() > 0) { + if (command != null && !command.isEmpty()) { for (CommandObject cobject : command) { if (cobject instanceof Plane) { return (Plane) cobject; @@ -1184,16 +1184,13 @@ public class GameState implements Serializable, Copyable { } /** - * Inherent triggers (Rad counters) in the rules have no source. - * However to fit better with the engine, we make a fake emblem source, - * which is not displayed in any game zone. That allows the trigger to - * have a source, which helps with a bunch of situation like hosting, - * rather than having a trigger. + * Add fake/helper emblems for hidden source of inherent triggers like Rad counters, + * additional card hints like storm counter, etc. See GameImpl.initGameDefaultHelperEmblems *

- * Should not be used except in very specific situations + * It allows game and GUI find and show full card object in stack's triggers, logs and hints popup. */ - public void addInherentEmblem(Emblem emblem, UUID controllerId) { - getInherentEmblems().add(emblem); + public void addHelperEmblem(Emblem emblem, UUID controllerId) { + helperEmblems.add(emblem); emblem.setControllerId(controllerId); for (Ability ability : emblem.getInitAbilities()) { ability.setControllerId(controllerId); diff --git a/Mage/src/main/java/mage/game/command/Emblem.java b/Mage/src/main/java/mage/game/command/Emblem.java index 570d030d1be..1070de0d475 100644 --- a/Mage/src/main/java/mage/game/command/Emblem.java +++ b/Mage/src/main/java/mage/game/command/Emblem.java @@ -19,6 +19,7 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.game.Game; import mage.game.events.ZoneChangeEvent; +import mage.util.CardUtil; import mage.util.SubTypes; import java.util.Collections; @@ -37,7 +38,7 @@ public abstract class Emblem extends CommandObjectImpl { private boolean copy; private MageObject copyFrom; // copied card INFO (used to call original adjusters) protected FrameStyle frameStyle; - private Abilities abilites = new AbilitiesImpl<>(); + private Abilities abilities = new AbilitiesImpl<>(); public Emblem(String name) { super(name); @@ -50,7 +51,7 @@ public abstract class Emblem extends CommandObjectImpl { this.sourceObject = emblem.sourceObject; this.copy = emblem.copy; this.copyFrom = emblem.copyFrom; - this.abilites = emblem.abilites.copy(); + this.abilities = emblem.abilities.copy(); } @Override @@ -73,7 +74,7 @@ public abstract class Emblem extends CommandObjectImpl { this.setImageFileName(""); // use default this.setImageNumber(foundInfo.getImageNumber()); } else { - // how-to fix: add emblem to the tokens-database + // how-to fix: add emblem to tokens-database.txt throw new IllegalArgumentException("Wrong code usage: can't find token info for the emblem: " + this.getClass().getName()); } } @@ -98,7 +99,7 @@ public abstract class Emblem extends CommandObjectImpl { public void setControllerId(UUID controllerId) { this.controllerId = controllerId; - this.abilites.setControllerId(controllerId); + this.abilities.setControllerId(controllerId); } @Override @@ -152,7 +153,7 @@ public abstract class Emblem extends CommandObjectImpl { @Override public Abilities getAbilities() { - return abilites; + return abilities; } @Override @@ -260,7 +261,7 @@ public abstract class Emblem extends CommandObjectImpl { } public void discardEffects() { - for (Ability ability : abilites) { + for (Ability ability : abilities) { for (Effect effect : ability.getEffects()) { if (effect instanceof ContinuousEffect) { ((ContinuousEffect) effect).discard(); diff --git a/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java b/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java index 0dbd3388593..37f58a14114 100644 --- a/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java @@ -45,7 +45,7 @@ public class RadiationEmblem extends Emblem { this.setImageFileName(""); // use default this.setImageNumber(foundInfo.getImageNumber()); } else { - // how-to fix: add emblem to the tokens-database + // how-to fix: add emblem to the tokens-database TokenRepository->loadXmageTokens throw new IllegalArgumentException("Wrong code usage: can't find xmage token info for: " + TokenRepository.XMAGE_IMAGE_NAME_RADIATION); } } diff --git a/Mage/src/main/java/mage/game/command/emblems/XmageHelperEmblem.java b/Mage/src/main/java/mage/game/command/emblems/XmageHelperEmblem.java new file mode 100644 index 00000000000..1fa47c17fbc --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/XmageHelperEmblem.java @@ -0,0 +1,52 @@ +package mage.game.command.emblems; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.hint.Hint; +import mage.cards.FrameStyle; +import mage.cards.repository.TokenInfo; +import mage.cards.repository.TokenRepository; +import mage.constants.Zone; +import mage.game.command.Emblem; + +/** + * GUI: inner xmage emblem to show additional info for players like global hints + * + * @author JayDi85 + */ +public class XmageHelperEmblem extends Emblem { + + public XmageHelperEmblem() { + super("Helper Emblem"); + this.frameStyle = FrameStyle.M15_NORMAL; + + TokenInfo foundInfo = TokenRepository.instance.findPreferredTokenInfoForXmage(TokenRepository.XMAGE_IMAGE_NAME_HELPER_EMBLEM, null); + if (foundInfo != null) { + this.setExpansionSetCode(foundInfo.getSetCode()); + this.setUsesVariousArt(false); + this.setCardNumber(""); + this.setImageFileName(""); // use default + this.setImageNumber(foundInfo.getImageNumber()); + } else { + // how-to fix: add emblem to the tokens-database TokenRepository->loadXmageTokens + throw new IllegalArgumentException("Wrong code usage: can't find xmage token info for: " + TokenRepository.XMAGE_IMAGE_NAME_HELPER_EMBLEM); + } + } + + private XmageHelperEmblem(final XmageHelperEmblem card) { + super(card); + } + + @Override + public XmageHelperEmblem copy() { + return new XmageHelperEmblem(this); + } + + public XmageHelperEmblem withCardHint(String name, Hint hint) { + this.getAbilities().add(new SimpleStaticAbility( + Zone.ALL, + new InfoEffect(name)).addHint(hint) + ); + return this; + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 5b873d3170e..5d9a1e373b2 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1207,9 +1207,9 @@ public final class CardUtil { } } - public static List getCardRulesWithAdditionalInfo(UUID cardId, String cardName, + public static List getCardRulesWithAdditionalInfo(MageObject object, Abilities rulesSource, Abilities hintAbilities) { - return getCardRulesWithAdditionalInfo(null, cardId, cardName, rulesSource, hintAbilities); + return getCardRulesWithAdditionalInfo(null, object, rulesSource, hintAbilities); } /** @@ -1218,10 +1218,10 @@ public final class CardUtil { * @param rulesSource abilities list to show as rules * @param hintsSource abilities list to show as card hints only (you can add additional hints here; example: from second or transformed side) */ - public static List getCardRulesWithAdditionalInfo(Game game, UUID cardId, String cardName, + public static List getCardRulesWithAdditionalInfo(Game game, MageObject object, Abilities rulesSource, Abilities hintsSource) { try { - List rules = rulesSource.getRules(cardName); + List rules = rulesSource.getRules(); if (game == null || game.getPhase() == null) { // dynamic hints for started game only @@ -1229,7 +1229,10 @@ public final class CardUtil { } // additional effect's info from card.addInfo methods - rules.addAll(game.getState().getCardState(cardId).getInfo().values()); + CardState cardState = game.getState().getCardState(object.getId()); + if (cardState != null) { + rules.addAll(cardState.getInfo().values()); + } // ability hints List abilityHints = new ArrayList<>(); @@ -1254,7 +1257,7 @@ public final class CardUtil { return rules; } catch (Exception e) { - logger.error("Exception in rules generation for card: " + cardName, e); + logger.error("Exception in rules generation for object: " + object.getName(), e); } return RULES_ERROR_INFO; } diff --git a/Mage/src/main/java/mage/util/ManaUtil.java b/Mage/src/main/java/mage/util/ManaUtil.java index 9ffd783400e..08b478d6e23 100644 --- a/Mage/src/main/java/mage/util/ManaUtil.java +++ b/Mage/src/main/java/mage/util/ManaUtil.java @@ -658,7 +658,7 @@ public final class ManaUtil { return getColorIdentity( token.getColor(), String.join("", token.getManaCostSymbols()), - token.getAbilities().getRules(token.getName()), + token.getAbilities().getRules(), token.getBackFace() == null ? null : token.getBackFace().getCopySourceCard() ); } diff --git a/Mage/src/main/java/mage/util/MultiAmountMessage.java b/Mage/src/main/java/mage/util/MultiAmountMessage.java index 569a31e731e..3749c20c408 100644 --- a/Mage/src/main/java/mage/util/MultiAmountMessage.java +++ b/Mage/src/main/java/mage/util/MultiAmountMessage.java @@ -2,8 +2,11 @@ package mage.util; import java.io.Serializable; -// Author: alexander-novo -// A helper class for facilitating the multi-choose dialog +/** + * A helper class for facilitating the multi-choose dialog + * + * @author alexander-novo + */ public class MultiAmountMessage implements Serializable { public String message; public int min; @@ -20,4 +23,9 @@ public class MultiAmountMessage implements Serializable { this.max = max; this.defaultValue = defaultValue; } + + @Override + public String toString() { + return String.format("%s - from %d to %d - default %d", message, min, max, defaultValue); + } }