Class DiscoverSpell

java.lang.Object
net.demilich.metastone.game.spells.Spell
net.demilich.metastone.game.spells.DiscoverSpell
All Implemented Interfaces:
java.io.Serializable, HasDesc<SpellDesc>
Direct Known Subclasses:
DiscoverCardSpell, DiscoverDrawSpell, DiscoverFilteredCardSpell, DiscoverRandomCardSpell, ExoticGoodsSpell

public class DiscoverSpell
extends Spell
Prompts the user to make an irreversible choice of some number of cards, and casts a spell for the chosen (and optionally, unchosen) cards.

Discover actions prompt the user to select from count=SpellArg.HOW_MANY cards, with a default of 3.

If an SpellArg.ATTRIBUTE is specified, it is added to the cards in the Zones.DISCOVER, and removed when they leave. This is used with the Attribute.UNCENSORED attribute to indicate that these cards should be visible to the opponent.

The cards to prompt the user with are gathered from the SpellArg.CARDS attribute and the specified CardSource and CardFilter in SpellArg.CARD_SOURCE and SpellArg.CARD_FILTER. When the number of cards to discover from is greater than count, cards are chosen at random from the possible options without replacement (i.e., duplicates will not appear).

If SpellArg.CARD_FILTER is specified without a SpellArg.CARD_SOURCE, it is assumed that the weighted (i.e., class-specific) catalogue source CatalogueSource should be used. However, if the casting player is the HeroClass.ANY or HeroClass.TEST class, an UnweightedCatalogueSource will be used instead.

When SpellArg.CANNOT_RECEIVE_OWNED is specified, the possible options exclude cards that are already in the player's Zones.HAND or Zones.HERO_POWER.

Some card sources generate new cards; these card sources implement the HasCardCreationSideEffects interface. Other card sources, like DeckSource, will reference the actual cards in the player's deck. Discovers always present copies of cards to users, regardless of their origin. To perform the spell on the actual card, set SpellArg.EXCLUSIVE to true. Trying to use an "exclusive" discover on cards that are always generated will throw an exception.

When the user makes a discover choice, the spell arguments are interpreted to determine what spell is "cast" with the chosen and unchosen cards:

If the CardSource implements the HasWeights interface, the discover will use the weights for random selection. Since the CatalogueSource is the default card source and it supports weighting, a discover spell with no card source specified will make a weighted selection from collectible cards in this DeckFormat. The weighting defaults to 4x likelihood for class cards that match the player's hero class, 1x likelihood for neutral cards, and no likelihood for other class cards.

If there are no choices generated by the card source and filter, or if all the choices are removed due to SpellArg.CANNOT_RECEIVE_OWNED, the user is not prompted for any choices and no spells are cast.

To discover a minion and put it in the player's hand:

     {
         "class": "DiscoverSpell",
         "cardFilter": {
             "class": "CardFilter",
             "cardType": "MINION"
         },
         "spell": {
             "class": "ReceiveCardSpell"
         }
     }
 
To discover a random minion from any class without weighting:
     {
         "class": "DiscoverSpell",
         "cardFilter": {
             "class": "CardFilter",
             "cardType": "MINION"
         },
         "cardSource": {
             "class": "UnweightedCatalogueSource"
         },
         "spell": {
             "class": "ReceiveCardSpell"
         }
     }
 
To discover a random Totem, including token totems, use an UncollectibleCatalogueSource to include tokens in the selection:
     {
         "class": "DiscoverSpell",
         "cardFilter": {
             "class": "CardFilter",
             "cardType": "MINION",
             "race": "TOTEM"
         },
         "cardSource": {
             "class": "UncollectibleCatalogueSource"
         },
         "spell": {
             "class": "ReceiveCardSpell"
         }
     }
 
To choose amongst 3 of a specific list of cards, specify the SpellArg.CARDS argument. Only 3 of these 4 cards will be shown to the user, choosing which 3 randomly but without duplicates.
     {
         "class": "DiscoverSpell",
         "cards": [
              "minion_bloodfen_raptor",
              "minion_argent_squire",
              "minion_snowflipper_penguin",
              "minion_ironbeak_owl"
         ],
         "spell": {
             "class": "ReceiveCardSpell"
         }
     }
 
