[TDM] Implement omen mechanic (#13501)

* Abstract AdventureCard to SingleFaceSplitCard

* Fix AdventureCardSpellImpl

* Finish converting adventure card and adventure spell

* Update Brightcap Badger

change finalize call to adventure card

* Update Darksteel Monolith

being cast from hand condition referencing AdventureCardSpell

* Update Tlincalli Hunter

exiled creature condition referencing AdventureCardSpell

* Update Twice Upon a Time

finalizeAdventure called from Adventure card

* Finish abstracting Adventure

missed some more references to adventure cards

* Implement Omen cards

* Implement Dirgur Island Dragon

* Missed some adventureSpellName references

* OmenCardSpell had wrong comma symbol

* Add tests for Omen Cards

* Rename two part card components

change from SingleFaceSplitCard to CardWithSpellOption

* Update comments and variable name
This commit is contained in:
Jmlundeen 2025-04-08 07:54:18 -05:00 committed by GitHub
parent e3937e31c1
commit 0df5f17603
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 637 additions and 264 deletions

View file

@ -123,6 +123,7 @@ public class ModernCardRenderer extends CardRenderer {
public static final Color ERROR_COLOR = new Color(255, 0, 255);
static String SUB_TYPE_ADVENTURE = "Adventure";
static String SUB_TYPE_OMEN = "Omen";
///////////////////////////////////////////////////////////////////////////
// Layout metrics for modern border cards
@ -168,8 +169,8 @@ public class ModernCardRenderer extends CardRenderer {
// Processed mana cost string
protected String manaCostString;
// Is an adventure
protected boolean isAdventure = false;
// Is an adventure or omen
protected boolean isCardWithSpellOption = false;
public ModernCardRenderer(CardView card) {
// Pass off to parent
@ -179,12 +180,13 @@ public class ModernCardRenderer extends CardRenderer {
manaCostString = ManaSymbols.getClearManaCost(cardView.getManaCostStr());
if (cardView.isSplitCard()) {
isAdventure = cardView.getRightSplitTypeLine().contains(SUB_TYPE_ADVENTURE);
isCardWithSpellOption = cardView.getRightSplitTypeLine().contains(SUB_TYPE_ADVENTURE)
|| cardView.getRightSplitTypeLine().contains(SUB_TYPE_OMEN);
}
}
protected boolean isAdventure() {
return isAdventure;
protected boolean isCardWithSpellOption() {
return isCardWithSpellOption;
}
@Override
@ -660,7 +662,7 @@ public class ModernCardRenderer extends CardRenderer {
drawRulesText(g, textboxKeywords, textboxRules,
contentWidth / 2 + totalContentInset + 4, totalContentInset + boxHeight + 2,
contentWidth / 2 - 8, typeLineY - totalContentInset - boxHeight - 6, false);
} else if (isAdventure) {
} else if (isCardWithSpellOption) {
drawRulesText(g, textboxKeywords, textboxRules,
contentWidth / 2 + totalContentInset + 4, typeLineY + boxHeight + 2,
contentWidth / 2 - 8, cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3, false);

View file

@ -56,7 +56,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
private boolean isAftermath = false;
private static String trimAdventure(String rule) {
if (rule.startsWith("Adventure")) {
if (rule.startsWith("Adventure") || rule.startsWith("Omen")) {
return rule.substring(rule.lastIndexOf("—") + 8);
}
return rule;
@ -71,7 +71,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
rightHalf.color = new ObjectColor(cardView.getRightSplitCostsStr());
leftHalf.color = new ObjectColor(cardView.getLeftSplitCostsStr());
if (isAdventure()) {
if (isCardWithSpellOption()) {
List<String> trimmedRules = new ArrayList<>();
for (String rule : view.getRightSplitRules()) {
trimmedRules.add(trimAdventure(rule));
@ -95,7 +95,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
// they "rotate" in opposite directions making consquence and normal split cards
// have the "right" vs "left" as the top half.
// Adventures are treated differently and not rotated at all.
if (isAdventure()) {
if (isCardWithSpellOption()) {
manaCostString = leftHalf.manaCostString;
textboxKeywords = leftHalf.keywords;
textboxRules = leftHalf.rules;
@ -159,7 +159,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
protected void drawBackground(Graphics2D g) {
if (cardView.isFaceDown()) {
drawCardBackTexture(g);
} if (isAdventure()) {
} if (isCardWithSpellOption()) {
super.drawBackground(g);
} else {
{ // Left half background (top of the card)
@ -204,7 +204,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
@Override
protected void drawArt(Graphics2D g) {
if (isAdventure) {
if (isCardWithSpellOption) {
super.drawArt(g);
} else if (artImage != null) {
if (isAftermath()) {
@ -318,7 +318,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
@Override
protected void drawFrame(Graphics2D g, CardPanelAttributes attribs, BufferedImage image, boolean lessOpaqueRulesTextBox) {
if (isAdventure()) {
if (isCardWithSpellOption()) {
super.drawFrame(g, attribs, image, lessOpaqueRulesTextBox);
CardPanelAttributes adventureAttribs = new CardPanelAttributes(

View file

@ -432,21 +432,21 @@ public class CardView extends SimpleCardView {
fullCardName = mainCard.getLeftHalfCard().getName() + MockCard.MODAL_DOUBLE_FACES_NAME_SEPARATOR + mainCard.getRightHalfCard().getName();
this.manaCostLeftStr = mainCard.getLeftHalfCard().getManaCostSymbols();
this.manaCostRightStr = mainCard.getRightHalfCard().getManaCostSymbols();
} else if (card instanceof AdventureCard) {
} else if (card instanceof CardWithSpellOption) {
this.isSplitCard = true;
AdventureCard adventureCard = ((AdventureCard) card);
leftSplitName = adventureCard.getName();
leftSplitCostsStr = String.join("", adventureCard.getManaCostSymbols());
leftSplitRules = adventureCard.getSharedRules(game);
leftSplitTypeLine = getCardTypeLine(game, adventureCard);
AdventureCardSpell adventureCardSpell = adventureCard.getSpellCard();
rightSplitName = adventureCardSpell.getName();
rightSplitCostsStr = String.join("", adventureCardSpell.getManaCostSymbols());
rightSplitRules = adventureCardSpell.getRules(game);
rightSplitTypeLine = getCardTypeLine(game, adventureCardSpell);
fullCardName = adventureCard.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + adventureCardSpell.getName();
this.manaCostLeftStr = adventureCard.getManaCostSymbols();
this.manaCostRightStr = adventureCardSpell.getManaCostSymbols();
CardWithSpellOption mainCard = ((CardWithSpellOption) card);
leftSplitName = mainCard.getName();
leftSplitCostsStr = String.join("", mainCard.getManaCostSymbols());
leftSplitRules = mainCard.getSharedRules(game);
leftSplitTypeLine = getCardTypeLine(game, mainCard);
SpellOptionCard splitCardSpell = mainCard.getSpellCard();
rightSplitName = splitCardSpell.getName();
rightSplitCostsStr = String.join("", splitCardSpell.getManaCostSymbols());
rightSplitRules = splitCardSpell.getRules(game);
rightSplitTypeLine = getCardTypeLine(game, splitCardSpell);
fullCardName = mainCard.getName() + MockCard.CARD_WITH_SPELL_OPTION_NAME_SEPARATOR + splitCardSpell.getName();
this.manaCostLeftStr = mainCard.getManaCostSymbols();
this.manaCostRightStr = splitCardSpell.getManaCostSymbols();
} else if (card instanceof MockCard) {
// deck editor cards
fullCardName = ((MockCard) card).getFullName(true);

View file

@ -52,7 +52,7 @@ public final class BrightcapBadger extends AdventureCard {
// Fungus Frolic
// Create two 1/1 green Saproling creature tokens.
this.getSpellCard().getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), 2));
this.getSpellCard().finalizeAdventure();
this.finalizeAdventure();
}
private BrightcapBadger(final BrightcapBadger card) {

View file

@ -54,7 +54,7 @@ enum IsBeingCastFromHandCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
MageObject object = game.getObject(source);
if (object instanceof SplitCardHalf || object instanceof AdventureCardSpell || object instanceof ModalDoubleFacedCardHalf) {
if (object instanceof SplitCardHalf || object instanceof SpellOptionCard || object instanceof ModalDoubleFacedCardHalf) {
UUID mainCardId = ((Card) object).getMainCard().getId();
object = game.getObject(mainCardId);
}

View file

@ -0,0 +1,52 @@
package mage.cards.d;
import mage.MageInt;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.TapTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.WardAbility;
import mage.cards.CardSetInfo;
import mage.cards.OmenCard;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author Jmlundeen
*/
public final class DirgurIslandDragon extends OmenCard {
public DirgurIslandDragon(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, new CardType[]{CardType.INSTANT}, "{5}{U}", "Skimming Strike", "{1}{U}");
this.subtype.add(SubType.DRAGON);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Ward {2}
this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}")));
// Skimming Strike
// Tap up to one target creature. Draw a card.
this.getSpellCard().getSpellAbility().addEffect(new TapTargetEffect());
this.getSpellCard().getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1));
this.getSpellCard().getSpellAbility().addTarget(new TargetCreaturePermanent(0, 1));
this.finalizeOmen();
}
private DirgurIslandDragon(final DirgurIslandDragon card) {
super(card);
}
@Override
public DirgurIslandDragon copy() {
return new DirgurIslandDragon(this);
}
}

View file

@ -65,7 +65,7 @@ enum ExiledCreatureSpellCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
MageObject object = game.getObject(source);
if (object instanceof SplitCardHalf || object instanceof AdventureCardSpell || object instanceof ModalDoubleFacedCardHalf) {
if (object instanceof SplitCardHalf || object instanceof SpellOptionCard || object instanceof ModalDoubleFacedCardHalf) {
UUID mainCardId = ((Card) object).getMainCard().getId();
object = game.getObject(mainCardId);
}

View file

@ -47,7 +47,7 @@ public final class TwiceUponATime extends AdventureCard {
// Unlikely Meeting
// Search your library for a Doctor card, reveal it, put it into your hand, then shuffle.
this.getSpellCard().getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter2), true));
this.getSpellCard().finalizeAdventure();
this.finalizeAdventure();
}
private TwiceUponATime(final TwiceUponATime card) {

View file

@ -67,6 +67,7 @@ public final class TarkirDragonstorm extends ExpansionSet {
cards.add(new SetCardInfo("Delta Bloodflies", 77, Rarity.COMMON, mage.cards.d.DeltaBloodflies.class));
cards.add(new SetCardInfo("Descendant of Storms", 8, Rarity.UNCOMMON, mage.cards.d.DescendantOfStorms.class));
cards.add(new SetCardInfo("Devoted Duelist", 104, Rarity.COMMON, mage.cards.d.DevotedDuelist.class));
cards.add(new SetCardInfo("Dirgur Island Dragon", 40, Rarity.COMMON, mage.cards.d.DirgurIslandDragon.class));
cards.add(new SetCardInfo("Dismal Backwater", 254, Rarity.COMMON, mage.cards.d.DismalBackwater.class));
cards.add(new SetCardInfo("Dispelling Exhale", 41, Rarity.COMMON, mage.cards.d.DispellingExhale.class));
cards.add(new SetCardInfo("Dracogenesis", 105, Rarity.MYTHIC, mage.cards.d.Dracogenesis.class));

View file

@ -0,0 +1,77 @@
package org.mage.test.cards.cost.omen;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
public class OmenCardsTest extends CardTestPlayerBase {
@Test
public void testDirgurIslandDragonShuffle() {
setStrictChooseMode(true);
skipInitShuffling();
removeAllCardsFromLibrary(playerA);
addCard(Zone.HAND, playerA, "Dirgur Island Dragon");
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerB, "Bear Cub");
addCard(Zone.LIBRARY, playerA, "Mountain");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Skimming Strike", "Bear Cub");
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertLibraryCount(playerA, "Dirgur Island Dragon", 1);
assertTapped("Bear Cub", true);
assertHandCount(playerA, 1);
}
@Test
public void testDirgurIslandDragonShuffleAndPlay() {
setStrictChooseMode(true);
skipInitShuffling();
removeAllCardsFromLibrary(playerA);
addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
addCard(Zone.BATTLEFIELD, playerB, "Bear Cub");
addCard(Zone.LIBRARY, playerA, "Mountain");
addCard(Zone.HAND, playerA, "Dirgur Island Dragon");
castSpell(2, PhaseStep.BEGIN_COMBAT, playerA, "Skimming Strike", "Bear Cub");
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Dirgur Island Dragon");
setStopAt(3, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Dirgur Island Dragon", 1);
}
@Test
public void testCounteredInGraveyard() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerB,"Island", 4);
addCard(Zone.BATTLEFIELD, playerB,"Bear Cub");
addCard(Zone.HAND, playerA, "Dirgur Island Dragon");
addCard(Zone.HAND, playerB, "Counterspell");
castSpell(2, PhaseStep.BEGIN_COMBAT, playerA, "Skimming Strike", "Bear Cub");
castSpell(2, PhaseStep.BEGIN_COMBAT, playerB, "Counterspell", "Skimming Strike", "Skimming Strike", StackClause.WHILE_ON_STACK);
attack(2, playerB, "Bear Cub");
setStopAt(2, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Dirgur Island Dragon", 1);
assertLife(playerA, 20 - 2);
}
@Test
public void testGraveyardCast() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerA, "Kess, Dissident Mage");
addCard(Zone.GRAVEYARD, playerA, "Dirgur Island Dragon");
addCard(Zone.BATTLEFIELD, playerB, "Bear Cub");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Skimming Strike", "Bear Cub");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLibraryCount(playerA, "Dirgur Island Dragon", 1);
}
}

View file

@ -6,8 +6,8 @@ import mage.MageObject;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.VariableManaCost;
import mage.abilities.keyword.FlashAbility;
import mage.cards.AdventureCardSpell;
import mage.cards.Card;
import mage.cards.SpellOptionCard;
import mage.cards.SplitCard;
import mage.constants.*;
import mage.game.Game;
@ -99,7 +99,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
// forced to cast (can be part id or main id)
Set<UUID> idsToCheck = new HashSet<>();
idsToCheck.add(object.getId());
if (object instanceof Card && !(object instanceof AdventureCardSpell)) {
if (object instanceof Card && !(object instanceof SpellOptionCard)) {
idsToCheck.add(((Card) object).getMainCard().getId());
}
for (UUID idToCheck : idsToCheck) {

View file

@ -4,7 +4,7 @@ package mage.abilities.condition.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.cards.AdventureCardSpell;
import mage.cards.SpellOptionCard;
import mage.cards.Card;
import mage.cards.ModalDoubleFacedCardHalf;
import mage.cards.SplitCardHalf;
@ -20,7 +20,7 @@ public enum IsBeingCastFromHandCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
MageObject object = game.getObject(source);
if (object instanceof SplitCardHalf || object instanceof AdventureCardSpell || object instanceof ModalDoubleFacedCardHalf) {
if (object instanceof SplitCardHalf || object instanceof SpellOptionCard || object instanceof ModalDoubleFacedCardHalf) {
UUID mainCardId = ((Card) object).getMainCard().getId();
object = game.getObject(mainCardId);
}

View file

@ -549,9 +549,9 @@ public class ContinuousEffects implements Serializable {
// rules:
// 708.4. In every zone except the stack, the characteristics of a split card are those of its two halves combined.
idToCheck = ((SplitCardHalf) objectToCheck).getMainCard().getId();
} else if (!type.needPlayCardAbility() && objectToCheck instanceof AdventureCardSpell) {
// adventure spell uses alternative characteristics for spell/stack, all other cases must use main card
idToCheck = ((AdventureCardSpell) objectToCheck).getMainCard().getId();
} else if (!type.needPlayCardAbility() && objectToCheck instanceof CardWithSpellOption) {
// adventure/omen spell uses alternative characteristics for spell/stack, all other cases must use main card
idToCheck = ((CardWithSpellOption) objectToCheck).getMainCard().getId();
} else if (!type.needPlayCardAbility() && objectToCheck instanceof ModalDoubleFacedCardHalf) {
// each mdf side uses own characteristics to check for playing, all other cases must use main card
// rules:

View file

@ -5,7 +5,7 @@ import mage.abilities.MageSingleton;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.AdventureCardSpell;
import mage.cards.AdventureSpellCard;
import mage.cards.Card;
import mage.constants.AsThoughEffectType;
import mage.constants.Duration;
@ -51,10 +51,10 @@ public class ExileAdventureSpellEffect extends OneShotEffect implements MageSing
Spell spell = game.getStack().getSpell(source.getId());
if (spell != null) {
Card spellCard = spell.getCard();
if (spellCard instanceof AdventureCardSpell) {
if (spellCard instanceof AdventureSpellCard) {
UUID exileId = adventureExileId(controller.getId(), game);
game.getExile().createZone(exileId, "On an Adventure from " + controller.getName());
AdventureCardSpell adventureSpellCard = (AdventureCardSpell) spellCard;
AdventureSpellCard adventureSpellCard = (AdventureSpellCard) spellCard;
Card parentCard = adventureSpellCard.getParentCard();
if (controller.moveCardsToExile(parentCard, source, game, true, exileId, "On an Adventure from " + controller.getName())) {
ContinuousEffect effect = new AdventureCastFromExileEffect();

View file

@ -257,7 +257,7 @@ public class ForetellAbility extends SpecialAction {
game.getState().addOtherAbility(rightHalfCard, ability);
}
}
} else if (card instanceof AdventureCard) {
} else if (card instanceof CardWithSpellOption) {
if (foretellCost != null) {
Card creatureCard = card.getMainCard();
ForetellCostAbility ability = new ForetellCostAbility(foretellCost);
@ -268,7 +268,7 @@ public class ForetellAbility extends SpecialAction {
game.getState().addOtherAbility(creatureCard, ability);
}
if (foretellSplitCost != null) {
Card spellCard = ((AdventureCard) card).getSpellCard();
Card spellCard = ((CardWithSpellOption) card).getSpellCard();
ForetellCostAbility ability = new ForetellCostAbility(foretellSplitCost);
ability.setSourceId(spellCard.getId());
ability.setControllerId(source.getControllerId());
@ -360,11 +360,11 @@ public class ForetellAbility extends SpecialAction {
} else if (((ModalDoubleFacedCard) card).getRightHalfCard().getName().equals(abilityName)) {
return ((ModalDoubleFacedCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
}
} else if (card instanceof AdventureCard) {
} else if (card instanceof CardWithSpellOption) {
if (card.getMainCard().getName().equals(abilityName)) {
return card.getMainCard().getSpellAbility().canActivate(playerId, game);
} else if (((AdventureCard) card).getSpellCard().getName().equals(abilityName)) {
return ((AdventureCard) card).getSpellCard().getSpellAbility().canActivate(playerId, game);
} else if (((CardWithSpellOption) card).getSpellCard().getName().equals(abilityName)) {
return ((CardWithSpellOption) card).getSpellCard().getSpellAbility().canActivate(playerId, game);
}
}
return card.getSpellAbility().canActivate(playerId, game);
@ -391,11 +391,11 @@ public class ForetellAbility extends SpecialAction {
} else if (((ModalDoubleFacedCard) card).getRightHalfCard().getName().equals(abilityName)) {
spellAbilityCopy = ((ModalDoubleFacedCard) card).getRightHalfCard().getSpellAbility().copy();
}
} else if (card instanceof AdventureCard) {
} else if (card instanceof CardWithSpellOption) {
if (card.getMainCard().getName().equals(abilityName)) {
spellAbilityCopy = card.getMainCard().getSpellAbility().copy();
} else if (((AdventureCard) card).getSpellCard().getName().equals(abilityName)) {
spellAbilityCopy = ((AdventureCard) card).getSpellCard().getSpellAbility().copy();
} else if (((CardWithSpellOption) card).getSpellCard().getName().equals(abilityName)) {
spellAbilityCopy = ((CardWithSpellOption) card).getSpellCard().getSpellAbility().copy();
}
} else {
spellAbilityCopy = card.getSpellAbility().copy();

View file

@ -268,11 +268,11 @@ class PlotSpellAbility extends SpellAbility {
} else if (((CardWithHalves) mainCard).getRightHalfCard().getName().equals(faceCardName)) {
return ((CardWithHalves) mainCard).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
}
} else if (card instanceof AdventureCard) {
} else if (card instanceof CardWithSpellOption) {
if (card.getMainCard().getName().equals(faceCardName)) {
return card.getMainCard().getSpellAbility().canActivate(playerId, game);
} else if (((AdventureCard) card).getSpellCard().getName().equals(faceCardName)) {
return ((AdventureCard) card).getSpellCard().getSpellAbility().canActivate(playerId, game);
} else if (((CardWithSpellOption) card).getSpellCard().getName().equals(faceCardName)) {
return ((CardWithSpellOption) card).getSpellCard().getSpellAbility().canActivate(playerId, game);
}
}
return card.getSpellAbility().canActivate(playerId, game);
@ -294,11 +294,11 @@ class PlotSpellAbility extends SpellAbility {
} else if (((CardWithHalves) card).getRightHalfCard().getName().equals(faceCardName)) {
spellAbilityCopy = ((CardWithHalves) card).getRightHalfCard().getSpellAbility().copy();
}
} else if (card instanceof AdventureCard) {
} else if (card instanceof CardWithSpellOption) {
if (card.getMainCard().getName().equals(faceCardName)) {
spellAbilityCopy = card.getMainCard().getSpellAbility().copy();
} else if (((AdventureCard) card).getSpellCard().getName().equals(faceCardName)) {
spellAbilityCopy = ((AdventureCard) card).getSpellCard().getSpellAbility().copy();
} else if (((CardWithSpellOption) card).getSpellCard().getName().equals(faceCardName)) {
spellAbilityCopy = ((CardWithSpellOption) card).getSpellCard().getSpellAbility().copy();
}
} else {
spellAbilityCopy = card.getSpellAbility().copy();

View file

@ -1,98 +1,29 @@
package mage.cards;
import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.constants.CardType;
import mage.constants.SpellAbilityType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
import mage.util.CardUtil;
import java.util.List;
import java.util.UUID;
/**
* @author phulin
*/
public abstract class AdventureCard extends CardImpl {
/* The adventure spell card, i.e. Swift End. */
protected AdventureCardSpell spellCard;
public abstract class AdventureCard extends CardWithSpellOption {
public AdventureCard(UUID ownerId, CardSetInfo setInfo, CardType[] types, CardType[] typesSpell, String costs, String adventureName, String costsSpell) {
super(ownerId, setInfo, types, costs);
this.spellCard = new AdventureCardSpellImpl(ownerId, setInfo, adventureName, typesSpell, costsSpell, this);
this.spellCard = new AdventureSpellCard(ownerId, setInfo, adventureName, typesSpell, costsSpell, this);
}
public AdventureCard(AdventureCard card) {
super(card);
}
public void finalizeAdventure() {
spellCard.finalizeAdventure();
}
protected AdventureCard(final AdventureCard card) {
super(card);
this.spellCard = card.getSpellCard().copy();
this.spellCard.setParentCard(this);
}
public AdventureCardSpell getSpellCard() {
return spellCard;
}
public void setParts(AdventureCardSpell cardSpell) {
// for card copy only - set new parts
this.spellCard = cardSpell;
cardSpell.setParentCard(this);
}
@Override
public void assignNewId() {
super.assignNewId();
spellCard.assignNewId();
}
@Override
public boolean moveToZone(Zone toZone, Ability source, Game game, boolean flag, List<UUID> appliedEffects) {
if (super.moveToZone(toZone, source, game, flag, appliedEffects)) {
Zone currentZone = game.getState().getZone(getId());
game.getState().setZone(getSpellCard().getId(), currentZone);
return true;
}
return false;
}
@Override
public void setZone(Zone zone, Game game) {
super.setZone(zone, game);
game.setZone(getSpellCard().getId(), zone);
}
@Override
public boolean moveToExile(UUID exileId, String name, Ability source, Game game, List<UUID> appliedEffects) {
if (super.moveToExile(exileId, name, source, game, appliedEffects)) {
Zone currentZone = game.getState().getZone(getId());
game.getState().setZone(getSpellCard().getId(), currentZone);
return true;
}
return false;
}
@Override
public boolean removeFromZone(Game game, Zone fromZone, Ability source) {
// zone contains only one main card
return super.removeFromZone(game, fromZone, source);
}
@Override
public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) {
if (isCopy()) { // same as meld cards
super.updateZoneChangeCounter(game, event);
return;
}
super.updateZoneChangeCounter(game, event);
getSpellCard().updateZoneChangeCounter(game, event);
spellCard.finalizeSpell();
}
@Override
@ -103,45 +34,4 @@ public abstract class AdventureCard extends CardImpl {
this.getSpellCard().getSpellAbility().setControllerId(controllerId);
return super.cast(game, fromZone, ability, controllerId);
}
@Override
public Abilities<Ability> getAbilities() {
Abilities<Ability> allAbilities = new AbilitiesImpl<>();
allAbilities.addAll(spellCard.getAbilities());
allAbilities.addAll(super.getAbilities());
return allAbilities;
}
@Override
public Abilities<Ability> getInitAbilities() {
// must init only parent related abilities, spell card must be init separately
return super.getAbilities();
}
@Override
public Abilities<Ability> getAbilities(Game game) {
Abilities<Ability> allAbilities = new AbilitiesImpl<>();
allAbilities.addAll(spellCard.getAbilities(game));
allAbilities.addAll(super.getAbilities(game));
return allAbilities;
}
public Abilities<Ability> getSharedAbilities(Game game) {
// abilities without spellcard
return super.getAbilities(game);
}
public List<String> getSharedRules(Game game) {
// rules without spellcard
Abilities<Ability> sourceAbilities = this.getSharedAbilities(game);
return CardUtil.getCardRulesWithAdditionalInfo(game, this, sourceAbilities, sourceAbilities);
}
@Override
public void setOwnerId(UUID ownerId) {
super.setOwnerId(ownerId);
abilities.setControllerId(ownerId);
spellCard.getAbilities().setControllerId(ownerId);
spellCard.setOwnerId(ownerId);
}
}

View file

@ -1,12 +0,0 @@
package mage.cards;
/**
* @author phulin
*/
public interface AdventureCardSpell extends SubCard<AdventureCard> {
@Override
AdventureCardSpell copy();
void finalizeAdventure();
}

View file

@ -19,11 +19,11 @@ import java.util.stream.Collectors;
/**
* @author phulin
*/
public class AdventureCardSpellImpl extends CardImpl implements AdventureCardSpell {
public class AdventureSpellCard extends CardImpl implements SpellOptionCard {
private AdventureCard adventureCardParent;
public AdventureCardSpellImpl(UUID ownerId, CardSetInfo setInfo, String adventureName, CardType[] cardTypes, String costs, AdventureCard adventureCardParent) {
public AdventureSpellCard(UUID ownerId, CardSetInfo setInfo, String adventureName, CardType[] cardTypes, String costs, AdventureCard adventureCardParent) {
super(ownerId, setInfo, cardTypes, costs, SpellAbilityType.ADVENTURE_SPELL);
this.subtype.add(SubType.ADVENTURE);
@ -35,13 +35,13 @@ public class AdventureCardSpellImpl extends CardImpl implements AdventureCardSpe
this.adventureCardParent = adventureCardParent;
}
public void finalizeAdventure() {
public void finalizeSpell() {
if (spellAbility instanceof AdventureCardSpellAbility) {
((AdventureCardSpellAbility) spellAbility).finalizeAdventure();
}
}
protected AdventureCardSpellImpl(final AdventureCardSpellImpl card) {
protected AdventureSpellCard(final AdventureSpellCard card) {
super(card);
this.adventureCardParent = card.adventureCardParent;
}
@ -83,13 +83,13 @@ public class AdventureCardSpellImpl extends CardImpl implements AdventureCardSpe
}
@Override
public AdventureCardSpellImpl copy() {
return new AdventureCardSpellImpl(this);
public AdventureSpellCard copy() {
return new AdventureSpellCard(this);
}
@Override
public void setParentCard(AdventureCard card) {
this.adventureCardParent = card;
public void setParentCard(CardWithSpellOption card) {
this.adventureCardParent = (AdventureCard) card;
}
@Override
@ -102,6 +102,11 @@ public class AdventureCardSpellImpl extends CardImpl implements AdventureCardSpe
// id must send to main card (popup card hint in game logs)
return getName() + " [" + adventureCardParent.getId().toString().substring(0, 3) + ']';
}
@Override
public String getSpellType() {
return "Adventure";
}
}
class AdventureCardSpellAbility extends SpellAbility {
@ -141,8 +146,8 @@ class AdventureCardSpellAbility extends SpellAbility {
public ActivationStatus canActivate(UUID playerId, Game game) {
ExileZone adventureExileZone = game.getExile().getExileZone(ExileAdventureSpellEffect.adventureExileId(playerId, game));
Card spellCard = game.getCard(this.getSourceId());
if (spellCard instanceof AdventureCardSpell) {
Card card = ((AdventureCardSpell) spellCard).getParentCard();
if (spellCard instanceof AdventureSpellCard) {
Card card = ((AdventureSpellCard) spellCard).getParentCard();
if (adventureExileZone != null && adventureExileZone.contains(card.getId())) {
return ActivationStatus.getFalse();
}

View file

@ -530,8 +530,8 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
}
}
if (stackObject == null && (this instanceof AdventureCard)) {
stackObject = game.getStack().getSpell(((AdventureCard) this).getSpellCard().getId(), false);
if (stackObject == null && (this instanceof CardWithSpellOption)) {
stackObject = game.getStack().getSpell(((CardWithSpellOption) this).getSpellCard().getId(), false);
}
if (stackObject == null) {

View file

@ -0,0 +1,131 @@
package mage.cards;
import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl;
import mage.abilities.Ability;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
import mage.util.CardUtil;
import java.util.List;
import java.util.UUID;
/**
* @author phulin, jmlundeen
*/
public abstract class CardWithSpellOption extends CardImpl {
/* The adventure/omen spell card, i.e. Swift End. */
protected SpellOptionCard spellCard;
public CardWithSpellOption(UUID ownerId, CardSetInfo setInfo, CardType[] types, String costs) {
super(ownerId, setInfo, types, costs);
}
public CardWithSpellOption(CardWithSpellOption card) {
super(card);
this.spellCard = card.getSpellCard().copy();
this.spellCard.setParentCard(this);
}
public SpellOptionCard getSpellCard() {
return spellCard;
}
public void setParts(SpellOptionCard cardSpell) {
// for card copy only - set new parts
this.spellCard = cardSpell;
cardSpell.setParentCard(this);
}
@Override
public void assignNewId() {
super.assignNewId();
spellCard.assignNewId();
}
@Override
public boolean moveToZone(Zone toZone, Ability source, Game game, boolean flag, List<UUID> appliedEffects) {
if (super.moveToZone(toZone, source, game, flag, appliedEffects)) {
Zone currentZone = game.getState().getZone(getId());
game.getState().setZone(getSpellCard().getId(), currentZone);
return true;
}
return false;
}
@Override
public void setZone(Zone zone, Game game) {
super.setZone(zone, game);
game.setZone(getSpellCard().getId(), zone);
}
@Override
public boolean moveToExile(UUID exileId, String name, Ability source, Game game, List<UUID> appliedEffects) {
if (super.moveToExile(exileId, name, source, game, appliedEffects)) {
Zone currentZone = game.getState().getZone(getId());
game.getState().setZone(getSpellCard().getId(), currentZone);
return true;
}
return false;
}
@Override
public boolean removeFromZone(Game game, Zone fromZone, Ability source) {
// zone contains only one main card
return super.removeFromZone(game, fromZone, source);
}
@Override
public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) {
if (isCopy()) { // same as meld cards
super.updateZoneChangeCounter(game, event);
return;
}
super.updateZoneChangeCounter(game, event);
getSpellCard().updateZoneChangeCounter(game, event);
}
@Override
public Abilities<Ability> getAbilities() {
Abilities<Ability> allAbilities = new AbilitiesImpl<>();
allAbilities.addAll(spellCard.getAbilities());
allAbilities.addAll(super.getAbilities());
return allAbilities;
}
@Override
public Abilities<Ability> getInitAbilities() {
// must init only parent related abilities, spell card must be init separately
return super.getAbilities();
}
@Override
public Abilities<Ability> getAbilities(Game game) {
Abilities<Ability> allAbilities = new AbilitiesImpl<>();
allAbilities.addAll(spellCard.getAbilities(game));
allAbilities.addAll(super.getAbilities(game));
return allAbilities;
}
public Abilities<Ability> getSharedAbilities(Game game) {
// abilities without spellCard
return super.getAbilities(game);
}
public List<String> getSharedRules(Game game) {
// rules without spellCard
Abilities<Ability> sourceAbilities = this.getSharedAbilities(game);
return CardUtil.getCardRulesWithAdditionalInfo(game, this, sourceAbilities, sourceAbilities);
}
@Override
public void setOwnerId(UUID ownerId) {
super.setOwnerId(ownerId);
abilities.setControllerId(ownerId);
spellCard.getAbilities().setControllerId(ownerId);
spellCard.setOwnerId(ownerId);
}
}

View file

@ -0,0 +1,34 @@
package mage.cards;
import mage.abilities.SpellAbility;
import mage.constants.CardType;
import mage.constants.SpellAbilityType;
import mage.constants.Zone;
import mage.game.Game;
import java.util.UUID;
public abstract class OmenCard extends CardWithSpellOption {
public OmenCard(UUID ownerId, CardSetInfo setInfo, CardType[] types, CardType[] typesSpell, String costs, String omenName, String costsSpell) {
super(ownerId, setInfo, types, costs);
this.spellCard = new OmenSpellCard(ownerId, setInfo, omenName, typesSpell, costsSpell, this);
}
public OmenCard(OmenCard card) {
super(card);
}
public void finalizeOmen() {
spellCard.finalizeSpell();
}
@Override
public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) {
if (ability.getSpellAbilityType() == SpellAbilityType.OMEN_SPELL) {
return this.getSpellCard().cast(game, fromZone, ability, controllerId);
}
this.getSpellCard().getSpellAbility().setControllerId(controllerId);
return super.cast(game, fromZone, ability, controllerId);
}
}

View file

@ -0,0 +1,174 @@
package mage.cards;
import mage.abilities.Ability;
import mage.abilities.Modes;
import mage.abilities.SpellAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect;
import mage.constants.CardType;
import mage.constants.SpellAbilityType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
public class OmenSpellCard extends CardImpl implements SpellOptionCard {
private OmenCard omenCardParent;
public OmenSpellCard(UUID ownerId, CardSetInfo setInfo, String omenName, CardType[] cardTypes, String costs, OmenCard omenCard) {
super(ownerId, setInfo, cardTypes, costs, SpellAbilityType.OMEN_SPELL);
this.subtype.add(SubType.OMEN);
OmenCardSpellAbility newSpellAbility = new OmenCardSpellAbility(getSpellAbility(), omenName, cardTypes, costs);
this.replaceSpellAbility(newSpellAbility);
spellAbility = newSpellAbility;
this.setName(omenName);
this.omenCardParent = omenCard;
}
public void finalizeSpell() {
if (spellAbility instanceof OmenCardSpellAbility) {
((OmenCardSpellAbility) spellAbility).finalizeOmen();
}
}
protected OmenSpellCard(final OmenSpellCard card) {
super(card);
this.omenCardParent = card.omenCardParent;
}
@Override
public UUID getOwnerId() {
return omenCardParent.getOwnerId();
}
@Override
public String getExpansionSetCode() {
return omenCardParent.getExpansionSetCode();
}
@Override
public String getCardNumber() {
return omenCardParent.getCardNumber();
}
@Override
public boolean moveToZone(Zone toZone, Ability source, Game game, boolean flag, List<UUID> appliedEffects) {
return omenCardParent.moveToZone(toZone, source, game, flag, appliedEffects);
}
@Override
public boolean moveToExile(UUID exileId, String name, Ability source, Game game, List<UUID> appliedEffects) {
return omenCardParent.moveToExile(exileId, name, source, game, appliedEffects);
}
@Override
public OmenCard getMainCard() {
return omenCardParent;
}
@Override
public void setZone(Zone zone, Game game) {
game.setZone(omenCardParent.getId(), zone);
game.setZone(omenCardParent.getSpellCard().getId(), zone);
}
@Override
public OmenSpellCard copy() {
return new OmenSpellCard(this);
}
@Override
public void setParentCard(CardWithSpellOption card) {
this.omenCardParent = (OmenCard) card;
}
@Override
public OmenCard getParentCard() {
return this.omenCardParent;
}
@Override
public String getIdName() {
// id must send to main card (popup card hint in game logs)
return getName() + " [" + omenCardParent.getId().toString().substring(0, 3) + ']';
}
@Override
public String getSpellType() {
return "Omen";
}
}
class OmenCardSpellAbility extends SpellAbility {
private String nameFull;
private boolean finalized = false;
public OmenCardSpellAbility(final SpellAbility baseSpellAbility, String omenName, CardType[] cardTypes, String costs) {
super(baseSpellAbility);
this.setName(cardTypes, omenName, costs);
this.setCardName(omenName);
}
public void finalizeOmen() {
if (finalized) {
throw new IllegalStateException("Wrong code usage. "
+ "Omen (" + cardName + ") "
+ "need to call finalizeOmen() exactly once.");
}
Effect effect = new ShuffleIntoLibrarySourceEffect();
effect.setText("");
this.addEffect(effect);
this.finalized = true;
}
protected OmenCardSpellAbility(final OmenCardSpellAbility ability) {
super(ability);
this.nameFull = ability.nameFull;
if (!ability.finalized) {
throw new IllegalStateException("Wrong code usage. "
+ "Omen (" + cardName + ") "
+ "need to call finalizeOmen() at the very end of the card's constructor.");
}
this.finalized = true;
}
public void setName(CardType[] cardTypes, String omenName, String costs) {
this.nameFull = "Omen " + Arrays.stream(cardTypes).map(CardType::toString).collect(Collectors.joining(" ")) + " &mdash; " + omenName;
this.name = this.nameFull + " " + costs;
}
@Override
public String getRule(boolean all) {
return this.getRule();
}
@Override
public String getRule() {
StringBuilder sbRule = new StringBuilder();
sbRule.append(this.nameFull);
sbRule.append(" ");
sbRule.append(getManaCosts().getText());
sbRule.append(" &mdash; ");
Modes modes = this.getModes();
if (modes.size() <= 1) {
sbRule.append(modes.getMode().getEffects().getTextStartingUpperCase(modes.getMode()));
} else {
sbRule.append(getModes().getText());
}
sbRule.append(" <i>(Then shuffle this card into its owner's library.)<i>");
return sbRule.toString();
}
@Override
public OmenCardSpellAbility copy() {
return new OmenCardSpellAbility(this);
}
}

View file

@ -0,0 +1,18 @@
package mage.cards;
public interface SpellOptionCard extends SubCard<CardWithSpellOption> {
@Override
SpellOptionCard copy();
/**
* Adds the final shared ability to the card. e.g. Adventure exile effect / Omen shuffle effect
*/
void finalizeSpell();
/**
* Used to get the card type text such as Adventure. Currently only used in {@link mage.game.stack.Spell#getSpellCastText Spell} for logging the spell
* being cast as part of the two part card.
*/
String getSpellType();
}

View file

@ -20,7 +20,7 @@ import java.util.List;
*/
public class MockCard extends CardImpl implements MockableCard {
public static String ADVENTURE_NAME_SEPARATOR = " // ";
public static String CARD_WITH_SPELL_OPTION_NAME_SEPARATOR = " // ";
public static String MODAL_DOUBLE_FACES_NAME_SEPARATOR = " // ";
// Needs to be here, as it is normally calculated from the
@ -34,7 +34,7 @@ public class MockCard extends CardImpl implements MockableCard {
protected List<String> manaCostLeftStr;
protected List<String> manaCostRightStr;
protected List<String> manaCostStr;
protected String adventureSpellName;
protected String spellOptionName; // adventure/omen spell name
protected boolean isModalDoubleFacedCard;
protected int manaValue;
@ -71,8 +71,8 @@ public class MockCard extends CardImpl implements MockableCard {
this.secondSideCard = new MockCard(CardRepository.instance.findCardWithPreferredSetAndNumber(card.getSecondSideName(), card.getSetCode(), card.getCardNumber()));
}
if (card.isAdventureCard()) {
this.adventureSpellName = card.getAdventureSpellName();
if (card.isCardWithSpellOption()) {
this.spellOptionName = card.getSpellOptionCardName();
}
if (card.isModalDoubleFacedCard()) {
@ -101,7 +101,7 @@ public class MockCard extends CardImpl implements MockableCard {
this.manaCostLeftStr = new ArrayList<>(card.manaCostLeftStr);
this.manaCostRightStr = new ArrayList<>(card.manaCostRightStr);
this.manaCostStr = new ArrayList<>(card.manaCostStr);
this.adventureSpellName = card.adventureSpellName;
this.spellOptionName = card.spellOptionName;
this.isModalDoubleFacedCard = card.isModalDoubleFacedCard;
this.manaValue = card.manaValue;
}
@ -155,8 +155,8 @@ public class MockCard extends CardImpl implements MockableCard {
return getName();
}
if (adventureSpellName != null) {
return getName() + ADVENTURE_NAME_SEPARATOR + adventureSpellName;
if (spellOptionName != null) {
return getName() + CARD_WITH_SPELL_OPTION_NAME_SEPARATOR + spellOptionName;
} else if (isModalDoubleFacedCard) {
return getName() + MODAL_DOUBLE_FACES_NAME_SEPARATOR + this.getSecondCardFace().getName();
} else {

View file

@ -106,9 +106,9 @@ public class CardInfo {
@DatabaseField
protected String secondSideName;
@DatabaseField
protected boolean adventureCard;
protected boolean cardWithSpellOption;
@DatabaseField
protected String adventureSpellName;
protected String spellOptionCardName;
@DatabaseField
protected boolean modalDoubleFacedCard;
@DatabaseField
@ -157,9 +157,9 @@ public class CardInfo {
this.secondSideName = secondSide.getName();
}
if (card instanceof AdventureCard) {
this.adventureCard = true;
this.adventureSpellName = ((AdventureCard) card).getSpellCard().getName();
if (card instanceof CardWithSpellOption) {
this.cardWithSpellOption = true;
this.spellOptionCardName = ((CardWithSpellOption) card).getSpellCard().getName();
}
if (card instanceof ModalDoubleFacedCard) {
@ -189,8 +189,8 @@ public class CardInfo {
List<String> manaCostLeft = ((ModalDoubleFacedCard) card).getLeftHalfCard().getManaCostSymbols();
List<String> manaCostRight = ((ModalDoubleFacedCard) card).getRightHalfCard().getManaCostSymbols();
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
} else if (card instanceof AdventureCard) {
List<String> manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCostSymbols();
} else if (card instanceof CardWithSpellOption) {
List<String> manaCostLeft = ((CardWithSpellOption) card).getSpellCard().getManaCostSymbols();
List<String> manaCostRight = card.getManaCostSymbols();
this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight));
} else {
@ -469,12 +469,16 @@ public class CardInfo {
return secondSideName;
}
public boolean isAdventureCard() {
return adventureCard;
public boolean isCardWithSpellOption() {
return cardWithSpellOption;
}
public String getAdventureSpellName() {
return adventureSpellName;
/**
* used for spell card portion of adventure/omen cards
* @return name of the spell
*/
public String getSpellOptionCardName() {
return spellOptionCardName;
}
public boolean isModalDoubleFacedCard() {

View file

@ -147,8 +147,8 @@ public enum CardRepository {
if (card.getMeldsToCardName() != null && !card.getMeldsToCardName().isEmpty()) {
namesList.add(card.getMeldsToCardName());
}
if (card.getAdventureSpellName() != null && !card.getAdventureSpellName().isEmpty()) {
namesList.add(card.getAdventureSpellName());
if (card.getSpellOptionCardName() != null && !card.getSpellOptionCardName().isEmpty()) {
namesList.add(card.getSpellOptionCardName());
}
}
@ -160,7 +160,7 @@ public enum CardRepository {
Set<String> names = new TreeSet<>();
try {
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "adventureSpellName");
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "spellOptionCardName");
List<CardInfo> results = cardsDao.query(qb.prepare());
for (CardInfo card : results) {
addNewNames(card, names);
@ -176,7 +176,7 @@ public enum CardRepository {
Set<String> names = new TreeSet<>();
try {
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "adventureSpellName");
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "spellOptionCardName");
qb.where().not().like("types", new SelectArg('%' + CardType.LAND.name() + '%'));
List<CardInfo> results = cardsDao.query(qb.prepare());
for (CardInfo card : results) {
@ -193,7 +193,7 @@ public enum CardRepository {
Set<String> names = new TreeSet<>();
try {
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "adventureSpellName");
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "spellOptionCardName");
Where<CardInfo, Object> where = qb.where();
where.and(
where.not().like("supertypes", '%' + SuperType.BASIC.name() + '%'),
@ -214,7 +214,7 @@ public enum CardRepository {
Set<String> names = new TreeSet<>();
try {
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "adventureSpellName");
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "spellOptionCardName");
qb.where().not().like("supertypes", new SelectArg('%' + SuperType.BASIC.name() + '%'));
List<CardInfo> results = cardsDao.query(qb.prepare());
for (CardInfo card : results) {
@ -231,7 +231,7 @@ public enum CardRepository {
Set<String> names = new TreeSet<>();
try {
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "adventureSpellName");
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "spellOptionCardName");
qb.where().like("types", new SelectArg('%' + CardType.CREATURE.name() + '%'));
List<CardInfo> results = cardsDao.query(qb.prepare());
for (CardInfo card : results) {
@ -248,7 +248,7 @@ public enum CardRepository {
Set<String> names = new TreeSet<>();
try {
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "adventureSpellName");
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "spellOptionCardName");
qb.where().like("types", new SelectArg('%' + CardType.ARTIFACT.name() + '%'));
List<CardInfo> results = cardsDao.query(qb.prepare());
for (CardInfo card : results) {
@ -265,7 +265,7 @@ public enum CardRepository {
Set<String> names = new TreeSet<>();
try {
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "adventureSpellName");
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "spellOptionCardName");
Where<CardInfo, Object> where = qb.where();
where.and(
where.not().like("types", '%' + CardType.CREATURE.name() + '%'),
@ -286,7 +286,7 @@ public enum CardRepository {
Set<String> names = new TreeSet<>();
try {
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "adventureSpellName");
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName", "spellOptionCardName");
Where<CardInfo, Object> where = qb.where();
where.and(
where.not().like("types", '%' + CardType.ARTIFACT.name() + '%'),
@ -511,7 +511,7 @@ public enum CardRepository {
queryBuilder.where()
.eq("flipCardName", new SelectArg(name)).or()
.eq("secondSideName", new SelectArg(name)).or()
.eq("adventureSpellName", new SelectArg(name)).or()
.eq("spellOptionCardName", new SelectArg(name)).or()
.eq("modalDoubleFacedSecondSideName", new SelectArg(name));
results = cardsDao.query(queryBuilder.prepare());
} else {

View file

@ -15,7 +15,8 @@ public enum SpellAbilityType {
MODAL_LEFT("LeftModal SpellAbility"),
MODAL_RIGHT("RightModal SpellAbility"),
SPLICE("Spliced SpellAbility"),
ADVENTURE_SPELL("Adventure SpellAbility");
ADVENTURE_SPELL("Adventure SpellAbility"),
OMEN_SPELL("Omen SpellAbility");
private final String text;

View file

@ -13,6 +13,7 @@ public enum SubType {
ADVENTURE("Adventure", SubTypeSet.SpellType),
ARCANE("Arcane", SubTypeSet.SpellType),
LESSON("Lesson", SubTypeSet.SpellType),
OMEN("Omen", SubTypeSet.SpellType),
TRAP("Trap", SubTypeSet.SpellType),
// Battle subtypes

View file

@ -1,9 +1,6 @@
package mage.filter.predicate.card;
import mage.cards.AdventureCard;
import mage.cards.Card;
import mage.cards.ModalDoubleFacedCard;
import mage.cards.SplitCard;
import mage.cards.*;
import mage.cards.mock.MockCard;
import mage.constants.SubType;
import mage.constants.SuperType;
@ -55,8 +52,8 @@ public class CardTextPredicate implements Predicate<Card> {
fullName = ((MockCard) input).getFullName(true);
} else if (input instanceof ModalDoubleFacedCard) {
fullName = input.getName() + MockCard.MODAL_DOUBLE_FACES_NAME_SEPARATOR + ((ModalDoubleFacedCard) input).getRightHalfCard().getName();
} else if (input instanceof AdventureCard) {
fullName = input.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + ((AdventureCard) input).getSpellCard().getName();
} else if (input instanceof CardWithSpellOption) {
fullName = input.getName() + MockCard.CARD_WITH_SPELL_OPTION_NAME_SEPARATOR + ((CardWithSpellOption) input).getSpellCard().getName();
}
if (fullName.toLowerCase(Locale.ENGLISH).contains(text.toLowerCase(Locale.ENGLISH))) {
@ -107,8 +104,8 @@ public class CardTextPredicate implements Predicate<Card> {
}
}
if (input instanceof AdventureCard) {
for (String rule : ((AdventureCard) input).getSpellCard().getRules(game)) {
if (input instanceof CardWithSpellOption) {
for (String rule : ((CardWithSpellOption) input).getSpellCard().getRules(game)) {
if (rule.toLowerCase(Locale.ENGLISH).contains(token)) {
found = true;
break;

View file

@ -334,8 +334,8 @@ public abstract class GameImpl implements Game {
Card rightCard = ((ModalDoubleFacedCard) card).getRightHalfCard();
rightCard.setOwnerId(ownerId);
addCardToState(rightCard);
} else if (card instanceof AdventureCard) {
Card spellCard = ((AdventureCard) card).getSpellCard();
} else if (card instanceof CardWithSpellOption) {
Card spellCard = ((CardWithSpellOption) card).getSpellCard();
spellCard.setOwnerId(ownerId);
addCardToState(spellCard);
} else if (card.isTransformable() && card.getSecondCardFace() != null) {

View file

@ -1639,14 +1639,14 @@ public class GameState implements Serializable, Copyable<GameState> {
copiedParts.add(rightCopied);
// sync parts
((ModalDoubleFacedCard) copiedCard).setParts(leftCopied, rightCopied);
} else if (copiedCard instanceof AdventureCard) {
} else if (copiedCard instanceof CardWithSpellOption) {
// right
AdventureCardSpell rightOriginal = ((AdventureCard) copiedCard).getSpellCard();
AdventureCardSpell rightCopied = rightOriginal.copy();
SpellOptionCard rightOriginal = ((CardWithSpellOption) copiedCard).getSpellCard();
SpellOptionCard rightCopied = rightOriginal.copy();
prepareCardForCopy(rightOriginal, rightCopied, newController);
copiedParts.add(rightCopied);
// sync parts
((AdventureCard) copiedCard).setParts(rightCopied);
((CardWithSpellOption) copiedCard).setParts(rightCopied);
}
// main part prepare (must be called after other parts cause it change ids for all)

View file

@ -209,10 +209,11 @@ public class Spell extends StackObjectImpl implements Card {
+ " using " + this.getSpellAbility().getSpellAbilityCastMode();
}
if (card instanceof AdventureCardSpell) {
AdventureCard adventureCard = ((AdventureCardSpell) card).getParentCard();
return GameLog.replaceNameByColoredName(card, getSpellAbility().toString(), adventureCard)
+ " as Adventure spell of " + GameLog.getColoredObjectIdName(adventureCard);
if (card instanceof SpellOptionCard) {
CardWithSpellOption parentCard = ((SpellOptionCard) card).getParentCard();
String type = ((SpellOptionCard) card).getSpellType();
return GameLog.replaceNameByColoredName(card, getSpellAbility().toString(), parentCard)
+ " as " + type + " spell of " + GameLog.getColoredObjectIdName(parentCard);
}
if (card instanceof ModalDoubleFacedCardHalf) {
@ -539,8 +540,8 @@ public class Spell extends StackObjectImpl implements Card {
public String getIdName() {
String idName;
if (card != null) {
if (card instanceof AdventureCardSpell) {
idName = ((AdventureCardSpell) card).getParentCard().getId().toString().substring(0, 3);
if (card instanceof SpellOptionCard) {
idName = ((SpellOptionCard) card).getParentCard().getId().toString().substring(0, 3);
} else {
idName = card.getId().toString().substring(0, 3);
}

View file

@ -4068,11 +4068,11 @@ public abstract class PlayerImpl implements Player, Serializable {
getPlayableFromObjectSingle(game, fromZone, mainCard.getLeftHalfCard(), mainCard.getLeftHalfCard().getAbilities(game), availableMana, output);
getPlayableFromObjectSingle(game, fromZone, mainCard.getRightHalfCard(), mainCard.getRightHalfCard().getAbilities(game), availableMana, output);
getPlayableFromObjectSingle(game, fromZone, mainCard, mainCard.getSharedAbilities(game), availableMana, output);
} else if (object instanceof AdventureCard) {
} else if (object instanceof CardWithSpellOption) {
// adventure must use different card characteristics for different spells (main or adventure)
AdventureCard adventureCard = (AdventureCard) object;
getPlayableFromObjectSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(game), availableMana, output);
getPlayableFromObjectSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(game), availableMana, output);
CardWithSpellOption cardWithSpellOption = (CardWithSpellOption) object;
getPlayableFromObjectSingle(game, fromZone, cardWithSpellOption.getSpellCard(), cardWithSpellOption.getSpellCard().getAbilities(game), availableMana, output);
getPlayableFromObjectSingle(game, fromZone, cardWithSpellOption, cardWithSpellOption.getSharedAbilities(game), availableMana, output);
} else if (object instanceof Card) {
getPlayableFromObjectSingle(game, fromZone, object, ((Card) object).getAbilities(game), availableMana, output);
} else if (object instanceof StackObject) {

View file

@ -1272,7 +1272,7 @@ public final class CardUtil {
Card permCard;
if (card instanceof SplitCard) {
permCard = card;
} else if (card instanceof AdventureCard) {
} else if (card instanceof CardWithSpellOption) {
permCard = card;
} else if (card instanceof ModalDoubleFacedCard) {
permCard = ((ModalDoubleFacedCard) card).getLeftHalfCard();
@ -1460,9 +1460,9 @@ public final class CardUtil {
if (cardToCast instanceof CardWithHalves) {
cards.add(((CardWithHalves) cardToCast).getLeftHalfCard());
cards.add(((CardWithHalves) cardToCast).getRightHalfCard());
} else if (cardToCast instanceof AdventureCard) {
} else if (cardToCast instanceof CardWithSpellOption) {
cards.add(cardToCast);
cards.add(((AdventureCard) cardToCast).getSpellCard());
cards.add(((CardWithSpellOption) cardToCast).getSpellCard());
} else {
cards.add(cardToCast);
}
@ -1651,9 +1651,9 @@ public final class CardUtil {
}
// handle adventure cards
if (card instanceof AdventureCard) {
if (card instanceof CardWithSpellOption) {
Card creatureCard = card.getMainCard();
Card spellCard = ((AdventureCard) card).getSpellCard();
Card spellCard = ((CardWithSpellOption) card).getSpellCard();
if (manaCost != null) {
// get additional cost if any
Costs<Cost> additionalCostsCreature = creatureCard.getSpellAbility().getCosts();
@ -1691,9 +1691,9 @@ public final class CardUtil {
game.getState().setValue("PlayFromNotOwnHandZone" + leftHalfCard.getId(), null);
game.getState().setValue("PlayFromNotOwnHandZone" + rightHalfCard.getId(), null);
}
if (card instanceof AdventureCard) {
if (card instanceof CardWithSpellOption) {
Card creatureCard = card.getMainCard();
Card spellCard = ((AdventureCard) card).getSpellCard();
Card spellCard = ((CardWithSpellOption) card).getSpellCard();
game.getState().setValue("PlayFromNotOwnHandZone" + creatureCard.getId(), null);
game.getState().setValue("PlayFromNotOwnHandZone" + spellCard.getId(), null);
}
@ -2078,8 +2078,8 @@ public final class CardUtil {
res.add(mainCard);
res.add(mainCard.getLeftHalfCard());
res.add(mainCard.getRightHalfCard());
} else if (object instanceof AdventureCard || object instanceof AdventureCardSpell) {
AdventureCard mainCard = (AdventureCard) ((Card) object).getMainCard();
} else if (object instanceof CardWithSpellOption || object instanceof SpellOptionCard) {
CardWithSpellOption mainCard = (CardWithSpellOption) ((Card) object).getMainCard();
res.add(mainCard);
res.add(mainCard.getSpellCard());
} else if (object instanceof Spell) {

View file

@ -13,10 +13,7 @@ import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.GetXValue;
import mage.abilities.effects.Effect;
import mage.abilities.mana.*;
import mage.cards.AdventureCard;
import mage.cards.Card;
import mage.cards.ModalDoubleFacedCard;
import mage.cards.SplitCard;
import mage.cards.*;
import mage.choices.Choice;
import mage.constants.ColoredManaSymbol;
import mage.constants.ManaType;
@ -644,8 +641,8 @@ public final class ManaUtil {
Card secondSide;
if (card instanceof SplitCard) {
secondSide = ((SplitCard) card).getRightHalfCard();
} else if (card instanceof AdventureCard) {
secondSide = ((AdventureCard) card).getSpellCard();
} else if (card instanceof CardWithSpellOption) {
secondSide = ((CardWithSpellOption) card).getSpellCard();
} else if (card instanceof ModalDoubleFacedCard) {
secondSide = ((ModalDoubleFacedCard) card).getRightHalfCard();
} else {