new feature: Emblem Cards (#10498)

* new feature: Emblem Cards

Allows match/tournament creator to specify cards to give each player
emblem versions of (or just the starting player for symmetric effects).

Technical details:
- new UI for specifying emblem cards (.dck files)
  - available for all match/tournament types
- new class `EmblemOfCard`
- new method `copyWithZone` on `AbilityImpl` (used to make abilities
  work from command zone)
- new fields on `GameOptions` and `MatchOptions` for emblem cards
- emblems are granted after mulligans, before first turn (technically
  after Planechase starting plane creation)

* fixes

* defaults for emblem cards in match options (fixes quick game buttons)

* minor fixes

* use DeckCardInfo instead of Card for emblem cards options

* restore accessible parent properties

* fix images for card emblems

* look up cards in a way that preserves which art

* fix typos; make Emblem.sourceObject protected

* add descriptions to planechase and emblem cards

* fixes

* add some unit tests for known working cards

* fix author name

* add explanation comment

* fix up tests

* copyWithZone: no longer modifies zone for singleton abilities

* directly check for MageSingleton
This commit is contained in:
Artemis Kearney 2023-09-26 21:47:13 -05:00 committed by GitHub
parent 04dba063aa
commit 41874b0b4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 774 additions and 39 deletions

View file

@ -33,7 +33,7 @@ public abstract class Emblem extends CommandObjectImpl {
private static final ManaCosts emptyCost = new ManaCostsImpl<>();
private UUID controllerId;
private MageObject sourceObject;
protected MageObject sourceObject;
private boolean copy;
private MageObject copyFrom; // copied card INFO (used to call original adjusters)
private FrameStyle frameStyle;

View file

@ -0,0 +1,108 @@
package mage.game.command.emblems;
import mage.MageObject;
import mage.abilities.AbilityImpl;
import mage.cards.Card;
import mage.cards.decks.DeckCardInfo;
import mage.cards.mock.MockCard;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.Zone;
import mage.game.command.Emblem;
import mage.util.CardUtil;
import org.apache.log4j.Logger;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author artemiswkearney
* Emblem with all the abilities of an existing card.
* Can be used for custom gamemodes like Omniscience Draft (as seen on Arena),
* mana burn with Yurlok of Scorch Thrash, and anything else players might think of.
*/
public final class EmblemOfCard extends Emblem {
private final boolean usesVariousArt;
private static final Logger logger = Logger.getLogger(EmblemOfCard.class);
public static Card lookupCard(
String cardName,
String cardNumber,
String setCode,
String infoTypeForError
) {
int cardNumberInt = CardUtil.parseCardNumberAsInt(cardNumber);
List<CardInfo> found = CardRepository.instance.findCards(new CardCriteria()
.name(cardName)
.minCardNumber(cardNumberInt)
.maxCardNumber(cardNumberInt)
.setCodes(setCode));
return found.stream()
.filter(ci -> ci.getCardNumber().equals(cardNumber))
.findFirst()
.orElseGet(() -> found.stream()
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No real card for " + infoTypeForError + " " + cardName)))
.getCard();
}
public static Card cardFromDeckInfo(DeckCardInfo info) {
return lookupCard(
info.getCardName(),
info.getCardNum(),
info.getSetCode(),
"DeckCardInfo"
);
}
public EmblemOfCard(Card card, Zone zone) {
super(card.getName());
if (card instanceof MockCard) {
card = lookupCard(
card.getName(),
card.getCardNumber(),
card.getExpansionSetCode(),
"MockCard"
);
}
this.getAbilities().addAll(card.getAbilities().stream().filter(
ability -> zone.match(ability.getZone())
).map(ability -> {
if (ability instanceof AbilityImpl && ability.getZone() == zone) {
return ((AbilityImpl)ability).copyWithZone(Zone.COMMAND);
}
return ability;
}).collect(Collectors.toList()));
this.getAbilities().setSourceId(this.getId());
this.setExpansionSetCode(card.getExpansionSetCode());
this.setCardNumber(card.getCardNumber());
this.setImageNumber(card.getImageNumber());
this.usesVariousArt = card.getUsesVariousArt();
}
public EmblemOfCard(Card card) {
this(card, Zone.BATTLEFIELD);
}
private EmblemOfCard(EmblemOfCard eoc) {
super(eoc);
this.usesVariousArt = eoc.usesVariousArt;
}
@Override
public EmblemOfCard copy() {
return new EmblemOfCard(this);
}
@Override
public void setSourceObject(MageObject sourceObject) {
this.sourceObject = sourceObject;
// super method would try and fail to find the emblem image here
// (not sure why that would be setSoureObject's job; we get our image during construction)
}
public boolean getUsesVariousArt() {
return usesVariousArt;
}
}