When SpellArg.HOW_MANY is specified and it is greater or equal to the number of SpellArg.CARDS elements there are, the user will be prompted for a discover choice in the exact order the cards appear:
      {
          "class": "DiscoverSpell",
          "spell": {
              "class": "ReceiveCardSpell"
          },
          "cards": [
              "spell_invocation_of_air",
              "spell_invocation_of_earth",
              "spell_invocation_of_fire",
              "spell_invocation_of_water"
          ],
          "howMany": 4
      }
 
To cast a discovered spell instead of receiving it, change the SpellArg.SPELL to a CastCardsSpell. When trying to do different effects with the chosen cards, it's important to check if the Spell supports the SpellArg.CARD argument (common ones like CastCardsSpell, SummonSpell and ReceiveCardSpell do:
      {
          "class": "DiscoverSpell",
          "spell": {
              "class": "CastCardsSpell"
          },
          "cards": [
              "spell_invocation_of_air",
              "spell_invocation_of_earth",
              "spell_invocation_of_fire",
              "spell_invocation_of_water"
          ],
          "howMany": 4
      }
 
You can also cast spells on cards that are not chosen. This effect implements the text: "Discover a Fireball and Pyroblast. Draw it. Put the card you did not choose into the opponent's hand."
     {
         "class": "DiscoverSpell",
         "spell1": {
             "class": "ReceiveCardSpell"
         },
         "spell2": {
             "class": "ReceiveCardSpell"
         },
         "cards": [
              "spell_pyroblast",
              "spell_fireball"
         ],
         "howMany": 2
     }
 
If you want to discover a copy of a card in the opponent's deck, use a DeckSource.
     {
         "class": "DiscoverSpell",
         "spell": {
             "class": "ReceiveCardSpell"
         },
         "cardSource": {
             "class": "DeckSource",
             "targetPlayer": "OPPONENT"
         }
     }
 
If instead you want to steal a card from the opponent's deck, use a StealCardSpell and set SpellArg.EXCLUSIVE to true. The SpellArg.EXCLUSIVE argument will cast StealCardSpell with the chosen card's Entity.getId() as the target. You must use SpellArg.EXCLUSIVE whenever you use spells that only accept target and not SpellArg.CARD.
     {
         "class": "DiscoverSpell",
         "spell": {
             "class": "StealCardSpell"
         },
         "cardSource": {
             "class": "DeckSource",
             "targetPlayer": "OPPONENT"
         },
         "exclusive": true
     }
 
Discovering spells from weighted sources like CatalogueSource but with different classes is a little tricky. Currently, only the CardSourceArg.TARGET_PLAYER argument can be used to discover against the opponent's class. For example, this spell will discover a rare card in the opponent's class:
     {
          "class": "DiscoverSpell",
          "spell": {
              "class": "ReceiveCardSpell",
              "targetPlayer": "SELF"
          },
          "cardFilter": {
              "class": "AndFilter",
              "filters": [
                  {
                      "class": "CardFilter",
                      "heroClass": "OPPONENT"
                  },
                  {
                      "class": "CardFilter",
                      "rarity": "RARE"
                  }
              ]
          },
          "cardSource": {
              "class": "CatalogueSource",
              "targetPlayer": "OPPONENT"
          }
      }
 
See Also:
for more about how card filtering works., Serialized Form
  • Constructor Details

  • Method Details

    • create

      public static SpellDesc create()
    • create

      public static SpellDesc create​(SpellDesc spellToCastOnSelectedCards)
    • onCast

      @Suspendable protected void onCast​(GameContext context, Player player, SpellDesc desc, Entity source, Entity target)
      Description copied from class: Spell
      Implementations of onCast are the meat-and-bones of a spell's effects. This should actually call a variety of methods in GameLogic, generate cards using SpellUtils.getCards(GameContext, Player, Entity, Entity, SpellDesc), interpret SpellArg keys in the desc, etc.

      Observe that subclasses of Spell mostly just need to implement this function. Also, observe that instances of Spell are stateless: all the state is provided as arguments to this function.

      Specified by:
      onCast in class Spell
      Parameters:
      context - The game context
      player - The casting player
      desc - The collection of SpellArg keys and values that are interpreted by the implementation of this function to actually cause effects in a game
      source - The entity from which this effect is happening (typically a card or a minion if it's a battlecry).
      target - The particular target of this invocation of the spell. When a spell hits multiple targets, like an AoE damage effect, this method is called once for each target in the list of targets.
      See Also:
      for an example of a complex spell implementation.