fix #11211 (CR 602.3)

(Golgothian Sylex and Apocalypse Chime)
This commit is contained in:
xenohedron 2023-09-26 22:21:43 -04:00
parent e66c9900f7
commit 04dba063aa
5 changed files with 78 additions and 110 deletions

View file

@ -1,6 +1,8 @@
package mage.cards.a;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
@ -14,7 +16,7 @@ import mage.constants.CardType;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.card.ExpansionSetPredicate;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.filter.predicate.permanent.TokenPredicate;
/**
@ -26,10 +28,14 @@ public final class ApocalypseChime extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("nontoken permanents with a name originally printed in the Homelands expansion");
static {
filter.add(Predicates.and(
TokenPredicate.FALSE,
new ExpansionSetPredicate("HML")
));
// Homelands names per CR 206.3c
List<String> nameStrings = Arrays.asList("Abbey Gargoyles", "Abbey Matron", "Aether Storm", "Aliban's Tower", "Ambush", "Ambush Party", "Anaba Ancestor", "Anaba Bodyguard", "Anaba Shaman", "Anaba Spirit Crafter", "An-Havva Constable", "An-Havva Inn", "An-Havva Township", "An-Zerrin Ruins", "Apocalypse Chime", "Autumn Willow", "Aysen Abbey", "Aysen Bureaucrats", "Aysen Crusader", "Aysen Highway", "Baki's Curse", "Baron Sengir", "Beast Walkers", "Black Carriage", "Broken Visage", "Carapace", "Castle Sengir", "Cemetery Gate", "Chain Stasis", "Chandler", "Clockwork Gnomes", "Clockwork Steed", "Clockwork Swarm", "Coral Reef", "Dark Maze", "Daughter of Autumn", "Death Speakers", "Didgeridoo", "Drudge Spell", "Dry Spell", "Dwarven Pony", "Dwarven Sea Clan", "Dwarven Trader", "Ebony Rhino", "Eron the Relentless", "Evaporate", "Faerie Noble", "Feast of the Unicorn", "Feroz's Ban", "Folk of An-Havva", "Forget", "Funeral March", "Ghost Hounds", "Giant Albatross", "Giant Oyster", "Grandmother Sengir", "Greater Werewolf", "Hazduhr the Abbot", "Headstone", "Heart Wolf", "Hungry Mist", "Ihsan's Shade", "Irini Sengir", "Ironclaw Curse", "Jinx", "Joven", "Joven's Ferrets", "Joven's Tools", "Koskun Falls", "Koskun Keep", "Labyrinth Minotaur", "Leaping Lizard", "Leeches", "Mammoth Harness", "Marjhan", "Memory Lapse", "Merchant Scroll", "Mesa Falcon", "Mystic Decree", "Narwhal", "Orcish Mine", "Primal Order", "Prophecy", "Rashka the Slayer", "Reef Pirates", "Renewal", "Retribution", "Reveka, Wizard Savant", "Root Spider", "Roots", "Roterothopter", "Rysorian Badger", "Samite Alchemist", "Sea Sprite", "Sea Troll", "Sengir Autocrat", "Sengir Bats", "Serra Aviary", "Serra Bestiary", "Serra Inquisitors", "Serra Paladin", "Serrated Arrows", "Shrink", "Soraya the Falconer", "Spectral Bears", "Timmerian Fiends", "Torture", "Trade Caravan", "Truce", "Veldrane of Sengir", "Wall of Kelp", "Willow Faerie", "Willow Priestess", "Winter Sky", "Wizards' School");
List<NamePredicate> namePredicates = new ArrayList<>();
for (String name: nameStrings) {
namePredicates.add(new NamePredicate(name));
}
filter.add(TokenPredicate.FALSE);
filter.add(Predicates.or(namePredicates));
}
public ApocalypseChime(UUID ownerId, CardSetInfo setInfo) {

View file

@ -15,6 +15,7 @@ import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.game.Game;
@ -22,16 +23,24 @@ import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import static mage.cards.c.CityInABottle.getArabianNightsNamePredicates;
/**
* @author emerald000
*/
public final class CityInABottle extends CardImpl {
// Arabian Nights names per CR 206.3a
static final List<NamePredicate> ARABIAN_NIGHTS_CARD_NAME_PREDICATES = new ArrayList<>();
static {
List<String> nameStrings = Arrays.asList("Abu Ja'far", "Aladdin", "Aladdin's Lamp", "Aladdin's Ring", "Ali Baba", "Ali from Cairo", "Army of Allah", "Bazaar of Baghdad", "Bird Maiden", "Bottle of Suleiman", "Brass Man", "Camel", "City in a Bottle", "City of Brass", "Cuombajj Witches", "Cyclone", "Dancing Scimitar", "Dandan", "Desert", "Desert Nomads", "Desert Twister", "Diamond Valley", "Drop of Honey", "Ebony Horse", "Elephant Graveyard", "El-Hajjaj", "Erg Raiders", "Erhnam Djinn", "Eye for an Eye", "Fishliver Oil", "Flying Carpet", "Flying Men", "Ghazban Ogre", "Giant Tortoise", "Guardian Beast", "Hasran Ogress", "Hurr Jackal", "Ifh-Biff Efreet", "Island Fish Jasconius", "Island of Wak-Wak", "Jandor's Ring", "Jandor's Saddlebags", "Jeweled Bird", "Jihad", "Junun Efreet", "Juzam Djinn", "Khabal Ghoul", "King Suleiman", "Kird Ape", "Library of Alexandria", "Magnetic Mountain", "Merchant Ship", "Metamorphosis", "Mijae Djinn", "Moorish Cavalry", "Nafs Asp", "Oasis", "Old Man of the Sea", "Oubliette", "Piety", "Pyramids", "Repentant Blacksmith", "Ring of Ma'ruf", "Rukh Egg", "Sandals of Abdallah", "Sandstorm", "Serendib Djinn", "Serendib Efreet", "Shahrazad", "Sindbad", "Singing Tree", "Sorceress Queen", "Stone-Throwing Devils", "Unstable Mutation", "War Elephant", "Wyluli Wolf", "Ydwen Efreet");
for (String name : nameStrings) {
ARABIAN_NIGHTS_CARD_NAME_PREDICATES.add(new NamePredicate(name));
}
}
public CityInABottle(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
@ -39,7 +48,7 @@ public final class CityInABottle extends CardImpl {
this.addAbility(new CityInABottleStateTriggeredAbility());
// Players can't play cards originally printed in the Arabian Nights expansion.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CityInABottleCantPlayEffect()));
this.addAbility(new SimpleStaticAbility(new CityInABottleCantPlayEffect()));
}
private CityInABottle(final CityInABottle card) {
@ -51,100 +60,20 @@ public final class CityInABottle extends CardImpl {
return new CityInABottle(this);
}
static public List<NamePredicate> getArabianNightsNamePredicates() {
List<NamePredicate> namePredicatesArabianNights = new ArrayList<>();
namePredicatesArabianNights.add(new NamePredicate("Abu Ja'far"));
namePredicatesArabianNights.add(new NamePredicate("Aladdin"));
namePredicatesArabianNights.add(new NamePredicate("Aladdin's Lamp"));
namePredicatesArabianNights.add(new NamePredicate("Aladdin's Ring"));
namePredicatesArabianNights.add(new NamePredicate("Ali Baba"));
namePredicatesArabianNights.add(new NamePredicate("Ali from Cairo"));
namePredicatesArabianNights.add(new NamePredicate("Army of Allah"));
namePredicatesArabianNights.add(new NamePredicate("Bazaar of Baghdad"));
namePredicatesArabianNights.add(new NamePredicate("Bird Maiden"));
namePredicatesArabianNights.add(new NamePredicate("Bottle of Suleiman"));
namePredicatesArabianNights.add(new NamePredicate("Brass Man"));
namePredicatesArabianNights.add(new NamePredicate("Camel"));
namePredicatesArabianNights.add(new NamePredicate("City of Brass"));
namePredicatesArabianNights.add(new NamePredicate("Cuombajj Witches"));
namePredicatesArabianNights.add(new NamePredicate("Cyclone"));
namePredicatesArabianNights.add(new NamePredicate("Dancing Scimitar"));
namePredicatesArabianNights.add(new NamePredicate("Dandan"));
namePredicatesArabianNights.add(new NamePredicate("Desert"));
namePredicatesArabianNights.add(new NamePredicate("Desert Nomads"));
namePredicatesArabianNights.add(new NamePredicate("Desert Twister"));
namePredicatesArabianNights.add(new NamePredicate("Diamond Valley"));
namePredicatesArabianNights.add(new NamePredicate("Drop of Honey"));
namePredicatesArabianNights.add(new NamePredicate("Ebony Horse"));
namePredicatesArabianNights.add(new NamePredicate("El-Hajjaj"));
namePredicatesArabianNights.add(new NamePredicate("Elephant Graveyard"));
namePredicatesArabianNights.add(new NamePredicate("Erg Raiders"));
namePredicatesArabianNights.add(new NamePredicate("Erhnam Djinn"));
namePredicatesArabianNights.add(new NamePredicate("Eye for an Eye"));
namePredicatesArabianNights.add(new NamePredicate("Fishliver Oil"));
namePredicatesArabianNights.add(new NamePredicate("Flying Carpet"));
namePredicatesArabianNights.add(new NamePredicate("Flying Men"));
namePredicatesArabianNights.add(new NamePredicate("Ghazban Ogre"));
namePredicatesArabianNights.add(new NamePredicate("Giant Tortoise"));
namePredicatesArabianNights.add(new NamePredicate("Guardian Beast"));
namePredicatesArabianNights.add(new NamePredicate("Hasran Ogress"));
namePredicatesArabianNights.add(new NamePredicate("Hurr Jackal"));
namePredicatesArabianNights.add(new NamePredicate("Ifh-Biff Efreet"));
namePredicatesArabianNights.add(new NamePredicate("Island Fish Jasconius"));
namePredicatesArabianNights.add(new NamePredicate("Island of Wak-Wak"));
namePredicatesArabianNights.add(new NamePredicate("Jandor's Ring"));
namePredicatesArabianNights.add(new NamePredicate("Jandor's Saddlebags"));
namePredicatesArabianNights.add(new NamePredicate("Jeweled Bird"));
namePredicatesArabianNights.add(new NamePredicate("Jihad"));
namePredicatesArabianNights.add(new NamePredicate("Junun Efreet"));
namePredicatesArabianNights.add(new NamePredicate("Juzam Djinn"));
namePredicatesArabianNights.add(new NamePredicate("Khabal Ghoul"));
namePredicatesArabianNights.add(new NamePredicate("King Suleiman"));
namePredicatesArabianNights.add(new NamePredicate("Kird Ape"));
namePredicatesArabianNights.add(new NamePredicate("Library of Alexandria"));
namePredicatesArabianNights.add(new NamePredicate("Magnetic Mountain"));
namePredicatesArabianNights.add(new NamePredicate("Merchant Ship"));
namePredicatesArabianNights.add(new NamePredicate("Metamorphosis"));
namePredicatesArabianNights.add(new NamePredicate("Mijae Djinn"));
namePredicatesArabianNights.add(new NamePredicate("Moorish Cavalry"));
namePredicatesArabianNights.add(new NamePredicate("Nafs Asp"));
namePredicatesArabianNights.add(new NamePredicate("Oasis"));
namePredicatesArabianNights.add(new NamePredicate("Old Man of the Sea"));
namePredicatesArabianNights.add(new NamePredicate("Oubliette"));
namePredicatesArabianNights.add(new NamePredicate("Piety"));
namePredicatesArabianNights.add(new NamePredicate("Pyramids"));
namePredicatesArabianNights.add(new NamePredicate("Repentant Blacksmith"));
namePredicatesArabianNights.add(new NamePredicate("Ring of Ma'ruf"));
namePredicatesArabianNights.add(new NamePredicate("Rukh Egg"));
namePredicatesArabianNights.add(new NamePredicate("Sandals of Abdallah"));
namePredicatesArabianNights.add(new NamePredicate("Sandstorm"));
namePredicatesArabianNights.add(new NamePredicate("Serendib Djinn"));
namePredicatesArabianNights.add(new NamePredicate("Serendib Efreet"));
namePredicatesArabianNights.add(new NamePredicate("Shahrazad"));
namePredicatesArabianNights.add(new NamePredicate("Sindbad"));
namePredicatesArabianNights.add(new NamePredicate("Singing Tree"));
namePredicatesArabianNights.add(new NamePredicate("Sorceress Queen"));
namePredicatesArabianNights.add(new NamePredicate("Stone-Throwing Devils"));
namePredicatesArabianNights.add(new NamePredicate("Unstable Mutation"));
namePredicatesArabianNights.add(new NamePredicate("War Elephant"));
namePredicatesArabianNights.add(new NamePredicate("Wyluli Wolf"));
namePredicatesArabianNights.add(new NamePredicate("Ydwen Efreet"));
return namePredicatesArabianNights;
}
}
class CityInABottleStateTriggeredAbility extends StateTriggeredAbility {
private static final FilterPermanent filter = new FilterPermanent("a nontoken permanent originally printed in the Arabian Nights expansion other than City in a Bottle");
private static final FilterPermanent filter = new FilterPermanent("other nontoken permanents with a name originally printed in the Arabian Nights expansion");
static {
filter.add(TokenPredicate.FALSE);
filter.add(Predicates.or(getArabianNightsNamePredicates()));
filter.add(Predicates.or(CityInABottle.ARABIAN_NIGHTS_CARD_NAME_PREDICATES));
filter.add(AnotherPredicate.instance);
}
CityInABottleStateTriggeredAbility() {
super(Zone.BATTLEFIELD, new CityInABottleSacrificeEffect());
setTriggerPhrase("Whenever one or more other nontoken permanents with a name originally printed in the <i>Arabian Nights</i> expansion are on the battlefield, ");
}
private CityInABottleStateTriggeredAbility(final CityInABottleStateTriggeredAbility ability) {
@ -161,24 +90,21 @@ class CityInABottleStateTriggeredAbility extends StateTriggeredAbility {
return game.getBattlefield().contains(filter, this.getControllerId(), this, game, 1);
}
@Override
public String getRule() {
return "Whenever one or more other nontoken permanents with a name originally printed in the <i>Arabian Nights</i> expansion are on the battlefield, their controllers sacrifice them";
}
}
class CityInABottleSacrificeEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent("a nontoken permanent originally printed in the Arabian Nights expansion other than City in a Bottle");
private static final FilterPermanent filter = new FilterPermanent("other nontoken permanents with a name originally printed in the Arabian Nights expansion");
static {
filter.add(TokenPredicate.FALSE);
filter.add(Predicates.or(getArabianNightsNamePredicates()));
filter.add(Predicates.or(CityInABottle.ARABIAN_NIGHTS_CARD_NAME_PREDICATES));
filter.add(AnotherPredicate.instance);
}
CityInABottleSacrificeEffect() {
super(Outcome.Sacrifice);
this.staticText = "its controller sacrifices it";
this.staticText = "their controllers sacrifice them";
}
private CityInABottleSacrificeEffect(final CityInABottleSacrificeEffect effect) {
@ -204,7 +130,7 @@ class CityInABottleCantPlayEffect extends ContinuousRuleModifyingEffectImpl {
private static final FilterCard filter = new FilterCard("cards originally printed in the Arabian Nights expansion");
static {
filter.add(Predicates.or(getArabianNightsNamePredicates()));
filter.add(Predicates.or(CityInABottle.ARABIAN_NIGHTS_CARD_NAME_PREDICATES));
}
CityInABottleCantPlayEffect() {

View file

@ -1,6 +1,8 @@
package mage.cards.g;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
@ -14,7 +16,7 @@ import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.card.ExpansionSetPredicate;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
@ -49,13 +51,17 @@ class GolgothianSylexEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent();
static {
filter.add(Predicates.and(
new ExpansionSetPredicate("ATQ"),
TokenPredicate.FALSE
));
// Antiquities names per CR 206.3b
List<String> nameStrings = Arrays.asList("Amulet of Kroog", "Argivian Archaeologist", "Argivian Blacksmith", "Argothian Pixies", "Argothian Treefolk", "Armageddon Clock", "Artifact Blast", "Artifact Possession", "Artifact Ward", "Ashnod's Altar", "Ashnod's Battle Gear", "Ashnod's Transmogrant", "Atog", "Battering Ram", "Bronze Tablet", "Candelabra of Tawnos", "Circle of Protection: Artifacts", "Citanul Druid", "Clay Statue", "Clockwork Avian", "Colossus of Sardia", "Coral Helm", "Crumble", "Cursed Rack", "Damping Field", "Detonate", "Drafna's Restoration", "Dragon Engine", "Dwarven Weaponsmith", "Energy Flux", "Feldon's Cane", "Gaea's Avenger", "Gate to Phyrexia", "Goblin Artisans", "Golgothian Sylex", "Grapeshot Catapult", "Haunting Wind", "Hurkyl's Recall", "Ivory Tower", "Jalum Tome", "Martyrs of Korlis", "Mightstone", "Millstone", "Mishra's Factory", "Mishra's War Machine", "Mishra's Workshop", "Obelisk of Undoing", "Onulet", "Orcish Mechanics", "Ornithopter", "Phyrexian Gremlins", "Power Artifact", "Powerleech", "Priest of Yawgmoth", "Primal Clay", "The Rack", "Rakalite", "Reconstruction", "Reverse Polarity", "Rocket Launcher", "Sage of Lat-Nam", "Shapeshifter", "Shatterstorm", "Staff of Zegon", "Strip Mine", "Su-Chi", "Tablet of Epityr", "Tawnos's Coffin", "Tawnos's Wand", "Tawnos's Weaponry", "Tetravus", "Titania's Song", "Transmute Artifact", "Triskelion", "Urza's Avenger", "Urza's Chalice", "Urza's Mine", "Urza's Miter", "Urza's Power Plant", "Urza's Tower", "Wall of Spears", "Weakstone", "Xenic Poltergeist", "Yawgmoth Demon", "Yotian Soldier");
List<NamePredicate> namePredicates = new ArrayList<>();
for (String name: nameStrings) {
namePredicates.add(new NamePredicate(name));
}
filter.add(TokenPredicate.FALSE);
filter.add(Predicates.or(namePredicates));
}
public GolgothianSylexEffect() {
GolgothianSylexEffect() {
super(Outcome.Benefit);
this.staticText = "Each nontoken permanent from the Antiquities expansion is sacrificed by its controller.";
}
@ -72,9 +78,9 @@ class GolgothianSylexEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) {
permanent.sacrifice(source, game);
}
return true;
}
}
}

View file

@ -51,4 +51,30 @@ public class NamePredicateTest extends CardTestPlayerBase {
assertNamePredicate("by inner - non existing name must return zero", 0, "Island", true);
assertNamePredicate("by inner - existing name must work", 3, "Forest", true);
}
@Test
public void testCityInABottle() {
String bottle = "City in a Bottle"; // Artifact {2}
// Whenever one or more other nontoken permanents with a name originally printed in the Arabian Nights expansion
// are on the battlefield, their controllers sacrifice them.
// Players cant cast spells or play lands with a name originally printed in the Arabian Nights expansion.
String nomads = "Desert Nomads"; // Creature {2}{R} (originally printed in Arabian Nights)
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); // printed in Arabian Nights, but first printed in Alpha
addCard(Zone.BATTLEFIELD, playerA, "Camel"); // originally printed in Arabian Nights
addCard(Zone.HAND, playerA, bottle);
addCard(Zone.HAND, playerA, nomads);
checkPlayableAbility("Nomads, unbottled", 1, PhaseStep.PRECOMBAT_MAIN , playerA, "Cast Desert Nomads", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bottle);
checkGraveyardCount("Camel sacrificed" , 1, PhaseStep.BEGIN_COMBAT, playerA, "Camel", 1);
checkPlayableAbility("Nomads, bottled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Desert Nomads", false);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, 1);
assertPermanentCount(playerA, bottle, 1);
assertPermanentCount(playerA, "Mountain", 5);
}
}

View file

@ -13,6 +13,10 @@ public class ExpansionSetPredicate implements Predicate<Card> {
private final String setCode;
/**
* Per CR 206.1, the expansion symbol has no effect on game play.
* Use only for utility or silver-bordered / acorn mechanics.
*/
public ExpansionSetPredicate(String setCode) {
this.setCode = setCode;
}