From 92e37a05aac3f9f44039c7e819008b93af06ba28 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 11 Apr 2025 18:34:48 -0400 Subject: [PATCH 01/95] [TDC] Implement Rampart Architect --- .../src/mage/cards/r/RampartArchitect.java | 59 +++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 1 + .../game/permanent/token/Wall13Token.java | 31 ++++++++++ 3 files changed, 91 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/r/RampartArchitect.java create mode 100644 Mage/src/main/java/mage/game/permanent/token/Wall13Token.java diff --git a/Mage.Sets/src/mage/cards/r/RampartArchitect.java b/Mage.Sets/src/mage/cards/r/RampartArchitect.java new file mode 100644 index 00000000000..fd1a0bfde46 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RampartArchitect.java @@ -0,0 +1,59 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.permanent.token.Wall13Token; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RampartArchitect extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("a creature you control with defender"); + + static { + filter.add(new AbilityPredicate(DefenderAbility.class)); + } + + public RampartArchitect(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.ELEPHANT); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Whenever this creature enters or attacks, create a 1/3 white Wall creature token with defender. + this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new CreateTokenEffect(new Wall13Token()))); + + // Whenever a creature you control with defender dies, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle. + this.addAbility(new DiesCreatureTriggeredAbility(new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true + ), true, filter)); + } + + private RampartArchitect(final RampartArchitect card) { + super(card); + } + + @Override + public RampartArchitect copy() { + return new RampartArchitect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index 277b0c89500..ea0680925b3 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -242,6 +242,7 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Putrefy", 300, Rarity.UNCOMMON, mage.cards.p.Putrefy.class)); cards.add(new SetCardInfo("Radiant Grove", 385, Rarity.COMMON, mage.cards.r.RadiantGrove.class)); cards.add(new SetCardInfo("Rampant Growth", 265, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); + cards.add(new SetCardInfo("Rampart Architect", 47, Rarity.RARE, mage.cards.r.RampartArchitect.class)); cards.add(new SetCardInfo("Rapacious Dragon", 229, Rarity.UNCOMMON, mage.cards.r.RapaciousDragon.class)); cards.add(new SetCardInfo("Rapid Hybridization", 162, Rarity.UNCOMMON, mage.cards.r.RapidHybridization.class)); cards.add(new SetCardInfo("Reality Shift", 163, Rarity.UNCOMMON, mage.cards.r.RealityShift.class)); diff --git a/Mage/src/main/java/mage/game/permanent/token/Wall13Token.java b/Mage/src/main/java/mage/game/permanent/token/Wall13Token.java new file mode 100644 index 00000000000..a5f86fe1c91 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/Wall13Token.java @@ -0,0 +1,31 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.DefenderAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class Wall13Token extends TokenImpl { + + public Wall13Token() { + super("Wall Token", "1/3 white Wall creature token with defender"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.WALL); + color.setWhite(true); + power = new MageInt(1); + toughness = new MageInt(3); + + addAbility(DefenderAbility.getInstance()); + } + + private Wall13Token(final Wall13Token token) { + super(token); + } + + public Wall13Token copy() { + return new Wall13Token(this); + } +} -- 2.47.2 From 27a0f361830848835217d8504b96b1b459688919 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 11 Apr 2025 18:46:23 -0400 Subject: [PATCH 02/95] [TDC] Implement Transcendent Dragon --- .../src/mage/cards/t/TranscendentDragon.java | 88 +++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 1 + 2 files changed, 89 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/TranscendentDragon.java diff --git a/Mage.Sets/src/mage/cards/t/TranscendentDragon.java b/Mage.Sets/src/mage/cards/t/TranscendentDragon.java new file mode 100644 index 00000000000..f4aca927772 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TranscendentDragon.java @@ -0,0 +1,88 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.CastFromEverywhereSourceCondition; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.TargetSpell; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TranscendentDragon extends CardImpl { + + public TranscendentDragon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); + + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When this creature enters, if you cast it, counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard, then you may cast it without paying its mana cost. + Ability ability = new EntersBattlefieldTriggeredAbility(new TranscendentDragonEffect()) + .withInterveningIf(CastFromEverywhereSourceCondition.instance); + ability.addTarget(new TargetSpell()); + this.addAbility(ability); + } + + private TranscendentDragon(final TranscendentDragon card) { + super(card); + } + + @Override + public TranscendentDragon copy() { + return new TranscendentDragon(this); + } +} + +class TranscendentDragonEffect extends OneShotEffect { + + TranscendentDragonEffect() { + super(Outcome.Benefit); + staticText = "counter target spell. If that spell is countered this way, " + + "exile it instead of putting it into its owner's graveyard, " + + "then you may cast it without paying its mana cost"; + } + + private TranscendentDragonEffect(final TranscendentDragonEffect effect) { + super(effect); + } + + @Override + public TranscendentDragonEffect copy() { + return new TranscendentDragonEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = game.getSpell(getTargetPointer().getFirst(game, source)); + if (spell == null || !game.getStack().counter(spell.getId(), source, game, PutCards.EXILED)) { + return false; + } + Player player = game.getPlayer(source.getControllerId()); + Card card = spell.getMainCard(); + if (player != null && card != null && Zone.EXILED.match(game.getState().getZone(card.getId()))) { + CardUtil.castSpellWithAttributesForFree(player, source, game, card); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index ea0680925b3..38791495467 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -338,6 +338,7 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Tocasia's Welcome", 135, Rarity.RARE, mage.cards.t.TocasiasWelcome.class)); cards.add(new SetCardInfo("Tower Defense", 275, Rarity.UNCOMMON, mage.cards.t.TowerDefense.class)); cards.add(new SetCardInfo("Towering Titan", 276, Rarity.MYTHIC, mage.cards.t.ToweringTitan.class)); + cards.add(new SetCardInfo("Transcendent Dragon", 22, Rarity.RARE, mage.cards.t.TranscendentDragon.class)); cards.add(new SetCardInfo("Transforming Flourish", 39, Rarity.RARE, mage.cards.t.TransformingFlourish.class)); cards.add(new SetCardInfo("Treasure Cruise", 169, Rarity.COMMON, mage.cards.t.TreasureCruise.class)); cards.add(new SetCardInfo("Tree of Redemption", 97, Rarity.MYTHIC, mage.cards.t.TreeOfRedemption.class)); -- 2.47.2 From 6d9079b2d2b6f9e79497670b43a4533ed659f5f2 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 11 Apr 2025 20:17:43 -0400 Subject: [PATCH 03/95] [TDC] Implement Will of the Mardu --- .../src/mage/cards/w/WillOfTheMardu.java | 113 ++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 1 + 2 files changed, 114 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WillOfTheMardu.java diff --git a/Mage.Sets/src/mage/cards/w/WillOfTheMardu.java b/Mage.Sets/src/mage/cards/w/WillOfTheMardu.java new file mode 100644 index 00000000000..946f4a4c935 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WillOfTheMardu.java @@ -0,0 +1,113 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.condition.common.ControlACommanderCondition; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CreaturesYouControlCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.RedWarriorToken; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class WillOfTheMardu extends CardImpl { + + public WillOfTheMardu(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); + + // Choose one. If you control a commander as you cast this spell, you may choose both instead. + this.getSpellAbility().getModes().setChooseText( + "Choose one. If you control a commander as you cast this spell, you may choose both instead." + ); + this.getSpellAbility().getModes().setMoreCondition(2, ControlACommanderCondition.instance); + + // * Create a number of 1/1 red Warrior creature tokens equal to the number of creatures target player controls. + this.getSpellAbility().addEffect(new CreateTokenEffect(new RedWarriorToken(), WillOfTheMarduValue.instance) + .setText("reate a number of 1/1 red Warrior creature tokens equal to the number of creatures target player controls")); + + // * Will of the Mardu deals damage to target creature equal to the number of creatures you control. + this.getSpellAbility().addMode(new Mode(new DamageTargetEffect(CreaturesYouControlCount.instance) + .setText("{this} deals damage to target creature equal to the number of creatures you control")).addTarget(new TargetCreaturePermanent())); + this.getSpellAbility().addHint(WillOfTheMarduHint.instance); + } + + private WillOfTheMardu(final WillOfTheMardu card) { + super(card); + } + + @Override + public WillOfTheMardu copy() { + return new WillOfTheMardu(this); + } +} + +enum WillOfTheMarduValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Player player = game.getPlayer(effect.getTargetPointer().getFirst(game, sourceAbility)); + return player == null + ? 0 + : game + .getBattlefield() + .count( + StaticFilters.FILTER_CONTROLLED_CREATURE, + player.getId(), sourceAbility, game + ); + } + + @Override + public WillOfTheMarduValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } + + @Override + public String toString() { + return "1"; + } +} + +enum WillOfTheMarduHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + return game.getState() + .getPlayersInRange(ability.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(player -> player.getLogName() + ": " + game.getBattlefield().count( + StaticFilters.FILTER_CONTROLLED_CREATURE, player.getId(), ability, game + )) + .collect(Collectors.joining( + ", ", "Creatures controlled by each player — ", "" + )); + } + + @Override + public Hint copy() { + return this; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index 38791495467..1eb7bde14a7 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -366,6 +366,7 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Welcoming Vampire", 140, Rarity.RARE, mage.cards.w.WelcomingVampire.class)); cards.add(new SetCardInfo("Whirlwind of Thought", 311, Rarity.RARE, mage.cards.w.WhirlwindOfThought.class)); cards.add(new SetCardInfo("Will of the Abzan", 31, Rarity.RARE, mage.cards.w.WillOfTheAbzan.class)); + cards.add(new SetCardInfo("Will of the Mardu", 17, Rarity.RARE, mage.cards.w.WillOfTheMardu.class)); cards.add(new SetCardInfo("Will of the Sultai", 49, Rarity.RARE, mage.cards.w.WillOfTheSultai.class)); cards.add(new SetCardInfo("Will of the Temur", 24, Rarity.RARE, mage.cards.w.WillOfTheTemur.class)); cards.add(new SetCardInfo("Windbrisk Heights", 411, Rarity.RARE, mage.cards.w.WindbriskHeights.class)); -- 2.47.2 From 482745928ea80c788ff59b5febcef232f646a790 Mon Sep 17 00:00:00 2001 From: androosss <101566943+androosss@users.noreply.github.com> Date: Sat, 12 Apr 2025 04:39:21 +0200 Subject: [PATCH 04/95] implement [TDM] All-Out Assault (#13512) - new main phase condition - Added when you attack delayed triggered ability --- .../src/mage/cards/a/AggravatedAssault.java | 3 +- Mage.Sets/src/mage/cards/a/AllOutAssault.java | 57 +++++++++++++++++++ Mage.Sets/src/mage/cards/d/DovinsAcuity.java | 20 +------ Mage.Sets/src/mage/cards/f/FullThrottle.java | 5 +- .../src/mage/cards/f/FuryOfTheHorde.java | 4 +- .../src/mage/cards/g/GrimReapersSprint.java | 36 ++---------- .../src/mage/cards/j/JayaFieryNegotiator.java | 52 +---------------- .../src/mage/cards/l/LastNightTogether.java | 2 +- .../src/mage/cards/m/MoraugFuryOfAkoum.java | 15 +---- .../src/mage/cards/o/OverpoweringAttack.java | 7 +-- .../src/mage/cards/r/RelentlessAssault.java | 3 +- .../src/mage/cards/r/ResponseResurgence.java | 1 + Mage.Sets/src/mage/cards/s/SeizeTheDay.java | 4 +- .../src/mage/cards/w/WavesOfAggression.java | 2 +- .../src/mage/sets/TarkirDragonstorm.java | 7 +++ .../WhenYouAttackDelayedTriggeredAbility.java | 49 ++++++++++++++++ .../common/IsMainPhaseCondition.java | 32 +++++++++++ .../common/AddCombatAndMainPhaseEffect.java | 3 +- 18 files changed, 173 insertions(+), 129 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/a/AllOutAssault.java create mode 100644 Mage/src/main/java/mage/abilities/common/delayed/WhenYouAttackDelayedTriggeredAbility.java create mode 100644 Mage/src/main/java/mage/abilities/condition/common/IsMainPhaseCondition.java diff --git a/Mage.Sets/src/mage/cards/a/AggravatedAssault.java b/Mage.Sets/src/mage/cards/a/AggravatedAssault.java index db32fbfd80a..5b014bbdd97 100644 --- a/Mage.Sets/src/mage/cards/a/AggravatedAssault.java +++ b/Mage.Sets/src/mage/cards/a/AggravatedAssault.java @@ -1,6 +1,7 @@ package mage.cards.a; import java.util.UUID; + import mage.abilities.Ability; import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -19,7 +20,7 @@ import mage.filter.StaticFilters; public final class AggravatedAssault extends CardImpl { public AggravatedAssault(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); // {3}{R}{R}: Untap all creatures you control. After this main phase, there is an additional combat phase followed by an additional main phase. Activate this ability only any time you could cast a sorcery. Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new UntapAllControllerEffect(StaticFilters.FILTER_CONTROLLED_CREATURES, "Untap all creatures you control"), new ManaCostsImpl<>("{3}{R}{R}")); diff --git a/Mage.Sets/src/mage/cards/a/AllOutAssault.java b/Mage.Sets/src/mage/cards/a/AllOutAssault.java new file mode 100644 index 00000000000..1e25977cabb --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AllOutAssault.java @@ -0,0 +1,57 @@ +package mage.cards.a; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.WhenYouAttackDelayedTriggeredAbility; +import mage.abilities.condition.common.IsMainPhaseCondition; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AddCombatAndMainPhaseEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.UntapAllControllerEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.StaticFilters; + +/** + * + * @author androosss + */ +public final class AllOutAssault extends CardImpl { + + public AllOutAssault(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}{W}{B}"); + + // Creatures you control get +1/+1 and have deathtouch. + Ability anthemAbility = new SimpleStaticAbility(new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield)); + Effect effect = new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES); + effect.setText("and have deathtouch"); + anthemAbility.addEffect(effect); + this.addAbility(anthemAbility); + + // When this enchantment enters, if it's your main phase, there is an additional combat phase after this phase followed by an additional main phase. When you next attack this turn, untap each creature you control. + TriggeredAbility extraCombatAbility = new EntersBattlefieldTriggeredAbility(new AddCombatAndMainPhaseEffect().setText("there is an additional combat phase after this phase followed by an additional main phase")).setTriggerPhrase("When this enchantment enters, "); + extraCombatAbility.addEffect(new CreateDelayedTriggeredAbilityEffect(new WhenYouAttackDelayedTriggeredAbility( + new UntapAllControllerEffect( + StaticFilters.FILTER_CONTROLLED_CREATURE, "untap each creature you control"), Duration.EndOfTurn, true))); + this.addAbility(extraCombatAbility.withInterveningIf(IsMainPhaseCondition.YOUR)); + + } + + private AllOutAssault(final AllOutAssault card) { + super(card); + } + + @Override + public AllOutAssault copy() { + return new AllOutAssault(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DovinsAcuity.java b/Mage.Sets/src/mage/cards/d/DovinsAcuity.java index ea8560753ea..d9f2fedf31f 100644 --- a/Mage.Sets/src/mage/cards/d/DovinsAcuity.java +++ b/Mage.Sets/src/mage/cards/d/DovinsAcuity.java @@ -3,7 +3,7 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.condition.Condition; +import mage.abilities.condition.common.IsMainPhaseCondition; import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -11,12 +11,8 @@ import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TurnPhase; import mage.filter.FilterSpell; -import mage.game.Game; -import java.util.EnumSet; -import java.util.Set; import java.util.UUID; /** @@ -42,7 +38,7 @@ public final class DovinsAcuity extends CardImpl { this.addAbility(new ConditionalTriggeredAbility( new SpellCastControllerTriggeredAbility( new ReturnToHandSourceEffect(true), filter, true - ), DovinsAcuityCondition.instance, + ), IsMainPhaseCondition.YOUR, "Whenever you cast an instant spell during your main phase, " + "you may return {this} to its owner's hand." )); @@ -56,16 +52,4 @@ public final class DovinsAcuity extends CardImpl { public DovinsAcuity copy() { return new DovinsAcuity(this); } -} - -enum DovinsAcuityCondition implements Condition { - - instance; - private static final Set turnPhases = EnumSet.of(TurnPhase.PRECOMBAT_MAIN, TurnPhase.POSTCOMBAT_MAIN); - - @Override - public boolean apply(Game game, Ability source) { - return game.isActivePlayer(source.getControllerId()) - && turnPhases.contains(game.getTurn().getPhase().getType()); - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/f/FullThrottle.java b/Mage.Sets/src/mage/cards/f/FullThrottle.java index 99feea84526..740867674f2 100644 --- a/Mage.Sets/src/mage/cards/f/FullThrottle.java +++ b/Mage.Sets/src/mage/cards/f/FullThrottle.java @@ -3,8 +3,7 @@ package mage.cards.f; import java.util.UUID; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.condition.OrCondition; -import mage.abilities.condition.common.IsPhaseCondition; +import mage.abilities.condition.common.IsMainPhaseCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.*; import mage.cards.CardImpl; @@ -29,7 +28,7 @@ public final class FullThrottle extends CardImpl { // After this main phase, there are two additional combat phases. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new AdditionalCombatPhaseEffect(2), - new OrCondition(new IsPhaseCondition(TurnPhase.PRECOMBAT_MAIN), new IsPhaseCondition(TurnPhase.POSTCOMBAT_MAIN)), + IsMainPhaseCondition.ANY, "After this main phase, there are two additional combat phases." )); // At the beginning of each combat this turn, untap all creatures that attacked this turn. diff --git a/Mage.Sets/src/mage/cards/f/FuryOfTheHorde.java b/Mage.Sets/src/mage/cards/f/FuryOfTheHorde.java index 3679df4841b..e9953b771c7 100644 --- a/Mage.Sets/src/mage/cards/f/FuryOfTheHorde.java +++ b/Mage.Sets/src/mage/cards/f/FuryOfTheHorde.java @@ -2,6 +2,7 @@ package mage.cards.f; import java.util.UUID; + import mage.ObjectColor; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.ExileFromHandCost; @@ -31,7 +32,7 @@ public final class FuryOfTheHorde extends CardImpl { } public FuryOfTheHorde(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{R}{R}"); // You may exile two red cards from your hand rather than pay Fury of the Horde's mana cost. this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(2, filter)))); @@ -39,7 +40,6 @@ public final class FuryOfTheHorde extends CardImpl { // Untap all creatures that attacked this turn. After this main phase, there is an additional combat phase followed by an additional main phase. this.getSpellAbility().addEffect(new UntapAllEffect(filter2)); this.getSpellAbility().addEffect(new AddCombatAndMainPhaseEffect()); - } private FuryOfTheHorde(final FuryOfTheHorde card) { diff --git a/Mage.Sets/src/mage/cards/g/GrimReapersSprint.java b/Mage.Sets/src/mage/cards/g/GrimReapersSprint.java index 987de512f41..10efdaf4a85 100644 --- a/Mage.Sets/src/mage/cards/g/GrimReapersSprint.java +++ b/Mage.Sets/src/mage/cards/g/GrimReapersSprint.java @@ -4,9 +4,10 @@ import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.IsMainPhaseCondition; import mage.abilities.condition.common.MorbidCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AdditionalCombatPhaseEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.UntapAllControllerEffect; @@ -21,7 +22,6 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import mage.util.CardUtil; @@ -59,7 +59,7 @@ public final class GrimReapersSprint extends CardImpl { "untap each creature you control" ), false ); - triggeredAbility.addEffect(new GrimReapersSprintEffect()); + triggeredAbility.addEffect(new ConditionalOneShotEffect(new AdditionalCombatPhaseEffect(), IsMainPhaseCondition.YOUR, "If it's your main phase, there is an additional combat phase after this phase.")); this.addAbility(triggeredAbility); // Enchanted creature gets +2/+2 and has haste. @@ -110,32 +110,4 @@ class GrimReapersSprintCostModificationEffect extends CostModificationEffectImpl public GrimReapersSprintCostModificationEffect copy() { return new GrimReapersSprintCostModificationEffect(this); } -} - -class GrimReapersSprintEffect extends OneShotEffect { - - GrimReapersSprintEffect() { - super(Outcome.Benefit); - this.staticText = "If it's your main phase, there is an additional combat phase after this phase"; - } - - private GrimReapersSprintEffect(final GrimReapersSprintEffect effect) { - super(effect); - } - - @Override - public GrimReapersSprintEffect copy() { - return new GrimReapersSprintEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null - && game.isActivePlayer(source.getControllerId()) - && game.getTurnPhaseType().isMain()) { - return new AdditionalCombatPhaseEffect().apply(game, source); - } - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/j/JayaFieryNegotiator.java b/Mage.Sets/src/mage/cards/j/JayaFieryNegotiator.java index ded7c8c9f7c..054c69698e7 100644 --- a/Mage.Sets/src/mage/cards/j/JayaFieryNegotiator.java +++ b/Mage.Sets/src/mage/cards/j/JayaFieryNegotiator.java @@ -1,9 +1,8 @@ package mage.cards.j; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; -import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.common.delayed.WhenYouAttackDelayedTriggeredAbility; import mage.abilities.dynamicvalue.common.AttackingCreatureCount; import mage.abilities.effects.common.*; import mage.cards.CardImpl; @@ -12,14 +11,10 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; -import mage.game.Controllable; -import mage.game.Game; import mage.game.command.emblems.JayaFieryNegotiatorEmblem; -import mage.game.events.GameEvent; import mage.game.permanent.token.MonkRedToken; import mage.target.common.TargetOpponentsCreaturePermanent; -import java.util.Objects; import java.util.UUID; /** @@ -42,7 +37,7 @@ public final class JayaFieryNegotiator extends CardImpl { // −2: Choose target creature an opponent controls. Whenever you attack this turn, Jaya, Fiery Negotiator deals damage equal to the number of attacking creatures to that creature. Ability ability = new LoyaltyAbility(new CreateDelayedTriggeredAbilityEffect( - new JayaFieryNegotiatorTriggeredAbility() + new WhenYouAttackDelayedTriggeredAbility(new DamageTargetEffect(new AttackingCreatureCount())) ).setText("choose target creature an opponent controls. Whenever you attack this turn, " + "{this} deals damage equal to the number of attacking creatures to that creature"), -2); ability.addTarget(new TargetOpponentsCreaturePermanent()); @@ -60,45 +55,4 @@ public final class JayaFieryNegotiator extends CardImpl { public JayaFieryNegotiator copy() { return new JayaFieryNegotiator(this); } -} - -class JayaFieryNegotiatorTriggeredAbility extends DelayedTriggeredAbility { - - private static final DynamicValue xValue = new AttackingCreatureCount(); - - JayaFieryNegotiatorTriggeredAbility() { - super(new DamageTargetEffect(xValue), Duration.EndOfTurn, false, false); - } - - private JayaFieryNegotiatorTriggeredAbility(final JayaFieryNegotiatorTriggeredAbility ability) { - super(ability); - } - - @Override - public JayaFieryNegotiatorTriggeredAbility copy() { - return new JayaFieryNegotiatorTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return isControlledBy(game.getCombat().getAttackingPlayerId()) - && game - .getCombat() - .getAttackers() - .stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .map(Controllable::getControllerId) - .anyMatch(this::isControlledBy); - } - - @Override - public String getRule() { - return "Whenever you attack this turn, {this} deals damage equal to the number of attacking creatures to that creature."; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/l/LastNightTogether.java b/Mage.Sets/src/mage/cards/l/LastNightTogether.java index 1988c97d5c3..3b804fc0b31 100644 --- a/Mage.Sets/src/mage/cards/l/LastNightTogether.java +++ b/Mage.Sets/src/mage/cards/l/LastNightTogether.java @@ -82,7 +82,7 @@ class LastNightTogetherEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { // 15.07.2006 If it's somehow not a main phase when Fury of the Horde resolves, all it does is untap all creatures that attacked that turn. No new phases are created. // Same ruling applies here - if (game.getTurnPhaseType() == TurnPhase.PRECOMBAT_MAIN || game.getTurnPhaseType() == TurnPhase.POSTCOMBAT_MAIN) { + if (game.getTurnPhaseType().isMain()) { // At the start of that combat, add a restriction effect preventing other creatures from attacking. TurnMod combat = new TurnMod(game.getState().getActivePlayerId()).withExtraPhase(TurnPhase.COMBAT); game.getState().getTurnMods().add(combat); diff --git a/Mage.Sets/src/mage/cards/m/MoraugFuryOfAkoum.java b/Mage.Sets/src/mage/cards/m/MoraugFuryOfAkoum.java index f446bd4c580..3843f45540f 100644 --- a/Mage.Sets/src/mage/cards/m/MoraugFuryOfAkoum.java +++ b/Mage.Sets/src/mage/cards/m/MoraugFuryOfAkoum.java @@ -6,7 +6,7 @@ import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.LandfallAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; +import mage.abilities.condition.common.IsMainPhaseCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; @@ -41,7 +41,7 @@ public final class MoraugFuryOfAkoum extends CardImpl { // Landfall — Whenever a land you control enters, if it's your main phase, there's an additional combat phase after this phase. At the beginning of that combat, untap all creatures you control. this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new LandfallAbility(new MoraugFuryOfAkoumCombatEffect()), MoraugFuryOfAkoumCondition.instance, + new LandfallAbility(new MoraugFuryOfAkoumCombatEffect()), IsMainPhaseCondition.YOUR, "Landfall — Whenever a land you control enters, " + "if it's your main phase, there's an additional combat phase after this phase. " + "At the beginning of that combat, untap all creatures you control." @@ -58,15 +58,6 @@ public final class MoraugFuryOfAkoum extends CardImpl { } } -enum MoraugFuryOfAkoumCondition implements Condition { - instance; - - @Override - public boolean apply(Game game, Ability source) { - return game.isActivePlayer(source.getControllerId()) && game.getTurnPhaseType().isMain(); - } -} - class MoraugFuryOfAkoumBoostEffect extends ContinuousEffectImpl { MoraugFuryOfAkoumBoostEffect() { @@ -198,4 +189,4 @@ class MoraugFuryOfAkoumWatcher extends Watcher { } } } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/o/OverpoweringAttack.java b/Mage.Sets/src/mage/cards/o/OverpoweringAttack.java index a5e8544e9b1..6be46cd93da 100644 --- a/Mage.Sets/src/mage/cards/o/OverpoweringAttack.java +++ b/Mage.Sets/src/mage/cards/o/OverpoweringAttack.java @@ -2,8 +2,6 @@ package mage.cards.o; import java.util.UUID; -import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.AddCombatAndMainPhaseEffect; import mage.abilities.effects.common.UntapAllControllerEffect; import mage.abilities.keyword.FreerunningAbility; @@ -27,14 +25,13 @@ public final class OverpoweringAttack extends CardImpl { public OverpoweringAttack(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}"); - // Freerunning {2}{R} this.addAbility(new FreerunningAbility("{2}{R}")); // Untap all creatures you control that attacked this turn. If it's your main phase, there is an additional combat phase after this phase, followed by an additional main phase. - this.getSpellAbility().addEffect(new UntapAllControllerEffect(filter, "untap all creatures you control that attacked this turn")); - this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new AddCombatAndMainPhaseEffect(), MyTurnCondition.instance, "If it's your main phase, there is an additional combat phase after this phase, followed by an additional main phase.")); + this.getSpellAbility().addEffect(new UntapAllControllerEffect(filter, "Untap all creatures you control that attacked this turn")); + this.getSpellAbility().addEffect(new AddCombatAndMainPhaseEffect().setText("If it's your main phase, there is an additional combat phase after this phase, followed by an additional main phase")); } private OverpoweringAttack(final OverpoweringAttack card) { diff --git a/Mage.Sets/src/mage/cards/r/RelentlessAssault.java b/Mage.Sets/src/mage/cards/r/RelentlessAssault.java index 3653960e528..8d79c295b4d 100644 --- a/Mage.Sets/src/mage/cards/r/RelentlessAssault.java +++ b/Mage.Sets/src/mage/cards/r/RelentlessAssault.java @@ -2,6 +2,7 @@ package mage.cards.r; import java.util.UUID; + import mage.abilities.effects.common.AddCombatAndMainPhaseEffect; import mage.abilities.effects.common.UntapAllEffect; import mage.cards.CardImpl; @@ -23,7 +24,7 @@ public final class RelentlessAssault extends CardImpl { } public RelentlessAssault(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}"); // Untap all creatures that attacked this turn. After this main phase, there is an additional combat phase followed by an additional main phase. this.getSpellAbility().addEffect(new UntapAllEffect(filter)); diff --git a/Mage.Sets/src/mage/cards/r/ResponseResurgence.java b/Mage.Sets/src/mage/cards/r/ResponseResurgence.java index 3d16a0a0bfa..afeb909dfef 100644 --- a/Mage.Sets/src/mage/cards/r/ResponseResurgence.java +++ b/Mage.Sets/src/mage/cards/r/ResponseResurgence.java @@ -1,6 +1,7 @@ package mage.cards.r; import java.util.UUID; + import mage.abilities.effects.common.AddCombatAndMainPhaseEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; diff --git a/Mage.Sets/src/mage/cards/s/SeizeTheDay.java b/Mage.Sets/src/mage/cards/s/SeizeTheDay.java index af935cf911d..bb1738dd632 100644 --- a/Mage.Sets/src/mage/cards/s/SeizeTheDay.java +++ b/Mage.Sets/src/mage/cards/s/SeizeTheDay.java @@ -2,6 +2,7 @@ package mage.cards.s; import java.util.UUID; + import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.AddCombatAndMainPhaseEffect; import mage.abilities.effects.common.UntapTargetEffect; @@ -9,7 +10,6 @@ import mage.abilities.keyword.FlashbackAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TimingRule; import mage.target.common.TargetCreaturePermanent; /** @@ -19,7 +19,7 @@ import mage.target.common.TargetCreaturePermanent; public final class SeizeTheDay extends CardImpl { public SeizeTheDay(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); // Untap target creature. After this main phase, there is an additional combat phase followed by an additional main phase. this.getSpellAbility().addEffect(new UntapTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/w/WavesOfAggression.java b/Mage.Sets/src/mage/cards/w/WavesOfAggression.java index 42c836e8edd..1ee37490612 100644 --- a/Mage.Sets/src/mage/cards/w/WavesOfAggression.java +++ b/Mage.Sets/src/mage/cards/w/WavesOfAggression.java @@ -25,7 +25,7 @@ public final class WavesOfAggression extends CardImpl { } public WavesOfAggression(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{R/W}{R/W}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R/W}{R/W}"); // Untap all creatures that attacked this turn. After this main phase, there is an additional combat phase followed by an additional main phase. this.getSpellAbility().addEffect(new UntapAllEffect(filter)); diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 866dfa8c84f..f7de48bf98c 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -1,5 +1,8 @@ package mage.sets; +import java.util.Arrays; +import java.util.List; + import mage.cards.ExpansionSet; import mage.constants.Rarity; import mage.constants.SetType; @@ -31,6 +34,10 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Ainok Wayfarer", 134, Rarity.COMMON, mage.cards.a.AinokWayfarer.class)); cards.add(new SetCardInfo("Alchemist's Assistant", 71, Rarity.UNCOMMON, mage.cards.a.AlchemistsAssistant.class)); cards.add(new SetCardInfo("Alesha's Legacy", 72, Rarity.COMMON, mage.cards.a.AleshasLegacy.class)); + cards.add(new SetCardInfo("All-Out Assault", 167, Rarity.MYTHIC, mage.cards.a.AllOutAssault.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("All-Out Assault", 352, Rarity.MYTHIC, mage.cards.a.AllOutAssault.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("All-Out Assault", 405, Rarity.MYTHIC, mage.cards.a.AllOutAssault.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("All-Out Assault", 415, Rarity.MYTHIC, mage.cards.a.AllOutAssault.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ambling Stormshell", 37, Rarity.RARE, mage.cards.a.AmblingStormshell.class)); cards.add(new SetCardInfo("Anafenza, Unyielding Lineage", 2, Rarity.RARE, mage.cards.a.AnafenzaUnyieldingLineage.class)); cards.add(new SetCardInfo("Arashin Sunshield", 3, Rarity.COMMON, mage.cards.a.ArashinSunshield.class)); diff --git a/Mage/src/main/java/mage/abilities/common/delayed/WhenYouAttackDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/WhenYouAttackDelayedTriggeredAbility.java new file mode 100644 index 00000000000..032828b701e --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/delayed/WhenYouAttackDelayedTriggeredAbility.java @@ -0,0 +1,49 @@ +package mage.abilities.common.delayed; + +import java.util.UUID; + +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * @author androosss + */ +public class WhenYouAttackDelayedTriggeredAbility extends DelayedTriggeredAbility { + + public WhenYouAttackDelayedTriggeredAbility(Effect effect) { + this(effect, Duration.EndOfTurn); + } + + public WhenYouAttackDelayedTriggeredAbility(Effect effect, Duration duration) { + this(effect, duration, false); + } + + public WhenYouAttackDelayedTriggeredAbility(Effect effect, Duration duration, boolean triggerOnlyOnce) { + super(effect, duration, triggerOnlyOnce); + setTriggerPhrase((triggerOnlyOnce ? "When you next" : "Whenever you") + " attack" + + (duration == Duration.EndOfTurn ? " this turn, " : ", ")); + } + + protected WhenYouAttackDelayedTriggeredAbility(final WhenYouAttackDelayedTriggeredAbility ability) { + super(ability); + } + + @Override + public WhenYouAttackDelayedTriggeredAbility copy() { + return new WhenYouAttackDelayedTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + UUID attackerId = game.getCombat().getAttackingPlayerId(); + return attackerId != null && attackerId.equals(getControllerId()) && !game.getCombat().getAttackers().isEmpty(); + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/IsMainPhaseCondition.java b/Mage/src/main/java/mage/abilities/condition/common/IsMainPhaseCondition.java new file mode 100644 index 00000000000..4d10747d907 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/IsMainPhaseCondition.java @@ -0,0 +1,32 @@ + +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; + +/** + * @author androosss + */ +public enum IsMainPhaseCondition implements Condition { + + YOUR(true), + ANY(false); + + private final boolean yourMainPhaseOnly; + + IsMainPhaseCondition(boolean yourMainPhaseOnly) { + this.yourMainPhaseOnly = yourMainPhaseOnly; + } + + @Override + public boolean apply(Game game, Ability source) { + return game.getTurnPhaseType().isMain() && + (!yourMainPhaseOnly || game.getActivePlayerId().equals(source.getControllerId())); + } + + @Override + public String toString() { + return "it's" + (yourMainPhaseOnly ? " your " : " ") + "main phase"; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/AddCombatAndMainPhaseEffect.java b/Mage/src/main/java/mage/abilities/effects/common/AddCombatAndMainPhaseEffect.java index 0caa4f4ec02..ddffc5830d6 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/AddCombatAndMainPhaseEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/AddCombatAndMainPhaseEffect.java @@ -35,8 +35,7 @@ public class AddCombatAndMainPhaseEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { // 15.07.2006 If it's somehow not a main phase when Fury of the Horde resolves, all it does is untap all creatures that attacked that turn. No new phases are created. - if (game.getTurnPhaseType() == TurnPhase.PRECOMBAT_MAIN - || game.getTurnPhaseType() == TurnPhase.POSTCOMBAT_MAIN) { + if (game.getTurnPhaseType().isMain()) { // we can't add two turn modes at once, will add additional post combat on delayed trigger resolution TurnMod combat = new TurnMod(source.getControllerId()).withExtraPhase(TurnPhase.COMBAT, TurnPhase.POSTCOMBAT_MAIN); game.getState().getTurnMods().add(combat); -- 2.47.2 From 6aa974c8498fab40945248b64530b6ad5a0cab92 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Fri, 11 Apr 2025 11:40:39 -0500 Subject: [PATCH 05/95] [TDM] Implement Afterlife from the Loam --- .../mage/cards/a/AfterlifeFromTheLoam.java | 74 +++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 2 + 2 files changed, 76 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java diff --git a/Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java b/Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java new file mode 100644 index 00000000000..eff8a06f86d --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java @@ -0,0 +1,74 @@ +package mage.cards.a; + +import java.security.acl.Owner; +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect; +import mage.abilities.keyword.DelveAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.card.OwnerIdPredicate; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; +import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetpointer.EachTargetPointer; + +/** + * + * @author Jmlundeen + */ +public final class AfterlifeFromTheLoam extends CardImpl { + + public AfterlifeFromTheLoam(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{B}{B}{B}"); + + + // Delve + this.addAbility(new DelveAbility(false)); + + // For each player, choose up to one target creature card in that player's graveyard. Put those cards onto the battlefield under your control. They're Zombies in addition to their other types. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect() + .setTargetPointer(new EachTargetPointer()) + .setText("For each player, choose up to one target creature card in that player's graveyard. Put those cards onto the battlefield under your control")); + this.getSpellAbility().addEffect(new AddCardSubTypeTargetEffect(SubType.ZOMBIE, Duration.WhileOnBattlefield) + .setTargetPointer(new EachTargetPointer()) + .setText("they're Zombies in addition to their other types")); + this.getSpellAbility().setTargetAdjuster(AfterlifeFromTheLoamAdjuster.instance); + } + + private AfterlifeFromTheLoam(final AfterlifeFromTheLoam card) { + super(card); + } + + @Override + public AfterlifeFromTheLoam copy() { + return new AfterlifeFromTheLoam(this); + } +} + +enum AfterlifeFromTheLoamAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + for (UUID playerId : game.getState().getPlayersInRange(ability.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + String playerName = ability.isControlledBy(playerId) ? "your" : player.getName() + "'s"; + FilterCreatureCard filter = new FilterCreatureCard("creature card in " + playerName + " graveyard"); + filter.add(new OwnerIdPredicate(playerId)); + ability.addTarget(new TargetCardInGraveyard(0, 1, filter)); + } + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index 1eb7bde14a7..6b9e6add407 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -24,6 +24,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Adaptive Training Post", 18, Rarity.RARE, mage.cards.a.AdaptiveTrainingPost.class)); cards.add(new SetCardInfo("Adarkar Wastes", 338, Rarity.RARE, mage.cards.a.AdarkarWastes.class)); cards.add(new SetCardInfo("Adeline, Resplendent Cathar", 108, Rarity.RARE, mage.cards.a.AdelineResplendentCathar.class)); + cards.add(new SetCardInfo("Afterlife from the Loam", 25, Rarity.RARE, mage.cards.a.AfterlifeFromTheLoam.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Afterlife from the Loam", 65, Rarity.RARE, mage.cards.a.AfterlifeFromTheLoam.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aligned Heart", 12, Rarity.RARE, mage.cards.a.AlignedHeart.class)); cards.add(new SetCardInfo("Amphin Mutineer", 143, Rarity.RARE, mage.cards.a.AmphinMutineer.class)); cards.add(new SetCardInfo("Ancestral Vision", 144, Rarity.RARE, mage.cards.a.AncestralVision.class)); -- 2.47.2 From 554f529f798fc51666f61d69c4125494298aabd1 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Fri, 11 Apr 2025 13:09:28 -0500 Subject: [PATCH 06/95] [TDM] Implement Ainok Strike Leader --- .../src/mage/cards/a/AinokStrikeLeader.java | 119 ++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 2 + 2 files changed, 121 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/a/AinokStrikeLeader.java diff --git a/Mage.Sets/src/mage/cards/a/AinokStrikeLeader.java b/Mage.Sets/src/mage/cards/a/AinokStrikeLeader.java new file mode 100644 index 00000000000..f8411dded83 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AinokStrikeLeader.java @@ -0,0 +1,119 @@ +package mage.cards.a; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.CreateTokenTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.mageobject.CommanderPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.token.GoblinToken; +import mage.target.TargetPlayer; +import mage.target.common.TargetOpponent; +import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetpointer.EachTargetPointer; +import mage.util.CardUtil; + +/** + * + * @author Jmlundeen + */ +public final class AinokStrikeLeader extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature tokens you control"); + private static final FilterCreaturePermanent attackFilter = new FilterCreaturePermanent("this creature and/or your commander"); + + static { + filter.add(TokenPredicate.TRUE); + filter.add(TargetController.YOU.getControllerPredicate()); + + attackFilter.add(AinokStrikeLeaderPredicate.instance); + } + + public AinokStrikeLeader(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.DOG); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever you attack with this creature and/or your commander, for each opponent, create a 1/1 red Goblin creature token that's tapped and attacking that player. + + this.addAbility(new AttacksWithCreaturesTriggeredAbility(new AinokStrikeLeaderEffect(), 1, attackFilter) + .setTriggerPhrase("Whenever you attack with " + attackFilter.getMessage() + ", ")); + + // Sacrifice this creature: Creature tokens you control gain indestructible until end of turn. + Effect effect2 = new GainAbilityAllEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, filter); + this.addAbility(new SimpleActivatedAbility(effect2, new SacrificeSourceCost())); + } + + private AinokStrikeLeader(final AinokStrikeLeader card) { + super(card); + } + + @Override + public AinokStrikeLeader copy() { + return new AinokStrikeLeader(this); + } +} + +enum AinokStrikeLeaderPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + if (input.getObject().getId().equals(input.getSourceId())) { + return input.getObject().getId().equals(input.getSource().getSourceId()); + } + return CommanderPredicate.instance.apply(input.getObject(), game); + + } + + @Override + public String toString() { + return "This creature and/or your commander"; + } +} + +class AinokStrikeLeaderEffect extends OneShotEffect { + + public AinokStrikeLeaderEffect() { + super(Outcome.Benefit); + staticText = "for each opponent, create a 1/1 red Goblin creature token that's tapped and attacking that player"; + } + + protected AinokStrikeLeaderEffect(final AinokStrikeLeaderEffect effect) { + super(effect); + } + + @Override + public AinokStrikeLeaderEffect copy() { + return new AinokStrikeLeaderEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID playerId : game.getOpponents(source.getControllerId())) { + new GoblinToken(false).putOntoBattlefield(1, game, source, source.getControllerId(), true, true, playerId); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index 6b9e6add407..b845acb3300 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -26,6 +26,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Adeline, Resplendent Cathar", 108, Rarity.RARE, mage.cards.a.AdelineResplendentCathar.class)); cards.add(new SetCardInfo("Afterlife from the Loam", 25, Rarity.RARE, mage.cards.a.AfterlifeFromTheLoam.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Afterlife from the Loam", 65, Rarity.RARE, mage.cards.a.AfterlifeFromTheLoam.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ainok Strike Leader", 11, Rarity.RARE, mage.cards.a.AinokStrikeLeader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ainok Strike Leader", 51, Rarity.RARE, mage.cards.a.AinokStrikeLeader.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aligned Heart", 12, Rarity.RARE, mage.cards.a.AlignedHeart.class)); cards.add(new SetCardInfo("Amphin Mutineer", 143, Rarity.RARE, mage.cards.a.AmphinMutineer.class)); cards.add(new SetCardInfo("Ancestral Vision", 144, Rarity.RARE, mage.cards.a.AncestralVision.class)); -- 2.47.2 From 6bfbc5d1490227c63241497f74c5560291af3df3 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Fri, 11 Apr 2025 16:05:56 -0500 Subject: [PATCH 07/95] [TDM] Implement Diviner of Mist --- Mage.Sets/src/mage/cards/d/DivinerOfMist.java | 148 ++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 2 + 2 files changed, 150 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DivinerOfMist.java diff --git a/Mage.Sets/src/mage/cards/d/DivinerOfMist.java b/Mage.Sets/src/mage/cards/d/DivinerOfMist.java new file mode 100644 index 00000000000..87730c43a8d --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DivinerOfMist.java @@ -0,0 +1,148 @@ +package mage.cards.d; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.Card; +import mage.cards.Cards; +import mage.constants.*; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; + +/** + * + * @author Jmlundeen + */ +public final class DivinerOfMist extends CardImpl { + + public DivinerOfMist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); + + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever this creature attacks, mill four cards. You may cast an instant or sorcery spell from your graveyard with mana value 4 or less without paying its mana cost. If that spell would be put into your graveyard, exile it instead. + Ability ability = new AttacksTriggeredAbility(new DivinerOfMistEffect()); + ability.addEffect(new DivinerOfMistReplacementEffect()); + this.addAbility(ability); + } + + private DivinerOfMist(final DivinerOfMist card) { + super(card); + } + + @Override + public DivinerOfMist copy() { + return new DivinerOfMist(this); + } +} + +class DivinerOfMistEffect extends OneShotEffect { + + public DivinerOfMistEffect() { + super(Outcome.PlayForFree); + staticText = "mill four cards. You may cast an instant or sorcery spell from your graveyard with mana value 4 or less without paying its mana cost"; + } + + protected DivinerOfMistEffect(final DivinerOfMistEffect effect) { + super(effect); + } + + @Override + public DivinerOfMistEffect copy() { + return new DivinerOfMistEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards milledCards = controller.millCards(4, source, game); + boolean hasTarget = controller.getGraveyard().getCards(game) + .stream() + .anyMatch(card -> card.getManaValue() <= 4 && card.isInstantOrSorcery(game)); + if (!hasTarget) { + return true; + } + FilterCard filter = new FilterInstantOrSorceryCard(); + filter.add(new ManaValuePredicate(ComparisonType.OR_LESS, 4)); + Target target = new TargetCardInYourGraveyard(filter); + target.withNotTarget(true); + controller.chooseTarget(Outcome.PlayForFree, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + card = card.getMainCard(); + game.getState().setValue("DivinerOfMist", new MageObjectReference(card, game)); + return CardUtil.castSpellWithAttributesForFree(controller, source, game, card, filter); + } + return true; + } +} + +class DivinerOfMistReplacementEffect extends ReplacementEffectImpl { + + public DivinerOfMistReplacementEffect() { + super(Duration.EndOfGame, Outcome.Exile); + staticText = "If that spell would be put into your graveyard, exile it instead."; + } + + protected DivinerOfMistReplacementEffect(final DivinerOfMistReplacementEffect effect) { + super(effect); + } + + @Override + public DivinerOfMistReplacementEffect copy() { + return new DivinerOfMistReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + MageObjectReference card = (MageObjectReference) game.getState().getValue("DivinerOfMist"); + if (card != null) { + ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + } + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (Zone.GRAVEYARD == ((ZoneChangeEvent) event).getToZone()) { + Card eventCard = game.getCard(event.getSourceId()); + MageObjectReference cardMor = (MageObjectReference) game.getState().getValue("DivinerOfMist"); + if (eventCard != null && cardMor != null) { + int zcc = eventCard.getZoneChangeCounter(game); + return eventCard.getId().equals(cardMor.getSourceId()) && zcc == (cardMor.getZoneChangeCounter() + 1); + } + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index b845acb3300..7731a5afb4e 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -107,6 +107,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Disciple of Bolas", 178, Rarity.RARE, mage.cards.d.DiscipleOfBolas.class)); cards.add(new SetCardInfo("Dismantling Wave", 112, Rarity.RARE, mage.cards.d.DismantlingWave.class)); cards.add(new SetCardInfo("Divine Visitation", 113, Rarity.MYTHIC, mage.cards.d.DivineVisitation.class)); + cards.add(new SetCardInfo("Diviner of Mist", 20, Rarity.RARE, mage.cards.d.DivinerOfMist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Diviner of Mist", 60, Rarity.RARE, mage.cards.d.DivinerOfMist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Draconic Lore", 151, Rarity.COMMON, mage.cards.d.DraconicLore.class)); cards.add(new SetCardInfo("Dragon Tempest", 94, Rarity.UNCOMMON, mage.cards.d.DragonTempest.class)); cards.add(new SetCardInfo("Dragon's Hoard", 317, Rarity.RARE, mage.cards.d.DragonsHoard.class)); -- 2.47.2 From d6a80492d47fb51237e7e6f307c2e776630a6277 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Fri, 11 Apr 2025 23:00:32 -0500 Subject: [PATCH 08/95] fix verify --- Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java | 2 -- Mage.Sets/src/mage/cards/a/AinokStrikeLeader.java | 9 --------- 2 files changed, 11 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java b/Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java index eff8a06f86d..6eb1514cc80 100644 --- a/Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java +++ b/Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java @@ -1,6 +1,5 @@ package mage.cards.a; -import java.security.acl.Owner; import java.util.UUID; import mage.abilities.Ability; @@ -14,7 +13,6 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.card.OwnerIdPredicate; -import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; diff --git a/Mage.Sets/src/mage/cards/a/AinokStrikeLeader.java b/Mage.Sets/src/mage/cards/a/AinokStrikeLeader.java index f8411dded83..17a6aae4c58 100644 --- a/Mage.Sets/src/mage/cards/a/AinokStrikeLeader.java +++ b/Mage.Sets/src/mage/cards/a/AinokStrikeLeader.java @@ -7,17 +7,13 @@ import mage.abilities.Ability; import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.effects.common.CreateTokenTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.IndestructibleAbility; import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; @@ -25,11 +21,6 @@ import mage.filter.predicate.mageobject.CommanderPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.token.GoblinToken; -import mage.target.TargetPlayer; -import mage.target.common.TargetOpponent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; -import mage.target.targetpointer.EachTargetPointer; -import mage.util.CardUtil; /** * -- 2.47.2 From 6335cee30c878d338b82273a0290ba7b9848b170 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Sat, 12 Apr 2025 10:35:41 -0500 Subject: [PATCH 09/95] [TDC] Implement Kotis, Sibsig Champion * Give ability to add another cost to CastFromGraveyardOnceEachTurnAbility --- .../src/mage/cards/k/KotisSibsigChampion.java | 98 +++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 1 + .../CastFromGraveyardOnceEachTurnAbility.java | 26 ++++- 3 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/k/KotisSibsigChampion.java diff --git a/Mage.Sets/src/mage/cards/k/KotisSibsigChampion.java b/Mage.Sets/src/mage/cards/k/KotisSibsigChampion.java new file mode 100644 index 00000000000..2a5ea82c714 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KotisSibsigChampion.java @@ -0,0 +1,98 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility; +import mage.abilities.common.EntersBattlefieldOneOrMoreTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.Game; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class KotisSibsigChampion extends CardImpl { + + private static final FilterCreatureCard filter = new FilterCreatureCard("a creature spell"); + private static final FilterCard filter2 = new FilterCard("other cards"); + + static { + filter2.add(AnotherPredicate.instance); + } + + public KotisSibsigChampion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{G}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Once during each of your turns, you may cast a creature spell from your graveyard by exiling three other cards from your graveyard in addition to paying its other costs. + Cost cost = new ExileFromGraveCost(new TargetCardInYourGraveyard(3, filter2)); + cost.setText(cost.getText().replace("exile", "exiling")); + this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter, cost)); + + // Whenever one or more creatures you control enter, if one or more of them entered from a graveyard or was cast from a graveyard, put two +1/+1 counters on Kotis. + this.addAbility(new KotisSibsigTriggeredAbility()); + } + + private KotisSibsigChampion(final KotisSibsigChampion card) { + super(card); + } + + @Override + public KotisSibsigChampion copy() { + return new KotisSibsigChampion(this); + } +} + +class KotisSibsigTriggeredAbility extends EntersBattlefieldOneOrMoreTriggeredAbility { + + KotisSibsigTriggeredAbility() { + super(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), StaticFilters.FILTER_OTHER_CONTROLLED_CREATURES, TargetController.YOU); + setTriggerPhrase("Whenever one or more creatures you control enter, " + + "if one or more of them entered from a graveyard or was cast from a graveyard, "); + } + + private KotisSibsigTriggeredAbility(final KotisSibsigTriggeredAbility ability) { + super(ability); + } + + @Override + public KotisSibsigTriggeredAbility copy() { + return new KotisSibsigTriggeredAbility(this); + } + + @Override + public boolean checkEvent(ZoneChangeEvent event, Game game) { + if (super.checkEvent(event, game)) { + Zone fromZone = event.getFromZone(); + if (fromZone == Zone.GRAVEYARD) { + return true; + } + if (fromZone == Zone.STACK) { + Permanent permanent = event.getTarget(); + Spell spell = game.getSpellOrLKIStack(permanent.getId()); + return spell != null && spell.getFromZone() == Zone.GRAVEYARD; + } + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index 7731a5afb4e..c25cd68e19f 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -191,6 +191,7 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Keiga, the Tide Star", 156, Rarity.RARE, mage.cards.k.KeigaTheTideStar.class)); cards.add(new SetCardInfo("Kessig Wolf Run", 375, Rarity.RARE, mage.cards.k.KessigWolfRun.class)); cards.add(new SetCardInfo("Kodama's Reach", 261, Rarity.COMMON, mage.cards.k.KodamasReach.class)); + cards.add(new SetCardInfo("Kotis, Sibsig Champion", 5, Rarity.MYTHIC, mage.cards.k.KotisSibsigChampion.class)); cards.add(new SetCardInfo("Lathliss, Dragon Queen", 219, Rarity.RARE, mage.cards.l.LathlissDragonQueen.class)); cards.add(new SetCardInfo("Legion Loyalty", 122, Rarity.MYTHIC, mage.cards.l.LegionLoyalty.class)); cards.add(new SetCardInfo("Legion Warboss", 220, Rarity.RARE, mage.cards.l.LegionWarboss.class)); diff --git a/Mage/src/main/java/mage/abilities/common/CastFromGraveyardOnceEachTurnAbility.java b/Mage/src/main/java/mage/abilities/common/CastFromGraveyardOnceEachTurnAbility.java index 17ddc9a8bd5..3a0f520b724 100644 --- a/Mage/src/main/java/mage/abilities/common/CastFromGraveyardOnceEachTurnAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CastFromGraveyardOnceEachTurnAbility.java @@ -4,6 +4,9 @@ import mage.MageIdentifier; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.SpellAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; import mage.abilities.effects.AsThoughEffectImpl; import mage.cards.Card; import mage.constants.*; @@ -28,7 +31,11 @@ import java.util.UUID; public class CastFromGraveyardOnceEachTurnAbility extends SimpleStaticAbility { public CastFromGraveyardOnceEachTurnAbility(FilterCard filter) { - super(new CastFromGraveyardOnceEffect(filter)); + this(filter, null); + } + + public CastFromGraveyardOnceEachTurnAbility(FilterCard filter, Cost additionalCost) { + super(new CastFromGraveyardOnceEffect(filter, additionalCost)); this.addWatcher(new CastFromGraveyardOnceWatcher()); this.setIdentifier(MageIdentifier.CastFromGraveyardOnceWatcher); } @@ -46,17 +53,21 @@ public class CastFromGraveyardOnceEachTurnAbility extends SimpleStaticAbility { class CastFromGraveyardOnceEffect extends AsThoughEffectImpl { private final FilterCard filter; + private final Cost additionalCost; - CastFromGraveyardOnceEffect(FilterCard filter) { + CastFromGraveyardOnceEffect(FilterCard filter, Cost additionalCost) { super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); this.filter = filter; this.staticText = "Once during each of your turns, you may cast " + filter.getMessage() - + (filter.getMessage().contains("your graveyard") ? "" : " from your graveyard"); + + (filter.getMessage().contains("your graveyard") ? "" : " from your graveyard") + + (additionalCost == null ? "" : " by " + additionalCost.getText() + " in addition to paying its other costs."); + this.additionalCost = additionalCost; } private CastFromGraveyardOnceEffect(final CastFromGraveyardOnceEffect effect) { super(effect); this.filter = effect.filter; + this.additionalCost = effect.additionalCost; } @Override @@ -97,7 +108,14 @@ class CastFromGraveyardOnceEffect extends AsThoughEffectImpl { } Set allowedToBeCastNow = spellAbility.spellCanBeActivatedNow(playerId, game); if (allowedToBeCastNow.contains(MageIdentifier.Default)) { - return filter.match(cardToCheck, playerId, source, game); + boolean matched = filter.match(cardToCheck, playerId, source, game); + if (matched && additionalCost != null) { + Costs costs = new CostsImpl<>(); + costs.add(additionalCost); + controller.setCastSourceIdWithAlternateMana(objectId, spellAbility.getManaCosts(), + costs, MageIdentifier.CastFromGraveyardOnceWatcher); + } + return matched; } } return false; -- 2.47.2 From 21defbf3240748b17a892a56799caf2d4ac4c26d Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Sat, 12 Apr 2025 12:15:05 -0500 Subject: [PATCH 10/95] [TDC] Implement Neriv, Crackling Vanguard --- .../mage/cards/n/NerivCracklingVanguard.java | 180 ++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 1 + 2 files changed, 181 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/n/NerivCracklingVanguard.java diff --git a/Mage.Sets/src/mage/cards/n/NerivCracklingVanguard.java b/Mage.Sets/src/mage/cards/n/NerivCracklingVanguard.java new file mode 100644 index 00000000000..75b317b9e1c --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NerivCracklingVanguard.java @@ -0,0 +1,180 @@ +package mage.cards.n; + +import java.util.*; +import java.util.stream.Collectors; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.cards.Card; +import mage.cards.Cards; +import mage.constants.*; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.game.Game; +import mage.game.permanent.PermanentToken; +import mage.game.permanent.token.GoblinToken; +import mage.players.Player; +import mage.target.targetpointer.FixedTargets; +import mage.util.CardUtil; +import mage.watchers.common.AttackedThisTurnWatcher; + +/** + * + * @author Jmlundeen + */ +public final class NerivCracklingVanguard extends CardImpl { + + public NerivCracklingVanguard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{W}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // When Neriv enters, create two 1/1 red Goblin creature tokens. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GoblinToken(false), 2))); + + // Whenever Neriv attacks, exile a number of cards from the top of your library equal to the number of differently named tokens you control. During any turn you attacked with a commander, you may play those cards. + this.addAbility(new AttacksTriggeredAbility(new NerivCracklingVanguardEffect())); + } + + private NerivCracklingVanguard(final NerivCracklingVanguard card) { + super(card); + } + + @Override + public NerivCracklingVanguard copy() { + return new NerivCracklingVanguard(this); + } +} + +class NerivCracklingVanguardEffect extends OneShotEffect { + + NerivCracklingVanguardEffect() { + super(Outcome.Benefit); + staticText = "exile a number of cards from the top of your library equal to the number of " + + "differently named tokens you control. During any turn you attacked with a commander, you may play those cards."; + } + + private NerivCracklingVanguardEffect(final NerivCracklingVanguardEffect effect) { + super(effect); + } + + @Override + public NerivCracklingVanguardEffect copy() { + return new NerivCracklingVanguardEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + int tokenNameCount = NerivDynamicValue.instance.calculate(game, source, this); + if (controller == null || tokenNameCount == 0) { + return false; + } + Set cards = controller.getLibrary().getTopCards(game, tokenNameCount); + if (cards.isEmpty()) { + return false; + } + controller.moveCardsToExile(cards, source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source)); + // copy ability in case Neriv leaves the battlefield. Card should be playable any turn you've attacked with a commander. + Ability copiedAbility = source.copy(); + copiedAbility.newId(); + copiedAbility.setControllerId(source.getControllerId()); + PlayFromNotOwnHandZoneTargetEffect playFromExile = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfGame); + playFromExile.setTargetPointer(new FixedTargets(cards, game)); + ConditionalAsThoughEffect playOnlyIfAttackedWithCommander = new ConditionalAsThoughEffect(playFromExile, new CommanderAttackedThisTurnCondition(copiedAbility)); + playOnlyIfAttackedWithCommander.setTargetPointer(new FixedTargets(cards, game)); + game.addEffect(playOnlyIfAttackedWithCommander, copiedAbility); + return true; + } +} + +enum NerivDynamicValue implements DynamicValue { + instance; + + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return (int) game.getBattlefield().getAllActivePermanents(sourceAbility.getControllerId()).stream() + .filter(permanent -> permanent instanceof PermanentToken) + .map(MageObject::getName) + .distinct() + .count(); + } + + @Override + public DynamicValue copy() { + return instance; + } + + @Override + public String getMessage() { + return "X"; + } +} + +class CommanderAttackedThisTurnCondition implements Condition { + + private Ability ability; + + CommanderAttackedThisTurnCondition(Ability source) { + this.ability = source; + } + + @Override + public boolean apply(Game game, Ability source) { + // in case Neriv leaves the battlefield, the ability must be referenced for controller information + if (ability == null) { + ability = source; + } + // your turn + if (!ability.isControlledBy(game.getActivePlayerId())) { + return false; + } + Player controller = game.getPlayer(ability.getControllerId()); + if (controller == null) { + return false; + } + // attacked with Commander + // note that the MOR object doesn't work well with LKI call when checking for the subtype, thus we check the LKI permanent in the battlefield + Set commanderIds = game.getPlayerList() + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(player -> game.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, true)) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + AttackedThisTurnWatcher watcher = game.getState().getWatcher(AttackedThisTurnWatcher.class); + return watcher != null && watcher.getAttackedThisTurnCreaturesPermanentLKI() + .stream() + .filter(Objects::nonNull) + .anyMatch(permanent -> commanderIds.contains(permanent.getId())); + } + + @Override + public String toString() { + return "During that turn you attacked with a Commander"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index c25cd68e19f..6ee9d2562ef 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -225,6 +225,7 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Narset's Reversal", 92, Rarity.RARE, mage.cards.n.NarsetsReversal.class)); cards.add(new SetCardInfo("Necromantic Selection", 189, Rarity.RARE, mage.cards.n.NecromanticSelection.class)); cards.add(new SetCardInfo("Necropolis Fiend", 190, Rarity.RARE, mage.cards.n.NecropolisFiend.class)); + cards.add(new SetCardInfo("Neriv, Crackling Vanguard", 6, Rarity.MYTHIC, mage.cards.n.NerivCracklingVanguard.class)); cards.add(new SetCardInfo("Nesting Dragon", 225, Rarity.RARE, mage.cards.n.NestingDragon.class)); cards.add(new SetCardInfo("Nogi, Draco-Zealot", 226, Rarity.MYTHIC, mage.cards.n.NogiDracoZealot.class)); cards.add(new SetCardInfo("Noxious Gearhulk", 191, Rarity.MYTHIC, mage.cards.n.NoxiousGearhulk.class)); -- 2.47.2 From 8ef9ed9da7796d8c1c7d6a3d63300450c8647f52 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Sat, 12 Apr 2025 13:51:43 -0500 Subject: [PATCH 11/95] [TDC] Implement Parapet Thrasher --- .../src/mage/cards/p/ParapetThrasher.java | 120 ++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 2 + 2 files changed, 122 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/ParapetThrasher.java diff --git a/Mage.Sets/src/mage/cards/p/ParapetThrasher.java b/Mage.Sets/src/mage/cards/p/ParapetThrasher.java new file mode 100644 index 00000000000..9775826826e --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ParapetThrasher.java @@ -0,0 +1,120 @@ +package mage.cards.p; + +import java.util.Objects; +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.OneOrMoreDamagePlayerTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; +import mage.abilities.hint.common.ModesAlreadyUsedHint; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; + +/** + * + * @author Jmlundeen + */ +public final class ParapetThrasher extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.DRAGON, "Dragons you control"); + private static final FilterPermanent artifactFilter = new FilterArtifactPermanent("artifact that opponent controls"); + + public ParapetThrasher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever one or more Dragons you control deal combat damage to an opponent, choose one that hasn't been chosen this turn -- + // * Destroy target artifact that opponent controls. + Ability ability = new OneOrMoreDamagePlayerTriggeredAbility(new DestroyTargetEffect(), filter, true, true) + .setTriggerPhrase("Whenever one or more Dragons you control deal combat damage to an opponent, "); + ability.addTarget(new TargetPermanent(artifactFilter)); + ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setModeTag("destroy artifact"); + ability.getModes().setLimitUsageByOnce(true); + + // * This creature deals 4 damage to each other opponent. + Mode mode = new Mode(new ParapetThrasherDamageEffect()); + mode.setModeTag("damage opponents"); + ability.addMode(mode); + + // * Exile the top card of your library. You may play it this turn. + Effect effect = new ExileTopXMayPlayUntilEffect(1, Duration.EndOfTurn); + effect.setText(effect.getText(null).replace("that card", "it")); + Mode mode2 = new Mode(effect); + mode2.setModeTag("exile top card"); + ability.addMode(mode2); + ability.addHint(ModesAlreadyUsedHint.instance); + this.addAbility(ability); + } + + private ParapetThrasher(final ParapetThrasher card) { + super(card); + } + + @Override + public ParapetThrasher copy() { + return new ParapetThrasher(this); + } +} + +class ParapetThrasherDamageEffect extends OneShotEffect { + + ParapetThrasherDamageEffect() { + super(Outcome.Benefit); + this.staticText = "This creature deals 4 damage to each other opponent"; + } + + private ParapetThrasherDamageEffect(final ParapetThrasherDamageEffect effect) { + super(effect); + } + + @Override + public ParapetThrasherDamageEffect copy() { + return new ParapetThrasherDamageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + UUID damagedOpponent = this.getTargetPointer().getFirst(game, source); + MageObject object = game.getObject(source); + if (object != null && damagedOpponent != null) { + for (UUID playerId : game.getOpponents(source.getControllerId())) { + if (!Objects.equals(playerId, damagedOpponent)) { + Player opponent = game.getPlayer(playerId); + if (opponent != null) { + int dealtDamage = opponent.damage(4, source.getSourceId(), source, game); + game.informPlayers(object.getLogName() + " deals " + dealtDamage + " damage to " + opponent.getLogName()); + } + } + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index 6ee9d2562ef..dbac43a3d2a 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -239,6 +239,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Orzhov Signet", 323, Rarity.COMMON, mage.cards.o.OrzhovSignet.class)); cards.add(new SetCardInfo("Overgrown Battlement", 264, Rarity.UNCOMMON, mage.cards.o.OvergrownBattlement.class)); cards.add(new SetCardInfo("Overgrown Farmland", 381, Rarity.RARE, mage.cards.o.OvergrownFarmland.class)); + cards.add(new SetCardInfo("Parapet Thrasher", 36, Rarity.RARE, mage.cards.p.ParapetThrasher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Parapet Thrasher", 76, Rarity.RARE, mage.cards.p.ParapetThrasher.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Path of Ancestry", 382, Rarity.COMMON, mage.cards.p.PathOfAncestry.class)); cards.add(new SetCardInfo("Perilous Landscape", 383, Rarity.COMMON, mage.cards.p.PerilousLandscape.class)); cards.add(new SetCardInfo("Phyrexian Reclamation", 194, Rarity.UNCOMMON, mage.cards.p.PhyrexianReclamation.class)); -- 2.47.2 From 4d38f6b942b2b31a5282553070630c71c5e44151 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Sat, 12 Apr 2025 14:59:25 -0500 Subject: [PATCH 12/95] Fix Riptide Gearhulk not bouncing all targets Reported on Discord, PutIntoLibraryNFromTopTargetEffect was only processing the first target. The effect now supports multiple targets. --- .../src/mage/cards/r/RiptideGearhulk.java | 1 + .../PutIntoLibraryNFromTopTargetEffect.java | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/r/RiptideGearhulk.java b/Mage.Sets/src/mage/cards/r/RiptideGearhulk.java index cdcf4c2a575..a1d5f7a4a44 100644 --- a/Mage.Sets/src/mage/cards/r/RiptideGearhulk.java +++ b/Mage.Sets/src/mage/cards/r/RiptideGearhulk.java @@ -21,6 +21,7 @@ import mage.target.targetpointer.EachTargetPointer; * @author Jmlundeen */ public final class RiptideGearhulk extends CardImpl { + public RiptideGearhulk(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{W}{W}{U}{U}"); diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutIntoLibraryNFromTopTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutIntoLibraryNFromTopTargetEffect.java index bc4b90ec513..c961fb3cf11 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutIntoLibraryNFromTopTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutIntoLibraryNFromTopTargetEffect.java @@ -9,6 +9,8 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.CardUtil; +import java.util.UUID; + /** * @author TheElk801 */ @@ -33,10 +35,19 @@ public class PutIntoLibraryNFromTopTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - return player != null && permanent != null - && player.putCardOnTopXOfLibrary(permanent, game, source, position, true); + boolean result = false; + for (UUID permanentId : getTargetPointer().getTargets(game, source)) { + Permanent permanent = game.getPermanent(permanentId); + if (permanent == null) { + continue; + } + Player player = game.getPlayer(permanent.getOwnerId()); + if (player == null) { + continue; + } + result |= player.putCardOnTopXOfLibrary(permanent, game, source, position, true); + } + return result; } @Override -- 2.47.2 From d5eee8e46de514ffb2237f27a08e6d01e4b95180 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Sat, 12 Apr 2025 16:31:49 -0500 Subject: [PATCH 13/95] [TDC] Implement Protector of the Wastes --- .../mage/cards/p/ProtectorOfTheWastes.java | 139 ++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 2 + 2 files changed, 141 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/ProtectorOfTheWastes.java diff --git a/Mage.Sets/src/mage/cards/p/ProtectorOfTheWastes.java b/Mage.Sets/src/mage/cards/p/ProtectorOfTheWastes.java new file mode 100644 index 00000000000..4ab7cb7b658 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProtectorOfTheWastes.java @@ -0,0 +1,139 @@ +package mage.cards.p; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.keyword.MonstrosityAbility; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +/** + * + * @author Jmlundeen + */ +public final class ProtectorOfTheWastes extends CardImpl { + + public ProtectorOfTheWastes(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}"); + + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When this creature enters or becomes monstrous, exile up to two target artifacts and/or enchantments controlled by different players. + Ability ability = new ProtectorOfTheWastesTriggeredAbility(); + ability.addTarget(new ProtectorOfTheWastesTarget()); + this.addAbility(ability); + + // {4}{W}: Monstrosity 3. + this.addAbility(new MonstrosityAbility("{4}{W}", 3)); + } + + private ProtectorOfTheWastes(final ProtectorOfTheWastes card) { + super(card); + } + + @Override + public ProtectorOfTheWastes copy() { + return new ProtectorOfTheWastes(this); + } +} + +class ProtectorOfTheWastesTarget extends TargetPermanent { + + private static final FilterArtifactOrEnchantmentPermanent filter = new FilterArtifactOrEnchantmentPermanent( + "artifacts and/or enchantments controlled by different players"); + + ProtectorOfTheWastesTarget() { + super(0, 2, filter); + } + + private ProtectorOfTheWastesTarget(ProtectorOfTheWastesTarget target) { + super(target); + } + + @Override + public ProtectorOfTheWastesTarget copy() { + return new ProtectorOfTheWastesTarget(this); + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + if (!super.canTarget(id, source, game)) { + return false; + } + Permanent permanent = game.getPermanent(id); + if (permanent == null) { + return false; + } + return this.getTargets().stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .noneMatch(perm -> !perm.getId().equals(permanent.getId()) + && perm.isControlledBy(permanent.getControllerId())); + } + + @Override + public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { + Set possibleTargets = new HashSet<>(); + MageObject targetSource = game.getObject(source); + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) { + boolean validTarget = this.getTargets().stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .noneMatch(perm -> !perm.getId().equals(permanent.getId()) && perm.isControlledBy(permanent.getControllerId())); + if (validTarget) { + if (notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, source, game)) { + possibleTargets.add(permanent.getId()); + } + } + } + return possibleTargets; + } +} + +class ProtectorOfTheWastesTriggeredAbility extends TriggeredAbilityImpl { + + public ProtectorOfTheWastesTriggeredAbility() { + super(Zone.ALL, new ExileTargetEffect()); + setTriggerPhrase("When {this} enters or becomes monstrous, "); + } + + private ProtectorOfTheWastesTriggeredAbility(final ProtectorOfTheWastesTriggeredAbility ability) { + super(ability); + } + + @Override + public ProtectorOfTheWastesTriggeredAbility copy() { + return new ProtectorOfTheWastesTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.BECOMES_MONSTROUS + || event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getSourceId().equals(this.getSourceId()); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index dbac43a3d2a..d407bcb0b10 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -249,6 +249,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Prairie Stream", 384, Rarity.RARE, mage.cards.p.PrairieStream.class)); cards.add(new SetCardInfo("Preordain", 161, Rarity.COMMON, mage.cards.p.Preordain.class)); cards.add(new SetCardInfo("Prismari Command", 299, Rarity.RARE, mage.cards.p.PrismariCommand.class)); + cards.add(new SetCardInfo("Protector of the Wastes", 14, Rarity.RARE, mage.cards.p.ProtectorOfTheWastes.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Protector of the Wastes", 54, Rarity.RARE, mage.cards.p.ProtectorOfTheWastes.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Putrefy", 300, Rarity.UNCOMMON, mage.cards.p.Putrefy.class)); cards.add(new SetCardInfo("Radiant Grove", 385, Rarity.COMMON, mage.cards.r.RadiantGrove.class)); cards.add(new SetCardInfo("Rampant Growth", 265, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); -- 2.47.2 From cf0ceb8a44bdd206bfff8e96f8800b4e7b3fc5a4 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sun, 13 Apr 2025 23:32:18 +0400 Subject: [PATCH 14/95] images: fixed scryfall images download after last website update (#13479); --- .../card/dl/sources/ScryfallApiCard.java | 19 ++++++++++++++++++- .../card/dl/sources/ScryfallImageSource.java | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallApiCard.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallApiCard.java index 4ea496665d0..b07e82734eb 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallApiCard.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallApiCard.java @@ -90,7 +90,24 @@ public class ScryfallApiCard { // - scryfall: Command Tower // Command Tower // - xmage: Command Tower (second side as diff card and direct link image), example: https://scryfall.com/card/rex/26/command-tower-command-tower if (this.layout.equals("reversible_card")) { - if (this.card_faces.get(0).layout == null || this.card_faces.get(0).layout.equals("normal")) { + if (false) { + // ignore + } else if (this.card_faces == null) { + // TODO: temporary fix, delete after scryfall site update + // broken adventure/omen card (scryfall changed it for some reason) + // Scavenger Regent // Exude Toxin + // https://scryfall.com/card/tdm/90/scavenger-regent-exude-toxin + if (this.name.contains("//")) { + throw new IllegalArgumentException("Scryfall: unsupported data type, broken reversible_card must have same simple name" + + this.set + " - " + this.collector_number + " - " + this.name); + } + } else if (this.card_faces.get(0).layout.equals("reversible_card")) { + // TODO: temporary fix, delete after scryfall site update + // broken adventure/omen card (scryfall changed it for some reason) + // Bloomvine Regent // Claim Territory + // https://scryfall.com/card/tdm/381/bloomvine-regent-claim-territory-bloomvine-regent + this.name = this.card_faces.get(0).name; + } else if (this.card_faces.get(0).layout == null || this.card_faces.get(0).layout.equals("normal")) { // simple card // Command Tower // Command Tower // https://scryfall.com/card/rex/26/command-tower-command-tower diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java index 62a53f5a097..8afcbdf4205 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java @@ -504,7 +504,7 @@ public class ScryfallImageSource implements CardImageSource { jsonReader.close(); return bulkCardsDatabaseAll.size() > 0; } catch (Exception e) { - logger.error("Can't read bulk file (possible reason: broken scryfall format), details: " + e); + logger.error("Can't read bulk file (possible reason: broken scryfall format), details: " + e, e); try { // clean up if (!SCRYFALL_BULK_FILES_DEBUG_READ_ONLY_MODE) { -- 2.47.2 From 552852bd86b586a09b5c1b5c72ee70a898016d4d Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 13 Apr 2025 17:53:35 -0400 Subject: [PATCH 15/95] [TDM] Implement Dalkovan Encampment --- .../src/mage/cards/d/DalkovanEncampment.java | 99 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + 2 files changed, 100 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DalkovanEncampment.java diff --git a/Mage.Sets/src/mage/cards/d/DalkovanEncampment.java b/Mage.Sets/src/mage/cards/d/DalkovanEncampment.java new file mode 100644 index 00000000000..5126582d86d --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DalkovanEncampment.java @@ -0,0 +1,99 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedUnlessAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.common.delayed.WhenYouAttackDelayedTriggeredAbility; +import mage.abilities.condition.common.YouControlPermanentCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.token.RedWarriorToken; +import mage.game.permanent.token.Token; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DalkovanEncampment extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("a Swamp or a Mountain"); + + static { + filter.add(Predicates.or( + SubType.SWAMP.getPredicate(), + SubType.MOUNTAIN.getPredicate() + )); + } + + private static final YouControlPermanentCondition condition = new YouControlPermanentCondition(filter); + + public DalkovanEncampment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // This land enters tapped unless you control a Swamp or a Mountain. + this.addAbility(new EntersBattlefieldTappedUnlessAbility(condition).addHint(condition.getHint())); + + // {T}: Add {W}. + this.addAbility(new WhiteManaAbility()); + + // {2}{W}, {T}: Whenever you attack this turn, create two 1/1 red Warrior creature tokens that are tapped and attacking. Sacrifice them at the beginning of the next end step. + Ability ability = new SimpleActivatedAbility(new CreateDelayedTriggeredAbilityEffect( + new WhenYouAttackDelayedTriggeredAbility(new DalkovanEncampmentEffect()) + ), new ManaCostsImpl<>("{2}{W}")); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private DalkovanEncampment(final DalkovanEncampment card) { + super(card); + } + + @Override + public DalkovanEncampment copy() { + return new DalkovanEncampment(this); + } +} + +class DalkovanEncampmentEffect extends OneShotEffect { + + DalkovanEncampmentEffect() { + super(Outcome.Benefit); + staticText = "create two 1/1 red Warrior creature tokens that are tapped and attacking. " + + "Sacrifice them at the beginning of the next end step"; + } + + private DalkovanEncampmentEffect(final DalkovanEncampmentEffect effect) { + super(effect); + } + + @Override + public DalkovanEncampmentEffect copy() { + return new DalkovanEncampmentEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new RedWarriorToken(); + token.putOntoBattlefield(2, game, source, source.getControllerId(), true, true); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new SacrificeTargetEffect("sacrifice those tokens") + .setTargetPointer(new FixedTargets(token, game)) + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index f7de48bf98c..6a7085c8ffc 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -71,6 +71,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Corroding Dragonstorm", 75, Rarity.UNCOMMON, mage.cards.c.CorrodingDragonstorm.class)); cards.add(new SetCardInfo("Craterhoof Behemoth", 138, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class)); cards.add(new SetCardInfo("Cruel Truths", 76, Rarity.COMMON, mage.cards.c.CruelTruths.class)); + cards.add(new SetCardInfo("Dalkovan Encampment", 253, Rarity.RARE, mage.cards.d.DalkovanEncampment.class)); cards.add(new SetCardInfo("Dalkovan Packbeasts", 7, Rarity.UNCOMMON, mage.cards.d.DalkovanPackbeasts.class)); cards.add(new SetCardInfo("Death Begets Life", 176, Rarity.MYTHIC, mage.cards.d.DeathBegetsLife.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Death Begets Life", 354, Rarity.MYTHIC, mage.cards.d.DeathBegetsLife.class, NON_FULL_USE_VARIOUS)); -- 2.47.2 From e54210ad2ef0aea6b365e70690f6d98c38bc66db Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 13 Apr 2025 18:00:14 -0400 Subject: [PATCH 16/95] [TDM] Implement Desperate Measures --- .../src/mage/cards/d/DesperateMeasures.java | 41 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + ...WhenTargetDiesDelayedTriggeredAbility.java | 12 +++++- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/d/DesperateMeasures.java diff --git a/Mage.Sets/src/mage/cards/d/DesperateMeasures.java b/Mage.Sets/src/mage/cards/d/DesperateMeasures.java new file mode 100644 index 00000000000..446b737329c --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DesperateMeasures.java @@ -0,0 +1,41 @@ +package mage.cards.d; + +import mage.abilities.common.delayed.WhenTargetDiesDelayedTriggeredAbility; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SetTargetPointer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DesperateMeasures extends CardImpl { + + public DesperateMeasures(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); + + // Target creature gets +1/-1 until end of turn. When it dies under your control this turn, draw two cards. + this.getSpellAbility().addEffect(new BoostTargetEffect(1, -1)); + this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect( + new WhenTargetDiesDelayedTriggeredAbility( + new DrawCardSourceControllerEffect(2), Duration.EndOfTurn, + SetTargetPointer.NONE, true + ), true + )); + } + + private DesperateMeasures(final DesperateMeasures card) { + super(card); + } + + @Override + public DesperateMeasures copy() { + return new DesperateMeasures(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 6a7085c8ffc..ac12291e318 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -80,6 +80,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Defibrillating Current", 177, Rarity.UNCOMMON, mage.cards.d.DefibrillatingCurrent.class)); 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("Desperate Measures", 78, Rarity.UNCOMMON, mage.cards.d.DesperateMeasures.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)); diff --git a/Mage/src/main/java/mage/abilities/common/delayed/WhenTargetDiesDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/WhenTargetDiesDelayedTriggeredAbility.java index 9fb75825cde..1e0b599c360 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/WhenTargetDiesDelayedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/WhenTargetDiesDelayedTriggeredAbility.java @@ -18,6 +18,7 @@ public class WhenTargetDiesDelayedTriggeredAbility extends DelayedTriggeredAbili protected MageObjectReference mor; private final SetTargetPointer setTargetPointer; + private final boolean onlyControlled; public WhenTargetDiesDelayedTriggeredAbility(Effect effect) { this(effect, SetTargetPointer.NONE); @@ -28,15 +29,21 @@ public class WhenTargetDiesDelayedTriggeredAbility extends DelayedTriggeredAbili } public WhenTargetDiesDelayedTriggeredAbility(Effect effect, Duration duration, SetTargetPointer setTargetPointer) { + this(effect, duration, setTargetPointer, false); + } + + public WhenTargetDiesDelayedTriggeredAbility(Effect effect, Duration duration, SetTargetPointer setTargetPointer, boolean onlyControlled) { super(effect, duration, true); this.setTargetPointer = setTargetPointer; - setTriggerPhrase("When that creature dies" + (duration == Duration.EndOfTurn ? " this turn, " : ", ")); + this.onlyControlled = onlyControlled; + setTriggerPhrase("When that creature dies" + (onlyControlled ? " under your control" : "") + (duration == Duration.EndOfTurn ? " this turn, " : ", ")); } protected WhenTargetDiesDelayedTriggeredAbility(final WhenTargetDiesDelayedTriggeredAbility ability) { super(ability); this.mor = ability.mor; this.setTargetPointer = ability.setTargetPointer; + this.onlyControlled = ability.onlyControlled; } @Override @@ -62,7 +69,8 @@ public class WhenTargetDiesDelayedTriggeredAbility extends DelayedTriggeredAbili return false; } Permanent permanent = zEvent.getTarget(); - if (mor == null || !mor.refersTo(permanent, game)) { + if (mor == null || !mor.refersTo(permanent, game) + || onlyControlled && !permanent.isControlledBy(getControllerId())) { return false; } switch (setTargetPointer) { -- 2.47.2 From af512ce3f4bdb35f97dc45c305fd0c6429d22a90 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 13 Apr 2025 18:09:01 -0400 Subject: [PATCH 17/95] [TDM] Implement Rite of Renewal --- Mage.Sets/src/mage/cards/r/RiteOfRenewal.java | 43 +++++++++++++++++++ .../src/mage/sets/TarkirDragonstorm.java | 1 + ...TargetPlayerShufflesTargetCardsEffect.java | 14 ++++-- .../TargetCardInTargetPlayersGraveyard.java | 10 ++++- 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/r/RiteOfRenewal.java diff --git a/Mage.Sets/src/mage/cards/r/RiteOfRenewal.java b/Mage.Sets/src/mage/cards/r/RiteOfRenewal.java new file mode 100644 index 00000000000..cfc8ade8917 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RiteOfRenewal.java @@ -0,0 +1,43 @@ +package mage.cards.r; + +import mage.abilities.effects.common.ExileSpellEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.TargetPlayerShufflesTargetCardsEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInTargetPlayersGraveyard; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RiteOfRenewal extends CardImpl { + + public RiteOfRenewal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); + + // Return up to two target permanent cards from your graveyard to your hand. Target player shuffles up to four target cards from their graveyard into their library. Exile Rite of Renewal. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + 0, 2, StaticFilters.FILTER_CARD_PERMANENTS + )); + this.getSpellAbility().addEffect(new TargetPlayerShufflesTargetCardsEffect(1)); + this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addTarget(new TargetCardInTargetPlayersGraveyard(3, 1)); + this.getSpellAbility().addEffect(new ExileSpellEffect()); + } + + private RiteOfRenewal(final RiteOfRenewal card) { + super(card); + } + + @Override + public RiteOfRenewal copy() { + return new RiteOfRenewal(this); + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index ac12291e318..e710745ba5f 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -212,6 +212,7 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Revival of the Ancestors", 218, Rarity.RARE, mage.cards.r.RevivalOfTheAncestors.class)); cards.add(new SetCardInfo("Riling Dawnbreaker", 21, Rarity.COMMON, mage.cards.r.RilingDawnbreaker.class)); cards.add(new SetCardInfo("Ringing Strike Mastery", 53, Rarity.COMMON, mage.cards.r.RingingStrikeMastery.class)); + cards.add(new SetCardInfo("Rite of Renewal", 153, Rarity.UNCOMMON, mage.cards.r.RiteOfRenewal.class)); cards.add(new SetCardInfo("Riverwalk Technique", 54, Rarity.COMMON, mage.cards.r.RiverwalkTechnique.class)); cards.add(new SetCardInfo("Riverwheel Sweep", 219, Rarity.UNCOMMON, mage.cards.r.RiverwheelSweep.class)); cards.add(new SetCardInfo("Roamer's Routine", 154, Rarity.COMMON, mage.cards.r.RoamersRoutine.class)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/TargetPlayerShufflesTargetCardsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TargetPlayerShufflesTargetCardsEffect.java index fa1c742c297..a0e055a7a15 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/TargetPlayerShufflesTargetCardsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/TargetPlayerShufflesTargetCardsEffect.java @@ -15,12 +15,20 @@ import mage.util.CardUtil; */ public class TargetPlayerShufflesTargetCardsEffect extends OneShotEffect { + private final int targetPlayerIndex; + public TargetPlayerShufflesTargetCardsEffect() { + this(0); + } + + public TargetPlayerShufflesTargetCardsEffect(int targetPlayerIndex) { super(Outcome.Neutral); + this.targetPlayerIndex = targetPlayerIndex; } private TargetPlayerShufflesTargetCardsEffect(final TargetPlayerShufflesTargetCardsEffect effect) { super(effect); + this.targetPlayerIndex = effect.targetPlayerIndex; } @Override @@ -30,8 +38,8 @@ public class TargetPlayerShufflesTargetCardsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - Cards cards = new CardsImpl(source.getTargets().get(1).getTargets()); + Player targetPlayer = game.getPlayer(source.getTargets().get(targetPlayerIndex).getFirstTarget()); + Cards cards = new CardsImpl(source.getTargets().get(targetPlayerIndex + 1).getTargets()); if (targetPlayer != null && !cards.isEmpty()) { return targetPlayer.shuffleCardsToLibrary(cards, game, source); } @@ -44,7 +52,7 @@ public class TargetPlayerShufflesTargetCardsEffect extends OneShotEffect { return staticText; } String rule = "target player shuffles "; - int targetNumber = mode.getTargets().get(1).getMaxNumberOfTargets(); + int targetNumber = mode.getTargets().get(targetPlayerIndex + 1).getMaxNumberOfTargets(); if (targetNumber == Integer.MAX_VALUE) { rule += "any number of target cards"; } else { diff --git a/Mage/src/main/java/mage/target/common/TargetCardInTargetPlayersGraveyard.java b/Mage/src/main/java/mage/target/common/TargetCardInTargetPlayersGraveyard.java index 419fcd44722..90422f1b407 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardInTargetPlayersGraveyard.java +++ b/Mage/src/main/java/mage/target/common/TargetCardInTargetPlayersGraveyard.java @@ -12,12 +12,20 @@ import java.util.UUID; */ public class TargetCardInTargetPlayersGraveyard extends TargetCardInGraveyard { + private final int targetPlayerIndex; + public TargetCardInTargetPlayersGraveyard(int targets) { + this(targets, 0); + } + + public TargetCardInTargetPlayersGraveyard(int targets, int targetPlayerIndex) { super(0, targets, StaticFilters.FILTER_CARD); + this.targetPlayerIndex = targetPlayerIndex; } private TargetCardInTargetPlayersGraveyard(final TargetCardInTargetPlayersGraveyard target) { super(target); + this.targetPlayerIndex = target.targetPlayerIndex; } @Override @@ -26,7 +34,7 @@ public class TargetCardInTargetPlayersGraveyard extends TargetCardInGraveyard { return false; } Card card = game.getCard(id); - return card != null && card.isOwnedBy(source.getFirstTarget()); + return card != null && card.isOwnedBy(source.getTargets().get(targetPlayerIndex).getFirstTarget()); } @Override -- 2.47.2 From 5e180c5c05d169a703dc40a87f9d0cf665c0d2c7 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 13 Apr 2025 18:09:51 -0400 Subject: [PATCH 18/95] [TDM] add all variants --- .../src/mage/sets/TarkirDragonstorm.java | 326 +++++++++++++----- 1 file changed, 232 insertions(+), 94 deletions(-) diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index e710745ba5f..4121a149f73 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -38,15 +38,21 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("All-Out Assault", 352, Rarity.MYTHIC, mage.cards.a.AllOutAssault.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("All-Out Assault", 405, Rarity.MYTHIC, mage.cards.a.AllOutAssault.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("All-Out Assault", 415, Rarity.MYTHIC, mage.cards.a.AllOutAssault.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Ambling Stormshell", 37, Rarity.RARE, mage.cards.a.AmblingStormshell.class)); - cards.add(new SetCardInfo("Anafenza, Unyielding Lineage", 2, Rarity.RARE, mage.cards.a.AnafenzaUnyieldingLineage.class)); + cards.add(new SetCardInfo("Ambling Stormshell", 332, Rarity.RARE, mage.cards.a.AmblingStormshell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ambling Stormshell", 37, Rarity.RARE, mage.cards.a.AmblingStormshell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Anafenza, Unyielding Lineage", 2, Rarity.RARE, mage.cards.a.AnafenzaUnyieldingLineage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Anafenza, Unyielding Lineage", 327, Rarity.RARE, mage.cards.a.AnafenzaUnyieldingLineage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arashin Sunshield", 3, Rarity.COMMON, mage.cards.a.ArashinSunshield.class)); - cards.add(new SetCardInfo("Armament Dragon", 168, Rarity.UNCOMMON, mage.cards.a.ArmamentDragon.class)); + cards.add(new SetCardInfo("Armament Dragon", 168, Rarity.UNCOMMON, mage.cards.a.ArmamentDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Armament Dragon", 307, Rarity.UNCOMMON, mage.cards.a.ArmamentDragon.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Attuned Hunter", 135, Rarity.UNCOMMON, mage.cards.a.AttunedHunter.class)); cards.add(new SetCardInfo("Auroral Procession", 169, Rarity.UNCOMMON, mage.cards.a.AuroralProcession.class)); - cards.add(new SetCardInfo("Avenger of the Fallen", 73, Rarity.RARE, mage.cards.a.AvengerOfTheFallen.class)); - cards.add(new SetCardInfo("Awaken the Honored Dead", 170, Rarity.RARE, mage.cards.a.AwakenTheHonoredDead.class)); - cards.add(new SetCardInfo("Barrensteppe Siege", 171, Rarity.RARE, mage.cards.b.BarrensteppeSiege.class)); + cards.add(new SetCardInfo("Avenger of the Fallen", 337, Rarity.RARE, mage.cards.a.AvengerOfTheFallen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Avenger of the Fallen", 73, Rarity.RARE, mage.cards.a.AvengerOfTheFallen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Awaken the Honored Dead", 170, Rarity.RARE, mage.cards.a.AwakenTheHonoredDead.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Awaken the Honored Dead", 383, Rarity.RARE, mage.cards.a.AwakenTheHonoredDead.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Barrensteppe Siege", 171, Rarity.RARE, mage.cards.b.BarrensteppeSiege.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Barrensteppe Siege", 384, Rarity.RARE, mage.cards.b.BarrensteppeSiege.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bearer of Glory", 4, Rarity.COMMON, mage.cards.b.BearerOfGlory.class)); cards.add(new SetCardInfo("Betor, Kin to All", 172, Rarity.MYTHIC, mage.cards.b.BetorKinToAll.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Betor, Kin to All", 308, Rarity.MYTHIC, mage.cards.b.BetorKinToAll.class, NON_FULL_USE_VARIOUS)); @@ -54,24 +60,40 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Bewildering Blizzard", 38, Rarity.UNCOMMON, mage.cards.b.BewilderingBlizzard.class)); cards.add(new SetCardInfo("Bloodfell Caves", 250, Rarity.COMMON, mage.cards.b.BloodfellCaves.class)); cards.add(new SetCardInfo("Bloomvine Regent", 136, Rarity.RARE, mage.cards.b.BloomvineRegent.class)); + cards.add(new SetCardInfo("Bloomvine Regent", 381, Rarity.RARE, mage.cards.b.BloomvineRegent.class)); cards.add(new SetCardInfo("Blossoming Sands", 251, Rarity.COMMON, mage.cards.b.BlossomingSands.class)); cards.add(new SetCardInfo("Bone-Cairn Butcher", 173, Rarity.UNCOMMON, mage.cards.b.BoneCairnButcher.class)); - cards.add(new SetCardInfo("Boulderborn Dragon", 239, Rarity.COMMON, mage.cards.b.BoulderbornDragon.class)); - cards.add(new SetCardInfo("Breaching Dragonstorm", 101, Rarity.UNCOMMON, mage.cards.b.BreachingDragonstorm.class)); - cards.add(new SetCardInfo("Call the Spirit Dragons", 174, Rarity.MYTHIC, mage.cards.c.CallTheSpiritDragons.class)); + cards.add(new SetCardInfo("Boulderborn Dragon", 239, Rarity.COMMON, mage.cards.b.BoulderbornDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Boulderborn Dragon", 323, Rarity.COMMON, mage.cards.b.BoulderbornDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Breaching Dragonstorm", 101, Rarity.UNCOMMON, mage.cards.b.BreachingDragonstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Breaching Dragonstorm", 299, Rarity.UNCOMMON, mage.cards.b.BreachingDragonstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Call the Spirit Dragons", 174, Rarity.MYTHIC, mage.cards.c.CallTheSpiritDragons.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Call the Spirit Dragons", 309, Rarity.MYTHIC, mage.cards.c.CallTheSpiritDragons.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Caustic Exhale", 74, Rarity.COMMON, mage.cards.c.CausticExhale.class)); cards.add(new SetCardInfo("Champion of Dusan", 137, Rarity.COMMON, mage.cards.c.ChampionOfDusan.class)); - cards.add(new SetCardInfo("Channeled Dragonfire", 102, Rarity.UNCOMMON, mage.cards.c.ChanneledDragonfire.class)); - cards.add(new SetCardInfo("Clarion Conqueror", 5, Rarity.RARE, mage.cards.c.ClarionConqueror.class)); + cards.add(new SetCardInfo("Channeled Dragonfire", 102, Rarity.UNCOMMON, mage.cards.c.ChanneledDragonfire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Channeled Dragonfire", 423, Rarity.UNCOMMON, mage.cards.c.ChanneledDragonfire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Clarion Conqueror", "377b", Rarity.RARE, mage.cards.c.ClarionConqueror.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Clarion Conqueror", 377, Rarity.RARE, mage.cards.c.ClarionConqueror.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Clarion Conqueror", 400, Rarity.MYTHIC, mage.cards.c.ClarionConqueror.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Clarion Conqueror", 410, Rarity.MYTHIC, mage.cards.c.ClarionConqueror.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Clarion Conqueror", 5, Rarity.RARE, mage.cards.c.ClarionConqueror.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Constrictor Sage", 39, Rarity.UNCOMMON, mage.cards.c.ConstrictorSage.class)); cards.add(new SetCardInfo("Coordinated Maneuver", 6, Rarity.COMMON, mage.cards.c.CoordinatedManeuver.class)); - cards.add(new SetCardInfo("Cori Mountain Monastery", 252, Rarity.RARE, mage.cards.c.CoriMountainMonastery.class)); + cards.add(new SetCardInfo("Cori Mountain Monastery", 252, Rarity.RARE, mage.cards.c.CoriMountainMonastery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cori Mountain Monastery", 393, Rarity.RARE, mage.cards.c.CoriMountainMonastery.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cori Mountain Stalwart", 175, Rarity.UNCOMMON, mage.cards.c.CoriMountainStalwart.class)); - cards.add(new SetCardInfo("Cori-Steel Cutter", 103, Rarity.RARE, mage.cards.c.CoriSteelCutter.class)); - cards.add(new SetCardInfo("Corroding Dragonstorm", 75, Rarity.UNCOMMON, mage.cards.c.CorrodingDragonstorm.class)); - cards.add(new SetCardInfo("Craterhoof Behemoth", 138, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class)); + cards.add(new SetCardInfo("Cori-Steel Cutter", 103, Rarity.RARE, mage.cards.c.CoriSteelCutter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cori-Steel Cutter", 343, Rarity.RARE, mage.cards.c.CoriSteelCutter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Corroding Dragonstorm", 297, Rarity.UNCOMMON, mage.cards.c.CorrodingDragonstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Corroding Dragonstorm", 75, Rarity.UNCOMMON, mage.cards.c.CorrodingDragonstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Craterhoof Behemoth", 138, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Craterhoof Behemoth", 346, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Craterhoof Behemoth", 404, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Craterhoof Behemoth", 414, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cruel Truths", 76, Rarity.COMMON, mage.cards.c.CruelTruths.class)); - cards.add(new SetCardInfo("Dalkovan Encampment", 253, Rarity.RARE, mage.cards.d.DalkovanEncampment.class)); + cards.add(new SetCardInfo("Dalkovan Encampment", 253, Rarity.RARE, mage.cards.d.DalkovanEncampment.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dalkovan Encampment", 394, Rarity.RARE, mage.cards.d.DalkovanEncampment.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dalkovan Packbeasts", 7, Rarity.UNCOMMON, mage.cards.d.DalkovanPackbeasts.class)); cards.add(new SetCardInfo("Death Begets Life", 176, Rarity.MYTHIC, mage.cards.d.DeathBegetsLife.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Death Begets Life", 354, Rarity.MYTHIC, mage.cards.d.DeathBegetsLife.class, NON_FULL_USE_VARIOUS)); @@ -82,40 +104,60 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Descendant of Storms", 8, Rarity.UNCOMMON, mage.cards.d.DescendantOfStorms.class)); cards.add(new SetCardInfo("Desperate Measures", 78, Rarity.UNCOMMON, mage.cards.d.DesperateMeasures.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("Dirgur Island Dragon", 294, Rarity.COMMON, mage.cards.d.DirgurIslandDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dirgur Island Dragon", 40, Rarity.COMMON, mage.cards.d.DirgurIslandDragon.class, NON_FULL_USE_VARIOUS)); 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("Disruptive Stormbrood", 178, Rarity.UNCOMMON, mage.cards.d.DisruptiveStormbrood.class)); - cards.add(new SetCardInfo("Dracogenesis", 105, Rarity.MYTHIC, mage.cards.d.Dracogenesis.class)); + cards.add(new SetCardInfo("Disruptive Stormbrood", 178, Rarity.UNCOMMON, mage.cards.d.DisruptiveStormbrood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Disruptive Stormbrood", 310, Rarity.UNCOMMON, mage.cards.d.DisruptiveStormbrood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dracogenesis", 105, Rarity.MYTHIC, mage.cards.d.Dracogenesis.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dracogenesis", 300, Rarity.MYTHIC, mage.cards.d.Dracogenesis.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dracogenesis", 402, Rarity.MYTHIC, mage.cards.d.Dracogenesis.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dracogenesis", 412, Rarity.MYTHIC, mage.cards.d.Dracogenesis.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dragon Sniper", 139, Rarity.UNCOMMON, mage.cards.d.DragonSniper.class)); cards.add(new SetCardInfo("Dragon's Prey", 79, Rarity.COMMON, mage.cards.d.DragonsPrey.class)); - cards.add(new SetCardInfo("Dragonback Assault", 179, Rarity.MYTHIC, mage.cards.d.DragonbackAssault.class)); + cards.add(new SetCardInfo("Dragonback Assault", 179, Rarity.MYTHIC, mage.cards.d.DragonbackAssault.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonback Assault", 355, Rarity.MYTHIC, mage.cards.d.DragonbackAssault.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dragonback Lancer", 9, Rarity.COMMON, mage.cards.d.DragonbackLancer.class)); cards.add(new SetCardInfo("Dragonbroods' Relic", 140, Rarity.UNCOMMON, mage.cards.d.DragonbroodsRelic.class)); cards.add(new SetCardInfo("Dragonclaw Strike", 180, Rarity.UNCOMMON, mage.cards.d.DragonclawStrike.class)); cards.add(new SetCardInfo("Dragonfire Blade", 240, Rarity.RARE, mage.cards.d.DragonfireBlade.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dragonfire Blade", 324, Rarity.RARE, mage.cards.d.DragonfireBlade.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Dragonologist", 42, Rarity.RARE, mage.cards.d.Dragonologist.class)); + cards.add(new SetCardInfo("Dragonologist", 295, Rarity.RARE, mage.cards.d.Dragonologist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonologist", 42, Rarity.RARE, mage.cards.d.Dragonologist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dragonstorm Forecaster", 43, Rarity.UNCOMMON, mage.cards.d.DragonstormForecaster.class)); cards.add(new SetCardInfo("Dragonstorm Globe", 241, Rarity.COMMON, mage.cards.d.DragonstormGlobe.class)); cards.add(new SetCardInfo("Dusyut Earthcarver", 141, Rarity.COMMON, mage.cards.d.DusyutEarthcarver.class)); cards.add(new SetCardInfo("Duty Beyond Death", 10, Rarity.UNCOMMON, mage.cards.d.DutyBeyondDeath.class)); cards.add(new SetCardInfo("Effortless Master", 181, Rarity.UNCOMMON, mage.cards.e.EffortlessMaster.class)); - cards.add(new SetCardInfo("Elspeth, Storm Slayer", 11, Rarity.MYTHIC, mage.cards.e.ElspethStormSlayer.class)); + cards.add(new SetCardInfo("Elspeth, Storm Slayer", 11, Rarity.MYTHIC, mage.cards.e.ElspethStormSlayer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elspeth, Storm Slayer", 398, Rarity.MYTHIC, mage.cards.e.ElspethStormSlayer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elspeth, Storm Slayer", 401, Rarity.MYTHIC, mage.cards.e.ElspethStormSlayer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elspeth, Storm Slayer", 411, Rarity.MYTHIC, mage.cards.e.ElspethStormSlayer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Embermouth Sentinel", 242, Rarity.COMMON, mage.cards.e.EmbermouthSentinel.class)); - cards.add(new SetCardInfo("Encroaching Dragonstorm", 142, Rarity.UNCOMMON, mage.cards.e.EncroachingDragonstorm.class)); + cards.add(new SetCardInfo("Encroaching Dragonstorm", 142, Rarity.UNCOMMON, mage.cards.e.EncroachingDragonstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Encroaching Dragonstorm", 305, Rarity.UNCOMMON, mage.cards.e.EncroachingDragonstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Encroaching Dragonstorm", 424, Rarity.UNCOMMON, mage.cards.e.EncroachingDragonstorm.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Equilibrium Adept", 106, Rarity.UNCOMMON, mage.cards.e.EquilibriumAdept.class)); - cards.add(new SetCardInfo("Eshki Dragonclaw", 182, Rarity.RARE, mage.cards.e.EshkiDragonclaw.class)); + cards.add(new SetCardInfo("Eshki Dragonclaw", 182, Rarity.RARE, mage.cards.e.EshkiDragonclaw.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eshki Dragonclaw", 356, Rarity.RARE, mage.cards.e.EshkiDragonclaw.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Essence Anchor", 44, Rarity.UNCOMMON, mage.cards.e.EssenceAnchor.class)); cards.add(new SetCardInfo("Evolving Wilds", 255, Rarity.COMMON, mage.cards.e.EvolvingWilds.class)); - cards.add(new SetCardInfo("Fangkeeper's Familiar", 183, Rarity.RARE, mage.cards.f.FangkeepersFamiliar.class)); - cards.add(new SetCardInfo("Felothar, Dawn of the Abzan", 184, Rarity.RARE, mage.cards.f.FelotharDawnOfTheAbzan.class)); - cards.add(new SetCardInfo("Feral Deathgorger", 80, Rarity.COMMON, mage.cards.f.FeralDeathgorger.class)); + cards.add(new SetCardInfo("Fangkeeper's Familiar", 183, Rarity.RARE, mage.cards.f.FangkeepersFamiliar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fangkeeper's Familiar", 357, Rarity.RARE, mage.cards.f.FangkeepersFamiliar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Felothar, Dawn of the Abzan", 184, Rarity.RARE, mage.cards.f.FelotharDawnOfTheAbzan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Felothar, Dawn of the Abzan", 358, Rarity.RARE, mage.cards.f.FelotharDawnOfTheAbzan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Feral Deathgorger", 298, Rarity.COMMON, mage.cards.f.FeralDeathgorger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Feral Deathgorger", 80, Rarity.COMMON, mage.cards.f.FeralDeathgorger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fire-Rim Form", 107, Rarity.COMMON, mage.cards.f.FireRimForm.class)); - cards.add(new SetCardInfo("Flamehold Grappler", 185, Rarity.RARE, mage.cards.f.FlameholdGrappler.class)); + cards.add(new SetCardInfo("Flamehold Grappler", 185, Rarity.RARE, mage.cards.f.FlameholdGrappler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Flamehold Grappler", 359, Rarity.RARE, mage.cards.f.FlameholdGrappler.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fleeting Effigy", 108, Rarity.UNCOMMON, mage.cards.f.FleetingEffigy.class)); cards.add(new SetCardInfo("Focus the Mind", 45, Rarity.COMMON, mage.cards.f.FocusTheMind.class)); + cards.add(new SetCardInfo("Forest", 276, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Forest", 285, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 286, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 291, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Formation Breaker", 143, Rarity.UNCOMMON, mage.cards.f.FormationBreaker.class)); cards.add(new SetCardInfo("Fortress Kin-Guard", 12, Rarity.COMMON, mage.cards.f.FortressKinGuard.class)); cards.add(new SetCardInfo("Fresh Start", 46, Rarity.UNCOMMON, mage.cards.f.FreshStart.class)); @@ -127,11 +169,13 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Glacial Dragonhunt", 188, Rarity.UNCOMMON, mage.cards.g.GlacialDragonhunt.class)); cards.add(new SetCardInfo("Glacierwood Siege", 189, Rarity.RARE, mage.cards.g.GlacierwoodSiege.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Glacierwood Siege", 386, Rarity.RARE, mage.cards.g.GlacierwoodSiege.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Great Arashin City", 257, Rarity.RARE, mage.cards.g.GreatArashinCity.class)); + cards.add(new SetCardInfo("Great Arashin City", 257, Rarity.RARE, mage.cards.g.GreatArashinCity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Great Arashin City", 395, Rarity.RARE, mage.cards.g.GreatArashinCity.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gurmag Nightwatch", 190, Rarity.COMMON, mage.cards.g.GurmagNightwatch.class)); cards.add(new SetCardInfo("Gurmag Rakshasa", 81, Rarity.UNCOMMON, mage.cards.g.GurmagRakshasa.class)); cards.add(new SetCardInfo("Hardened Tactician", 191, Rarity.UNCOMMON, mage.cards.h.HardenedTactician.class)); - cards.add(new SetCardInfo("Herd Heirloom", 144, Rarity.RARE, mage.cards.h.HerdHeirloom.class)); + cards.add(new SetCardInfo("Herd Heirloom", 144, Rarity.RARE, mage.cards.h.HerdHeirloom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Herd Heirloom", 347, Rarity.RARE, mage.cards.h.HerdHeirloom.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Heritage Reclamation", 145, Rarity.COMMON, mage.cards.h.HeritageReclamation.class)); cards.add(new SetCardInfo("Highspire Bell-Ringer", 47, Rarity.COMMON, mage.cards.h.HighspireBellRinger.class)); cards.add(new SetCardInfo("Hollowmurk Siege", 192, Rarity.RARE, mage.cards.h.HollowmurkSiege.class, NON_FULL_USE_VARIOUS)); @@ -140,181 +184,275 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Humbling Elder", 48, Rarity.COMMON, mage.cards.h.HumblingElder.class)); cards.add(new SetCardInfo("Hundred-Battle Veteran", 82, Rarity.UNCOMMON, mage.cards.h.HundredBattleVeteran.class)); cards.add(new SetCardInfo("Iceridge Serpent", 49, Rarity.COMMON, mage.cards.i.IceridgeSerpent.class)); - cards.add(new SetCardInfo("Inevitable Defeat", 194, Rarity.RARE, mage.cards.i.InevitableDefeat.class)); + cards.add(new SetCardInfo("Inevitable Defeat", 194, Rarity.RARE, mage.cards.i.InevitableDefeat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inevitable Defeat", 360, Rarity.RARE, mage.cards.i.InevitableDefeat.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Inspirited Vanguard", 146, Rarity.UNCOMMON, mage.cards.i.InspiritedVanguard.class)); cards.add(new SetCardInfo("Iridescent Tiger", 109, Rarity.UNCOMMON, mage.cards.i.IridescentTiger.class)); + cards.add(new SetCardInfo("Island", 273, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Island", 279, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 280, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 288, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Jade-Cast Sentinel", 243, Rarity.COMMON, mage.cards.j.JadeCastSentinel.class)); cards.add(new SetCardInfo("Jeskai Brushmaster", 195, Rarity.UNCOMMON, mage.cards.j.JeskaiBrushmaster.class)); cards.add(new SetCardInfo("Jeskai Devotee", 110, Rarity.COMMON, mage.cards.j.JeskaiDevotee.class)); cards.add(new SetCardInfo("Jeskai Monument", 244, Rarity.UNCOMMON, mage.cards.j.JeskaiMonument.class)); - cards.add(new SetCardInfo("Jeskai Revelation", 196, Rarity.MYTHIC, mage.cards.j.JeskaiRevelation.class)); - cards.add(new SetCardInfo("Jeskai Shrinekeeper", 197, Rarity.UNCOMMON, mage.cards.j.JeskaiShrinekeeper.class)); + cards.add(new SetCardInfo("Jeskai Revelation", 196, Rarity.MYTHIC, mage.cards.j.JeskaiRevelation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jeskai Revelation", 361, Rarity.MYTHIC, mage.cards.j.JeskaiRevelation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jeskai Shrinekeeper", 197, Rarity.UNCOMMON, mage.cards.j.JeskaiShrinekeeper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jeskai Shrinekeeper", 311, Rarity.UNCOMMON, mage.cards.j.JeskaiShrinekeeper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jungle Hollow", 258, Rarity.COMMON, mage.cards.j.JungleHollow.class)); - cards.add(new SetCardInfo("Karakyk Guardian", 198, Rarity.UNCOMMON, mage.cards.k.KarakykGuardian.class)); - cards.add(new SetCardInfo("Kheru Goldkeeper", 199, Rarity.UNCOMMON, mage.cards.k.KheruGoldkeeper.class)); + cards.add(new SetCardInfo("Karakyk Guardian", 198, Rarity.UNCOMMON, mage.cards.k.KarakykGuardian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Karakyk Guardian", 312, Rarity.UNCOMMON, mage.cards.k.KarakykGuardian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kheru Goldkeeper", 199, Rarity.UNCOMMON, mage.cards.k.KheruGoldkeeper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kheru Goldkeeper", 313, Rarity.UNCOMMON, mage.cards.k.KheruGoldkeeper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kin-Tree Nurturer", 83, Rarity.COMMON, mage.cards.k.KinTreeNurturer.class)); cards.add(new SetCardInfo("Kin-Tree Severance", 200, Rarity.UNCOMMON, mage.cards.k.KinTreeSeverance.class)); cards.add(new SetCardInfo("Kishla Skimmer", 201, Rarity.UNCOMMON, mage.cards.k.KishlaSkimmer.class)); cards.add(new SetCardInfo("Kishla Trawlers", 50, Rarity.UNCOMMON, mage.cards.k.KishlaTrawlers.class)); - cards.add(new SetCardInfo("Kishla Village", 259, Rarity.RARE, mage.cards.k.KishlaVillage.class)); + cards.add(new SetCardInfo("Kishla Village", 259, Rarity.RARE, mage.cards.k.KishlaVillage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kishla Village", 396, Rarity.RARE, mage.cards.k.KishlaVillage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Knockout Maneuver", 147, Rarity.UNCOMMON, mage.cards.k.KnockoutManeuver.class)); - cards.add(new SetCardInfo("Kotis, the Fangkeeper", 202, Rarity.RARE, mage.cards.k.KotisTheFangkeeper.class)); + cards.add(new SetCardInfo("Kotis, the Fangkeeper", 202, Rarity.RARE, mage.cards.k.KotisTheFangkeeper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kotis, the Fangkeeper", 362, Rarity.RARE, mage.cards.k.KotisTheFangkeeper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Krotiq Nestguard", 148, Rarity.COMMON, mage.cards.k.KrotiqNestguard.class)); cards.add(new SetCardInfo("Krumar Initiate", 84, Rarity.UNCOMMON, mage.cards.k.KrumarInitiate.class)); - cards.add(new SetCardInfo("Lasyd Prowler", 149, Rarity.RARE, mage.cards.l.LasydProwler.class)); + cards.add(new SetCardInfo("Lasyd Prowler", 149, Rarity.RARE, mage.cards.l.LasydProwler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lasyd Prowler", 348, Rarity.RARE, mage.cards.l.LasydProwler.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lie in Wait", 203, Rarity.UNCOMMON, mage.cards.l.LieInWait.class)); cards.add(new SetCardInfo("Lightfoot Technique", 14, Rarity.COMMON, mage.cards.l.LightfootTechnique.class)); cards.add(new SetCardInfo("Lotuslight Dancers", 204, Rarity.RARE, mage.cards.l.LotuslightDancers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lotuslight Dancers", 363, Rarity.RARE, mage.cards.l.LotuslightDancers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Loxodon Battle Priest", 15, Rarity.UNCOMMON, mage.cards.l.LoxodonBattlePriest.class)); - cards.add(new SetCardInfo("Maelstrom of the Spirit Dragon", 260, Rarity.RARE, mage.cards.m.MaelstromOfTheSpiritDragon.class)); - cards.add(new SetCardInfo("Magmatic Hellkite", 111, Rarity.RARE, mage.cards.m.MagmaticHellkite.class)); + cards.add(new SetCardInfo("Maelstrom of the Spirit Dragon", 260, Rarity.RARE, mage.cards.m.MaelstromOfTheSpiritDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Maelstrom of the Spirit Dragon", 326, Rarity.RARE, mage.cards.m.MaelstromOfTheSpiritDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Magmatic Hellkite", "380b", Rarity.RARE, mage.cards.m.MagmaticHellkite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Magmatic Hellkite", 111, Rarity.RARE, mage.cards.m.MagmaticHellkite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Magmatic Hellkite", 301, Rarity.RARE, mage.cards.m.MagmaticHellkite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Magmatic Hellkite", 380, Rarity.RARE, mage.cards.m.MagmaticHellkite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mammoth Bellow", 205, Rarity.UNCOMMON, mage.cards.m.MammothBellow.class)); + cards.add(new SetCardInfo("Marang River Regent", 378, Rarity.RARE, mage.cards.m.MarangRiverRegent.class)); cards.add(new SetCardInfo("Marang River Regent", 51, Rarity.RARE, mage.cards.m.MarangRiverRegent.class)); cards.add(new SetCardInfo("Mardu Devotee", 16, Rarity.COMMON, mage.cards.m.MarduDevotee.class)); cards.add(new SetCardInfo("Mardu Monument", 245, Rarity.UNCOMMON, mage.cards.m.MarduMonument.class)); - cards.add(new SetCardInfo("Mardu Siegebreaker", 206, Rarity.RARE, mage.cards.m.MarduSiegebreaker.class)); + cards.add(new SetCardInfo("Mardu Siegebreaker", 206, Rarity.RARE, mage.cards.m.MarduSiegebreaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mardu Siegebreaker", 364, Rarity.RARE, mage.cards.m.MarduSiegebreaker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Marshal of the Lost", 207, Rarity.UNCOMMON, mage.cards.m.MarshalOfTheLost.class)); cards.add(new SetCardInfo("Meticulous Artisan", 112, Rarity.COMMON, mage.cards.m.MeticulousArtisan.class)); - cards.add(new SetCardInfo("Mistrise Village", 261, Rarity.RARE, mage.cards.m.MistriseVillage.class)); + cards.add(new SetCardInfo("Mistrise Village", 261, Rarity.RARE, mage.cards.m.MistriseVillage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mistrise Village", 397, Rarity.RARE, mage.cards.m.MistriseVillage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Molten Exhale", 113, Rarity.COMMON, mage.cards.m.MoltenExhale.class)); cards.add(new SetCardInfo("Monastery Messenger", 208, Rarity.COMMON, mage.cards.m.MonasteryMessenger.class)); + cards.add(new SetCardInfo("Mountain", 275, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 283, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mox Jasper", 246, Rarity.MYTHIC, mage.cards.m.MoxJasper.class)); + cards.add(new SetCardInfo("Mountain", 284, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 290, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mox Jasper", 246, Rarity.MYTHIC, mage.cards.m.MoxJasper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mox Jasper", 325, Rarity.MYTHIC, mage.cards.m.MoxJasper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mox Jasper", 419, Rarity.MYTHIC, mage.cards.m.MoxJasper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mystic Monastery", 262, Rarity.UNCOMMON, mage.cards.m.MysticMonastery.class)); - cards.add(new SetCardInfo("Naga Fleshcrafter", 52, Rarity.RARE, mage.cards.n.NagaFleshcrafter.class)); + cards.add(new SetCardInfo("Naga Fleshcrafter", 333, Rarity.RARE, mage.cards.n.NagaFleshcrafter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Naga Fleshcrafter", 52, Rarity.RARE, mage.cards.n.NagaFleshcrafter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Narset's Rebuke", 114, Rarity.COMMON, mage.cards.n.NarsetsRebuke.class)); - cards.add(new SetCardInfo("Narset, Jeskai Waymaster", 209, Rarity.RARE, mage.cards.n.NarsetJeskaiWaymaster.class)); - cards.add(new SetCardInfo("Nature's Rhythm", 150, Rarity.RARE, mage.cards.n.NaturesRhythm.class)); - cards.add(new SetCardInfo("Neriv, Heart of the Storm", 210, Rarity.MYTHIC, mage.cards.n.NerivHeartOfTheStorm.class)); - cards.add(new SetCardInfo("New Way Forward", 211, Rarity.RARE, mage.cards.n.NewWayForward.class)); + cards.add(new SetCardInfo("Narset, Jeskai Waymaster", 209, Rarity.RARE, mage.cards.n.NarsetJeskaiWaymaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Narset, Jeskai Waymaster", 365, Rarity.RARE, mage.cards.n.NarsetJeskaiWaymaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Narset, Jeskai Waymaster", 407, Rarity.MYTHIC, mage.cards.n.NarsetJeskaiWaymaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Narset, Jeskai Waymaster", 417, Rarity.MYTHIC, mage.cards.n.NarsetJeskaiWaymaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nature's Rhythm", 150, Rarity.RARE, mage.cards.n.NaturesRhythm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nature's Rhythm", 349, Rarity.RARE, mage.cards.n.NaturesRhythm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Neriv, Heart of the Storm", 210, Rarity.MYTHIC, mage.cards.n.NerivHeartOfTheStorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Neriv, Heart of the Storm", 314, Rarity.MYTHIC, mage.cards.n.NerivHeartOfTheStorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Neriv, Heart of the Storm", 366, Rarity.MYTHIC, mage.cards.n.NerivHeartOfTheStorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("New Way Forward", 211, Rarity.RARE, mage.cards.n.NewWayForward.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("New Way Forward", 367, Rarity.RARE, mage.cards.n.NewWayForward.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nightblade Brigade", 85, Rarity.COMMON, mage.cards.n.NightbladeBrigade.class)); cards.add(new SetCardInfo("Nomad Outpost", 263, Rarity.UNCOMMON, mage.cards.n.NomadOutpost.class)); cards.add(new SetCardInfo("Opulent Palace", 264, Rarity.UNCOMMON, mage.cards.o.OpulentPalace.class)); cards.add(new SetCardInfo("Osseous Exhale", 17, Rarity.COMMON, mage.cards.o.OsseousExhale.class)); cards.add(new SetCardInfo("Overwhelming Surge", 115, Rarity.UNCOMMON, mage.cards.o.OverwhelmingSurge.class)); - cards.add(new SetCardInfo("Perennation", 212, Rarity.MYTHIC, mage.cards.p.Perennation.class)); + cards.add(new SetCardInfo("Perennation", 212, Rarity.MYTHIC, mage.cards.p.Perennation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Perennation", 368, Rarity.MYTHIC, mage.cards.p.Perennation.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Piercing Exhale", 151, Rarity.COMMON, mage.cards.p.PiercingExhale.class)); + cards.add(new SetCardInfo("Plains", 272, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Plains", 277, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 278, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 287, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Poised Practitioner", 18, Rarity.COMMON, mage.cards.p.PoisedPractitioner.class)); - cards.add(new SetCardInfo("Purging Stormbrood", 213, Rarity.UNCOMMON, mage.cards.p.PurgingStormbrood.class)); - cards.add(new SetCardInfo("Qarsi Revenant", 86, Rarity.RARE, mage.cards.q.QarsiRevenant.class)); + cards.add(new SetCardInfo("Purging Stormbrood", 213, Rarity.UNCOMMON, mage.cards.p.PurgingStormbrood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Purging Stormbrood", 315, Rarity.UNCOMMON, mage.cards.p.PurgingStormbrood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Qarsi Revenant", 338, Rarity.RARE, mage.cards.q.QarsiRevenant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Qarsi Revenant", 426, Rarity.RARE, mage.cards.q.QarsiRevenant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Qarsi Revenant", 86, Rarity.RARE, mage.cards.q.QarsiRevenant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rainveil Rejuvenator", 152, Rarity.UNCOMMON, mage.cards.r.RainveilRejuvenator.class)); cards.add(new SetCardInfo("Rakshasa's Bargain", 214, Rarity.UNCOMMON, mage.cards.r.RakshasasBargain.class)); cards.add(new SetCardInfo("Rally the Monastery", 19, Rarity.UNCOMMON, mage.cards.r.RallyTheMonastery.class)); cards.add(new SetCardInfo("Rebellious Strike", 20, Rarity.COMMON, mage.cards.r.RebelliousStrike.class)); - cards.add(new SetCardInfo("Rediscover the Way", 215, Rarity.RARE, mage.cards.r.RediscoverTheWay.class)); + cards.add(new SetCardInfo("Rediscover the Way", 215, Rarity.RARE, mage.cards.r.RediscoverTheWay.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rediscover the Way", 388, Rarity.RARE, mage.cards.r.RediscoverTheWay.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Reigning Victor", 216, Rarity.COMMON, mage.cards.r.ReigningVictor.class)); cards.add(new SetCardInfo("Reputable Merchant", 217, Rarity.COMMON, mage.cards.r.ReputableMerchant.class)); cards.add(new SetCardInfo("Rescue Leopard", 116, Rarity.COMMON, mage.cards.r.RescueLeopard.class)); cards.add(new SetCardInfo("Reverberating Summons", 117, Rarity.UNCOMMON, mage.cards.r.ReverberatingSummons.class)); - cards.add(new SetCardInfo("Revival of the Ancestors", 218, Rarity.RARE, mage.cards.r.RevivalOfTheAncestors.class)); - cards.add(new SetCardInfo("Riling Dawnbreaker", 21, Rarity.COMMON, mage.cards.r.RilingDawnbreaker.class)); + cards.add(new SetCardInfo("Revival of the Ancestors", 218, Rarity.RARE, mage.cards.r.RevivalOfTheAncestors.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Revival of the Ancestors", 389, Rarity.RARE, mage.cards.r.RevivalOfTheAncestors.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Riling Dawnbreaker", 21, Rarity.COMMON, mage.cards.r.RilingDawnbreaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Riling Dawnbreaker", 292, Rarity.COMMON, mage.cards.r.RilingDawnbreaker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ringing Strike Mastery", 53, Rarity.COMMON, mage.cards.r.RingingStrikeMastery.class)); cards.add(new SetCardInfo("Rite of Renewal", 153, Rarity.UNCOMMON, mage.cards.r.RiteOfRenewal.class)); cards.add(new SetCardInfo("Riverwalk Technique", 54, Rarity.COMMON, mage.cards.r.RiverwalkTechnique.class)); cards.add(new SetCardInfo("Riverwheel Sweep", 219, Rarity.UNCOMMON, mage.cards.r.RiverwheelSweep.class)); cards.add(new SetCardInfo("Roamer's Routine", 154, Rarity.COMMON, mage.cards.r.RoamersRoutine.class)); - cards.add(new SetCardInfo("Roar of Endless Song", 220, Rarity.RARE, mage.cards.r.RoarOfEndlessSong.class)); - cards.add(new SetCardInfo("Roiling Dragonstorm", 55, Rarity.UNCOMMON, mage.cards.r.RoilingDragonstorm.class)); - cards.add(new SetCardInfo("Rot-Curse Rakshasa", 87, Rarity.MYTHIC, mage.cards.r.RotCurseRakshasa.class)); + cards.add(new SetCardInfo("Roar of Endless Song", 220, Rarity.RARE, mage.cards.r.RoarOfEndlessSong.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Roar of Endless Song", 390, Rarity.RARE, mage.cards.r.RoarOfEndlessSong.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Roiling Dragonstorm", 296, Rarity.UNCOMMON, mage.cards.r.RoilingDragonstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Roiling Dragonstorm", 421, Rarity.UNCOMMON, mage.cards.r.RoilingDragonstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Roiling Dragonstorm", 55, Rarity.UNCOMMON, mage.cards.r.RoilingDragonstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rot-Curse Rakshasa", 339, Rarity.MYTHIC, mage.cards.r.RotCurseRakshasa.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rot-Curse Rakshasa", 87, Rarity.MYTHIC, mage.cards.r.RotCurseRakshasa.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rugged Highlands", 265, Rarity.COMMON, mage.cards.r.RuggedHighlands.class)); - cards.add(new SetCardInfo("Runescale Stormbrood", 221, Rarity.UNCOMMON, mage.cards.r.RunescaleStormbrood.class)); + cards.add(new SetCardInfo("Runescale Stormbrood", 221, Rarity.UNCOMMON, mage.cards.r.RunescaleStormbrood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Runescale Stormbrood", 316, Rarity.UNCOMMON, mage.cards.r.RunescaleStormbrood.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sage of the Fang", 155, Rarity.UNCOMMON, mage.cards.s.SageOfTheFang.class)); cards.add(new SetCardInfo("Sage of the Skies", 22, Rarity.RARE, mage.cards.s.SageOfTheSkies.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sage of the Skies", 328, Rarity.RARE, mage.cards.s.SageOfTheSkies.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sagu Pummeler", 156, Rarity.COMMON, mage.cards.s.SaguPummeler.class)); - cards.add(new SetCardInfo("Sagu Wildling", 157, Rarity.COMMON, mage.cards.s.SaguWildling.class)); + cards.add(new SetCardInfo("Sagu Wildling", 157, Rarity.COMMON, mage.cards.s.SaguWildling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sagu Wildling", 306, Rarity.COMMON, mage.cards.s.SaguWildling.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Salt Road Packbeast", 23, Rarity.COMMON, mage.cards.s.SaltRoadPackbeast.class)); cards.add(new SetCardInfo("Salt Road Skirmish", 88, Rarity.UNCOMMON, mage.cards.s.SaltRoadSkirmish.class)); cards.add(new SetCardInfo("Sandskitter Outrider", 89, Rarity.COMMON, mage.cards.s.SandskitterOutrider.class)); cards.add(new SetCardInfo("Sandsteppe Citadel", 266, Rarity.UNCOMMON, mage.cards.s.SandsteppeCitadel.class)); cards.add(new SetCardInfo("Sarkhan's Resolve", 158, Rarity.COMMON, mage.cards.s.SarkhansResolve.class)); - cards.add(new SetCardInfo("Sarkhan, Dragon Ascendant", 118, Rarity.RARE, mage.cards.s.SarkhanDragonAscendant.class)); + cards.add(new SetCardInfo("Sarkhan, Dragon Ascendant", 118, Rarity.RARE, mage.cards.s.SarkhanDragonAscendant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sarkhan, Dragon Ascendant", 302, Rarity.RARE, mage.cards.s.SarkhanDragonAscendant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sarkhan, Dragon Ascendant", 403, Rarity.MYTHIC, mage.cards.s.SarkhanDragonAscendant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sarkhan, Dragon Ascendant", 413, Rarity.MYTHIC, mage.cards.s.SarkhanDragonAscendant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scavenger Regent", 379, Rarity.RARE, mage.cards.s.ScavengerRegent.class)); cards.add(new SetCardInfo("Scavenger Regent", 90, Rarity.RARE, mage.cards.s.ScavengerRegent.class)); cards.add(new SetCardInfo("Scoured Barrens", 267, Rarity.COMMON, mage.cards.s.ScouredBarrens.class)); cards.add(new SetCardInfo("Seize Opportunity", 119, Rarity.COMMON, mage.cards.s.SeizeOpportunity.class)); - cards.add(new SetCardInfo("Severance Priest", 222, Rarity.RARE, mage.cards.s.SeverancePriest.class)); - cards.add(new SetCardInfo("Shiko, Paragon of the Way", 223, Rarity.MYTHIC, mage.cards.s.ShikoParagonOfTheWay.class)); + cards.add(new SetCardInfo("Severance Priest", 222, Rarity.RARE, mage.cards.s.SeverancePriest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Severance Priest", 369, Rarity.RARE, mage.cards.s.SeverancePriest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shiko, Paragon of the Way", 223, Rarity.MYTHIC, mage.cards.s.ShikoParagonOfTheWay.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shiko, Paragon of the Way", 317, Rarity.MYTHIC, mage.cards.s.ShikoParagonOfTheWay.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shiko, Paragon of the Way", 370, Rarity.MYTHIC, mage.cards.s.ShikoParagonOfTheWay.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shock Brigade", 120, Rarity.COMMON, mage.cards.s.ShockBrigade.class)); cards.add(new SetCardInfo("Shocking Sharpshooter", 121, Rarity.UNCOMMON, mage.cards.s.ShockingSharpshooter.class)); cards.add(new SetCardInfo("Sibsig Appraiser", 56, Rarity.COMMON, mage.cards.s.SibsigAppraiser.class)); - cards.add(new SetCardInfo("Sidisi, Regent of the Mire", 92, Rarity.RARE, mage.cards.s.SidisiRegentOfTheMire.class)); - cards.add(new SetCardInfo("Sinkhole Surveyor", 93, Rarity.RARE, mage.cards.s.SinkholeSurveyor.class)); - cards.add(new SetCardInfo("Skirmish Rhino", 224, Rarity.UNCOMMON, mage.cards.s.SkirmishRhino.class)); - cards.add(new SetCardInfo("Smile at Death", 24, Rarity.MYTHIC, mage.cards.s.SmileAtDeath.class)); + cards.add(new SetCardInfo("Sidisi, Regent of the Mire", 341, Rarity.RARE, mage.cards.s.SidisiRegentOfTheMire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sidisi, Regent of the Mire", 92, Rarity.RARE, mage.cards.s.SidisiRegentOfTheMire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sinkhole Surveyor", 342, Rarity.RARE, mage.cards.s.SinkholeSurveyor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sinkhole Surveyor", 93, Rarity.RARE, mage.cards.s.SinkholeSurveyor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skirmish Rhino", 224, Rarity.UNCOMMON, mage.cards.s.SkirmishRhino.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skirmish Rhino", 408, Rarity.MYTHIC, mage.cards.s.SkirmishRhino.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skirmish Rhino", 418, Rarity.MYTHIC, mage.cards.s.SkirmishRhino.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Smile at Death", 24, Rarity.MYTHIC, mage.cards.s.SmileAtDeath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Smile at Death", 329, Rarity.MYTHIC, mage.cards.s.SmileAtDeath.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Snakeskin Veil", 159, Rarity.COMMON, mage.cards.s.SnakeskinVeil.class)); cards.add(new SetCardInfo("Snowmelt Stag", 57, Rarity.COMMON, mage.cards.s.SnowmeltStag.class)); - cards.add(new SetCardInfo("Songcrafter Mage", 225, Rarity.RARE, mage.cards.s.SongcrafterMage.class)); - cards.add(new SetCardInfo("Sonic Shrieker", 226, Rarity.UNCOMMON, mage.cards.s.SonicShrieker.class)); + cards.add(new SetCardInfo("Songcrafter Mage", 225, Rarity.RARE, mage.cards.s.SongcrafterMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Songcrafter Mage", 371, Rarity.RARE, mage.cards.s.SongcrafterMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sonic Shrieker", 226, Rarity.UNCOMMON, mage.cards.s.SonicShrieker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sonic Shrieker", 318, Rarity.UNCOMMON, mage.cards.s.SonicShrieker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spectral Denial", 58, Rarity.UNCOMMON, mage.cards.s.SpectralDenial.class)); - cards.add(new SetCardInfo("Stadium Headliner", 122, Rarity.RARE, mage.cards.s.StadiumHeadliner.class)); + cards.add(new SetCardInfo("Stadium Headliner", 122, Rarity.RARE, mage.cards.s.StadiumHeadliner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stadium Headliner", 344, Rarity.RARE, mage.cards.s.StadiumHeadliner.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Stalwart Successor", 227, Rarity.UNCOMMON, mage.cards.s.StalwartSuccessor.class)); cards.add(new SetCardInfo("Starry-Eyed Skyrider", 25, Rarity.UNCOMMON, mage.cards.s.StarryEyedSkyrider.class)); - cards.add(new SetCardInfo("Static Snare", 26, Rarity.UNCOMMON, mage.cards.s.StaticSnare.class)); - cards.add(new SetCardInfo("Stillness in Motion", 59, Rarity.RARE, mage.cards.s.StillnessInMotion.class)); + cards.add(new SetCardInfo("Static Snare", 26, Rarity.UNCOMMON, mage.cards.s.StaticSnare.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Static Snare", 420, Rarity.UNCOMMON, mage.cards.s.StaticSnare.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stillness in Motion", 334, Rarity.RARE, mage.cards.s.StillnessInMotion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stillness in Motion", 59, Rarity.RARE, mage.cards.s.StillnessInMotion.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Stormbeacon Blade", 27, Rarity.UNCOMMON, mage.cards.s.StormbeaconBlade.class)); cards.add(new SetCardInfo("Stormplain Detainment", 28, Rarity.COMMON, mage.cards.s.StormplainDetainment.class)); - cards.add(new SetCardInfo("Stormscale Scion", 123, Rarity.MYTHIC, mage.cards.s.StormscaleScion.class)); - cards.add(new SetCardInfo("Stormshriek Feral", 124, Rarity.COMMON, mage.cards.s.StormshriekFeral.class)); - cards.add(new SetCardInfo("Strategic Betrayal", 94, Rarity.UNCOMMON, mage.cards.s.StrategicBetrayal.class)); + cards.add(new SetCardInfo("Stormscale Scion", 123, Rarity.MYTHIC, mage.cards.s.StormscaleScion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stormscale Scion", 303, Rarity.MYTHIC, mage.cards.s.StormscaleScion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stormshriek Feral", 124, Rarity.COMMON, mage.cards.s.StormshriekFeral.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stormshriek Feral", 304, Rarity.COMMON, mage.cards.s.StormshriekFeral.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strategic Betrayal", 422, Rarity.UNCOMMON, mage.cards.s.StrategicBetrayal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strategic Betrayal", 94, Rarity.UNCOMMON, mage.cards.s.StrategicBetrayal.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sultai Devotee", 160, Rarity.COMMON, mage.cards.s.SultaiDevotee.class)); cards.add(new SetCardInfo("Sultai Monument", 247, Rarity.UNCOMMON, mage.cards.s.SultaiMonument.class)); cards.add(new SetCardInfo("Summit Intimidator", 125, Rarity.COMMON, mage.cards.s.SummitIntimidator.class)); cards.add(new SetCardInfo("Sunpearl Kirin", 29, Rarity.UNCOMMON, mage.cards.s.SunpearlKirin.class)); cards.add(new SetCardInfo("Sunset Strikemaster", 126, Rarity.UNCOMMON, mage.cards.s.SunsetStrikemaster.class)); - cards.add(new SetCardInfo("Surrak, Elusive Hunter", 161, Rarity.RARE, mage.cards.s.SurrakElusiveHunter.class)); + cards.add(new SetCardInfo("Surrak, Elusive Hunter", 161, Rarity.RARE, mage.cards.s.SurrakElusiveHunter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Surrak, Elusive Hunter", 350, Rarity.RARE, mage.cards.s.SurrakElusiveHunter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 274, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swamp", 281, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 282, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 289, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swiftwater Cliffs", 268, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class)); cards.add(new SetCardInfo("Synchronized Charge", 162, Rarity.UNCOMMON, mage.cards.s.SynchronizedCharge.class)); - cards.add(new SetCardInfo("Taigam, Master Opportunist", 60, Rarity.MYTHIC, mage.cards.t.TaigamMasterOpportunist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Taigam, Master Opportunist", 335, Rarity.MYTHIC, mage.cards.t.TaigamMasterOpportunist.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Teeming Dragonstorm", 30, Rarity.UNCOMMON, mage.cards.t.TeemingDragonstorm.class)); + cards.add(new SetCardInfo("Taigam, Master Opportunist", 60, Rarity.MYTHIC, mage.cards.t.TaigamMasterOpportunist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teeming Dragonstorm", 293, Rarity.UNCOMMON, mage.cards.t.TeemingDragonstorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teeming Dragonstorm", 30, Rarity.UNCOMMON, mage.cards.t.TeemingDragonstorm.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tempest Hawk", 31, Rarity.COMMON, mage.cards.t.TempestHawk.class)); - cards.add(new SetCardInfo("Temur Battlecrier", 228, Rarity.RARE, mage.cards.t.TemurBattlecrier.class)); + cards.add(new SetCardInfo("Temur Battlecrier", 228, Rarity.RARE, mage.cards.t.TemurBattlecrier.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Temur Battlecrier", 372, Rarity.RARE, mage.cards.t.TemurBattlecrier.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Temur Battlecrier", 425, Rarity.RARE, mage.cards.t.TemurBattlecrier.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Temur Devotee", 61, Rarity.COMMON, mage.cards.t.TemurDevotee.class)); cards.add(new SetCardInfo("Temur Monument", 248, Rarity.UNCOMMON, mage.cards.t.TemurMonument.class)); cards.add(new SetCardInfo("Temur Tawnyback", 229, Rarity.COMMON, mage.cards.t.TemurTawnyback.class)); - cards.add(new SetCardInfo("Tersa Lightshatter", 127, Rarity.RARE, mage.cards.t.TersaLightshatter.class)); - cards.add(new SetCardInfo("Teval, Arbiter of Virtue", 230, Rarity.MYTHIC, mage.cards.t.TevalArbiterOfVirtue.class)); - cards.add(new SetCardInfo("The Sibsig Ceremony", 91, Rarity.RARE, mage.cards.t.TheSibsigCeremony.class)); + cards.add(new SetCardInfo("Tersa Lightshatter", 127, Rarity.RARE, mage.cards.t.TersaLightshatter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tersa Lightshatter", 345, Rarity.RARE, mage.cards.t.TersaLightshatter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teval, Arbiter of Virtue", 230, Rarity.MYTHIC, mage.cards.t.TevalArbiterOfVirtue.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teval, Arbiter of Virtue", 319, Rarity.MYTHIC, mage.cards.t.TevalArbiterOfVirtue.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teval, Arbiter of Virtue", 373, Rarity.MYTHIC, mage.cards.t.TevalArbiterOfVirtue.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Sibsig Ceremony", 340, Rarity.RARE, mage.cards.t.TheSibsigCeremony.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Sibsig Ceremony", 91, Rarity.RARE, mage.cards.t.TheSibsigCeremony.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Thornwood Falls", 269, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class)); - cards.add(new SetCardInfo("Thunder of Unity", 231, Rarity.RARE, mage.cards.t.ThunderOfUnity.class)); + cards.add(new SetCardInfo("Thunder of Unity", 231, Rarity.RARE, mage.cards.t.ThunderOfUnity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thunder of Unity", 391, Rarity.RARE, mage.cards.t.ThunderOfUnity.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Trade Route Envoy", 163, Rarity.COMMON, mage.cards.t.TradeRouteEnvoy.class)); cards.add(new SetCardInfo("Tranquil Cove", 270, Rarity.COMMON, mage.cards.t.TranquilCove.class)); cards.add(new SetCardInfo("Traveling Botanist", 164, Rarity.UNCOMMON, mage.cards.t.TravelingBotanist.class)); cards.add(new SetCardInfo("Twin Bolt", 128, Rarity.COMMON, mage.cards.t.TwinBolt.class)); - cards.add(new SetCardInfo("Twinmaw Stormbrood", 232, Rarity.UNCOMMON, mage.cards.t.TwinmawStormbrood.class)); - cards.add(new SetCardInfo("Ugin, Eye of the Storms", 1, Rarity.MYTHIC, mage.cards.u.UginEyeOfTheStorms.class)); + cards.add(new SetCardInfo("Twinmaw Stormbrood", 232, Rarity.UNCOMMON, mage.cards.t.TwinmawStormbrood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Twinmaw Stormbrood", 320, Rarity.UNCOMMON, mage.cards.t.TwinmawStormbrood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ugin, Eye of the Storms", "382b", Rarity.MYTHIC, mage.cards.u.UginEyeOfTheStorms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ugin, Eye of the Storms", 1, Rarity.MYTHIC, mage.cards.u.UginEyeOfTheStorms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ugin, Eye of the Storms", 382, Rarity.MYTHIC, mage.cards.u.UginEyeOfTheStorms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ugin, Eye of the Storms", 399, Rarity.MYTHIC, mage.cards.u.UginEyeOfTheStorms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ugin, Eye of the Storms", 409, Rarity.MYTHIC, mage.cards.u.UginEyeOfTheStorms.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Unburied Earthcarver", 95, Rarity.COMMON, mage.cards.u.UnburiedEarthcarver.class)); cards.add(new SetCardInfo("Underfoot Underdogs", 129, Rarity.COMMON, mage.cards.u.UnderfootUnderdogs.class)); cards.add(new SetCardInfo("Undergrowth Leopard", 165, Rarity.COMMON, mage.cards.u.UndergrowthLeopard.class)); cards.add(new SetCardInfo("Unending Whisper", 62, Rarity.COMMON, mage.cards.u.UnendingWhisper.class)); - cards.add(new SetCardInfo("United Battlefront", 32, Rarity.RARE, mage.cards.u.UnitedBattlefront.class)); + cards.add(new SetCardInfo("United Battlefront", 32, Rarity.RARE, mage.cards.u.UnitedBattlefront.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("United Battlefront", 330, Rarity.RARE, mage.cards.u.UnitedBattlefront.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Unrooted Ancestor", 96, Rarity.UNCOMMON, mage.cards.u.UnrootedAncestor.class)); cards.add(new SetCardInfo("Unsparing Boltcaster", 130, Rarity.UNCOMMON, mage.cards.u.UnsparingBoltcaster.class)); cards.add(new SetCardInfo("Ureni's Rebuff", 63, Rarity.UNCOMMON, mage.cards.u.UrenisRebuff.class)); - cards.add(new SetCardInfo("Ureni, the Song Unending", 233, Rarity.MYTHIC, mage.cards.u.UreniTheSongUnending.class)); + cards.add(new SetCardInfo("Ureni, the Song Unending", 233, Rarity.MYTHIC, mage.cards.u.UreniTheSongUnending.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ureni, the Song Unending", 321, Rarity.MYTHIC, mage.cards.u.UreniTheSongUnending.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ureni, the Song Unending", 374, Rarity.MYTHIC, mage.cards.u.UreniTheSongUnending.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Venerated Stormsinger", 97, Rarity.UNCOMMON, mage.cards.v.VeneratedStormsinger.class)); cards.add(new SetCardInfo("Veteran Ice Climber", 64, Rarity.UNCOMMON, mage.cards.v.VeteranIceClimber.class)); - cards.add(new SetCardInfo("Voice of Victory", 33, Rarity.RARE, mage.cards.v.VoiceOfVictory.class)); - cards.add(new SetCardInfo("War Effort", 131, Rarity.UNCOMMON, mage.cards.w.WarEffort.class)); + cards.add(new SetCardInfo("Voice of Victory", 33, Rarity.RARE, mage.cards.v.VoiceOfVictory.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voice of Victory", 331, Rarity.RARE, mage.cards.v.VoiceOfVictory.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Wail of War", 98, Rarity.UNCOMMON, mage.cards.w.WailOfWar.class)); - cards.add(new SetCardInfo("Warden of the Grove", 166, Rarity.RARE, mage.cards.w.WardenOfTheGrove.class)); + cards.add(new SetCardInfo("War Effort", 131, Rarity.UNCOMMON, mage.cards.w.WarEffort.class)); + cards.add(new SetCardInfo("Warden of the Grove", 166, Rarity.RARE, mage.cards.w.WardenOfTheGrove.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Warden of the Grove", 351, Rarity.RARE, mage.cards.w.WardenOfTheGrove.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Watcher of the Wayside", 249, Rarity.COMMON, mage.cards.w.WatcherOfTheWayside.class)); cards.add(new SetCardInfo("Wayspeaker Bodyguard", 34, Rarity.UNCOMMON, mage.cards.w.WayspeakerBodyguard.class)); - cards.add(new SetCardInfo("Whirlwing Stormbrood", 234, Rarity.UNCOMMON, mage.cards.w.WhirlwingStormbrood.class)); + cards.add(new SetCardInfo("Whirlwing Stormbrood", 234, Rarity.UNCOMMON, mage.cards.w.WhirlwingStormbrood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Whirlwing Stormbrood", 322, Rarity.UNCOMMON, mage.cards.w.WhirlwingStormbrood.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Wild Ride", 132, Rarity.COMMON, mage.cards.w.WildRide.class)); cards.add(new SetCardInfo("Wind-Scarred Crag", 271, Rarity.COMMON, mage.cards.w.WindScarredCrag.class)); - cards.add(new SetCardInfo("Windcrag Siege", 235, Rarity.RARE, mage.cards.w.WindcragSiege.class)); + cards.add(new SetCardInfo("Windcrag Siege", 235, Rarity.RARE, mage.cards.w.WindcragSiege.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Windcrag Siege", 392, Rarity.RARE, mage.cards.w.WindcragSiege.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Wingblade Disciple", 65, Rarity.UNCOMMON, mage.cards.w.WingbladeDisciple.class)); cards.add(new SetCardInfo("Wingspan Stride", 66, Rarity.COMMON, mage.cards.w.WingspanStride.class)); - cards.add(new SetCardInfo("Winternight Stories", 67, Rarity.RARE, mage.cards.w.WinternightStories.class)); + cards.add(new SetCardInfo("Winternight Stories", 336, Rarity.RARE, mage.cards.w.WinternightStories.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Winternight Stories", 67, Rarity.RARE, mage.cards.w.WinternightStories.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Worthy Cost", 99, Rarity.COMMON, mage.cards.w.WorthyCost.class)); - cards.add(new SetCardInfo("Yathan Roadwatcher", 236, Rarity.RARE, mage.cards.y.YathanRoadwatcher.class)); + cards.add(new SetCardInfo("Yathan Roadwatcher", 236, Rarity.RARE, mage.cards.y.YathanRoadwatcher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yathan Roadwatcher", 375, Rarity.RARE, mage.cards.y.YathanRoadwatcher.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Yathan Tombguard", 100, Rarity.UNCOMMON, mage.cards.y.YathanTombguard.class)); cards.add(new SetCardInfo("Zurgo's Vanguard", 133, Rarity.UNCOMMON, mage.cards.z.ZurgosVanguard.class)); - cards.add(new SetCardInfo("Zurgo, Thunder's Decree", 237, Rarity.RARE, mage.cards.z.ZurgoThundersDecree.class)); + cards.add(new SetCardInfo("Zurgo, Thunder's Decree", 237, Rarity.RARE, mage.cards.z.ZurgoThundersDecree.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zurgo, Thunder's Decree", 376, Rarity.RARE, mage.cards.z.ZurgoThundersDecree.class, NON_FULL_USE_VARIOUS)); } } -- 2.47.2 From bf3b662a0031f905d5564d1c0831862bd06e21fe Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Sun, 13 Apr 2025 09:06:06 -0500 Subject: [PATCH 19/95] [TDC] Implement Shiko and Narset, Unified --- .../mage/cards/s/ShikoAndNarsetUnified.java | 97 +++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 1 + 2 files changed, 98 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/ShikoAndNarsetUnified.java diff --git a/Mage.Sets/src/mage/cards/s/ShikoAndNarsetUnified.java b/Mage.Sets/src/mage/cards/s/ShikoAndNarsetUnified.java new file mode 100644 index 00000000000..51e4ef4e9fc --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShikoAndNarsetUnified.java @@ -0,0 +1,97 @@ +package mage.cards.s; + +import java.util.Collection; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.FlurryAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.Target; + +/** + * + * @author Jmlundeen + */ +public final class ShikoAndNarsetUnified extends CardImpl { + + public ShikoAndNarsetUnified(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Flurry -- Whenever you cast your second spell each turn, copy that spell if it targets a permanent or player, and you may choose new targets for the copy. If you don't copy a spell this way, draw a card. + this.addAbility(new FlurryAbility(new ShikoAndNarsetEffect())); + } + + private ShikoAndNarsetUnified(final ShikoAndNarsetUnified card) { + super(card); + } + + @Override + public ShikoAndNarsetUnified copy() { + return new ShikoAndNarsetUnified(this); + } + +} +class ShikoAndNarsetEffect extends OneShotEffect { + + public ShikoAndNarsetEffect() { + super(Outcome.Copy); + this.staticText = "copy that spell if it targets a permanent or player, and you may choose new targets for the copy. " + + "If you don't copy a spell this way, draw a card."; + } + + private ShikoAndNarsetEffect(final ShikoAndNarsetEffect effect) { + super(effect); + } + + @Override + public ShikoAndNarsetEffect copy() { + return new ShikoAndNarsetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Spell spell = (Spell) this.getValue("spellCast"); + if (controller == null || spell == null) { + return false; + } + + boolean targetsPermOrPlayer = spell.getStackAbility().getTargets().stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .anyMatch(uuid -> game.getPlayer(uuid) != null || game.getPermanent(uuid) != null); + if (targetsPermOrPlayer) { + boolean newTargets = controller.chooseUse(Outcome.Neutral, "Choose new targets for the copy of " + spell.getLogName() + "?", source, game); + spell.createCopyOnStack(game, source, source.getControllerId(), newTargets); + } + else { + controller.drawCards(1, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index d407bcb0b10..c7db37256c8 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -285,6 +285,7 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Shattered Sanctum", 391, Rarity.RARE, mage.cards.s.ShatteredSanctum.class)); cards.add(new SetCardInfo("Sheltered Thicket", 392, Rarity.RARE, mage.cards.s.ShelteredThicket.class)); cards.add(new SetCardInfo("Shigeki, Jukai Visionary", 270, Rarity.RARE, mage.cards.s.ShigekiJukaiVisionary.class)); + cards.add(new SetCardInfo("Shiko and Narset, Unified", 7, Rarity.MYTHIC, mage.cards.s.ShikoAndNarsetUnified.class)); cards.add(new SetCardInfo("Shiny Impetus", 231, Rarity.COMMON, mage.cards.s.ShinyImpetus.class)); cards.add(new SetCardInfo("Shivan Reef", 393, Rarity.RARE, mage.cards.s.ShivanReef.class)); cards.add(new SetCardInfo("Sidar Kondo of Jamuraa", 303, Rarity.MYTHIC, mage.cards.s.SidarKondoOfJamuraa.class)); -- 2.47.2 From 0420d0083610e9b0adbe5a53b37720b1c20bd0e2 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Sun, 13 Apr 2025 11:00:11 -0500 Subject: [PATCH 20/95] [TDC] Implement Steward of the Harvest --- .../src/mage/cards/s/StewardOfTheHarvest.java | 112 ++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 2 + 2 files changed, 114 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/s/StewardOfTheHarvest.java diff --git a/Mage.Sets/src/mage/cards/s/StewardOfTheHarvest.java b/Mage.Sets/src/mage/cards/s/StewardOfTheHarvest.java new file mode 100644 index 00000000000..c7c65df84b9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StewardOfTheHarvest.java @@ -0,0 +1,112 @@ +package mage.cards.s; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileCardFromOwnGraveyardControllerEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.StaticFilters; +import mage.filter.common.FilterLandCard; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; + +/** + * + * @author Jmlundeen + */ +public final class StewardOfTheHarvest extends CardImpl { + + private static final FilterLandCard filter = new FilterLandCard("land cards from your graveyard"); + + public StewardOfTheHarvest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When this creature enters, exile up to three target land cards from your graveyard. + Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetEffect().setToSourceExileZone(true)); + ability.addTarget(new TargetCardInYourGraveyard(0, 3, filter)); + this.addAbility(ability); + + // Creatures you control have all activated abilities of all land cards exiled with this creature. + this.addAbility(new SimpleStaticAbility(new StewardOfTheHarvestEffect())); + } + + private StewardOfTheHarvest(final StewardOfTheHarvest card) { + super(card); + } + + @Override + public StewardOfTheHarvest copy() { + return new StewardOfTheHarvest(this); + } +} + +class StewardOfTheHarvestEffect extends ContinuousEffectImpl { + + List abilities = new ArrayList<>(); + ExileZone lastZone; + + public StewardOfTheHarvestEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "Creatures you control have all activated abilities of all land cards exiled with this creature."; + } + + private StewardOfTheHarvestEffect(StewardOfTheHarvestEffect effect) { + super(effect); + this.abilities = effect.abilities; + this.lastZone = effect.lastZone; + } + + @Override + public StewardOfTheHarvestEffect copy() { + return new StewardOfTheHarvestEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), CardUtil.getActualSourceObjectZoneChangeCounter(game, source)); + ExileZone exile = game.getExile().getExileZone(exileId); + if (exile != null) { + lastZone = exile; + if (abilities.isEmpty()) { + exile.getCards(game).stream() + .map(card -> card.getAbilities(game)) + .flatMap(List::stream) + .filter(ability -> ability instanceof ActivatedAbility) + .forEach(ability -> abilities.add(ability)); + } + } + else { + abilities.clear(); + if (lastZone != null) { + lastZone.getCards(game).forEach(card -> game.getExile().moveToMainExileZone(card, game)); + } + } + + List creatures = game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURE, source.getControllerId(), source, game); + for (Ability ability : abilities) { + for (Permanent creature : creatures) { + creature.addAbility(ability, source.getSourceId(), game); + } + } + return true; + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index c7db37256c8..c7a9a48c4f2 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -302,6 +302,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Springbloom Druid", 271, Rarity.COMMON, mage.cards.s.SpringbloomDruid.class)); cards.add(new SetCardInfo("Staff of Compleation", 326, Rarity.MYTHIC, mage.cards.s.StaffOfCompleation.class)); cards.add(new SetCardInfo("Steel Hellkite", 327, Rarity.RARE, mage.cards.s.SteelHellkite.class)); + cards.add(new SetCardInfo("Steward of the Harvest", 48, Rarity.RARE, mage.cards.s.StewardOfTheHarvest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Steward of the Harvest", 88, Rarity.RARE, mage.cards.s.StewardOfTheHarvest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Stitcher's Supplier", 196, Rarity.UNCOMMON, mage.cards.s.StitchersSupplier.class)); cards.add(new SetCardInfo("Storm's Wrath", 236, Rarity.RARE, mage.cards.s.StormsWrath.class)); cards.add(new SetCardInfo("Storm-Kiln Artist", 235, Rarity.UNCOMMON, mage.cards.s.StormKilnArtist.class)); -- 2.47.2 From e931b06955b1b8219588758501b4bb18eab0cec7 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Sun, 13 Apr 2025 15:18:46 -0500 Subject: [PATCH 21/95] Fix Parapet Thrasher first mode --- Mage.Sets/src/mage/cards/p/ParapetThrasher.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/cards/p/ParapetThrasher.java b/Mage.Sets/src/mage/cards/p/ParapetThrasher.java index 9775826826e..8fe9ba4b29e 100644 --- a/Mage.Sets/src/mage/cards/p/ParapetThrasher.java +++ b/Mage.Sets/src/mage/cards/p/ParapetThrasher.java @@ -9,23 +9,17 @@ import mage.abilities.Mode; import mage.abilities.common.OneOrMoreDamagePlayerTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DamagePlayersEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; import mage.abilities.hint.common.ModesAlreadyUsedHint; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SubType; +import mage.constants.*; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; -import mage.game.events.DamagedPlayerEvent; -import mage.game.events.GameEvent; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; @@ -51,7 +45,8 @@ public final class ParapetThrasher extends CardImpl { // Whenever one or more Dragons you control deal combat damage to an opponent, choose one that hasn't been chosen this turn -- // * Destroy target artifact that opponent controls. - Ability ability = new OneOrMoreDamagePlayerTriggeredAbility(new DestroyTargetEffect(), filter, true, true) + Ability ability = new OneOrMoreDamagePlayerTriggeredAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), + filter, true, true, SetTargetPointer.PLAYER, false) .setTriggerPhrase("Whenever one or more Dragons you control deal combat damage to an opponent, "); ability.addTarget(new TargetPermanent(artifactFilter)); ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); -- 2.47.2 From 121845ad93b1414139cd4a98c3f3010266a8359a Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Sun, 13 Apr 2025 15:19:14 -0500 Subject: [PATCH 22/95] [TDC] Implement Thundermane Dragon --- .../src/mage/cards/t/ThundermaneDragon.java | 89 +++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 2 + Mage/src/main/java/mage/MageIdentifier.java | 1 + 3 files changed, 92 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/t/ThundermaneDragon.java diff --git a/Mage.Sets/src/mage/cards/t/ThundermaneDragon.java b/Mage.Sets/src/mage/cards/t/ThundermaneDragon.java new file mode 100644 index 00000000000..e1a32328e2c --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThundermaneDragon.java @@ -0,0 +1,89 @@ +package mage.cards.t; + +import java.util.UUID; + +import mage.MageIdentifier; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; +import mage.abilities.effects.common.continuous.PlayFromTopOfLibraryEffect; +import mage.abilities.keyword.HasteAbility; +import mage.constants.*; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.Watcher; + +/** + * + * @author Jmlundeen + */ +public final class ThundermaneDragon extends CardImpl { + + private static final FilterCreatureCard filter = new FilterCreatureCard("cast creature spells with power 4 or greater"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.OR_GREATER, 4)); + } + + public ThundermaneDragon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // You may look at the top card of your library any time. + this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); + + // You may cast creature spells with power 4 or greater from the top of your library. If you cast a creature spell this way, it gains haste until end of turn. + Effect effect = new PlayFromTopOfLibraryEffect(filter); + effect.setText(effect.getText(null) + ". If you cast a creature spell this way, it gains haste until end of turn"); + Ability ability = new SimpleStaticAbility(effect); + ability.setIdentifier(MageIdentifier.ThundermanDragonWatcher); + ability.addWatcher(new ThundermaneDragonWatcher()); + this.addAbility(ability); + } + + private ThundermaneDragon(final ThundermaneDragon card) { + super(card); + } + + @Override + public ThundermaneDragon copy() { + return new ThundermaneDragon(this); + } +} + +class ThundermaneDragonWatcher extends Watcher { + + ThundermaneDragonWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (GameEvent.EventType.SPELL_CAST.equals(event.getType()) + && event.hasApprovingIdentifier(MageIdentifier.ThundermanDragonWatcher)) { + Spell target = game.getSpell(event.getTargetId()); + if (target != null) { + ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance()); + effect.setTargetPointer(new FixedTarget(target.getCard().getId())); + game.getState().addEffect(effect, target.getSpellAbility()); + } + } + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index c7a9a48c4f2..8e76ca1e6a9 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -347,6 +347,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Think Twice", 168, Rarity.COMMON, mage.cards.t.ThinkTwice.class)); cards.add(new SetCardInfo("Third Path Iconoclast", 307, Rarity.UNCOMMON, mage.cards.t.ThirdPathIconoclast.class)); cards.add(new SetCardInfo("Thunderbreak Regent", 241, Rarity.RARE, mage.cards.t.ThunderbreakRegent.class)); + cards.add(new SetCardInfo("Thundermane Dragon", 38, Rarity.RARE, mage.cards.t.ThundermaneDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thundermane Dragon", 78, Rarity.RARE, mage.cards.t.ThundermaneDragon.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Time Wipe", 308, Rarity.RARE, mage.cards.t.TimeWipe.class)); cards.add(new SetCardInfo("Timeless Witness", 274, Rarity.UNCOMMON, mage.cards.t.TimelessWitness.class)); cards.add(new SetCardInfo("Tip the Scales", 29, Rarity.RARE, mage.cards.t.TipTheScales.class)); diff --git a/Mage/src/main/java/mage/MageIdentifier.java b/Mage/src/main/java/mage/MageIdentifier.java index 26059236f48..923c6ead139 100644 --- a/Mage/src/main/java/mage/MageIdentifier.java +++ b/Mage/src/main/java/mage/MageIdentifier.java @@ -35,6 +35,7 @@ public enum MageIdentifier { CourtOfLocthwainWatcher("Without paying manacost"), LaraCroftTombRaiderWatcher, CoramTheUndertakerWatcher, + ThundermanDragonWatcher, // ----------------------------// // alternate casts // -- 2.47.2 From 4a928a6bc9eb2796a81e9db274a68198082bd951 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Sun, 13 Apr 2025 16:08:24 -0500 Subject: [PATCH 23/95] [TDC] Implement Voracious Bibliophile --- .../mage/cards/v/VoraciousBibliophile.java | 126 ++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 2 + 2 files changed, 128 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/v/VoraciousBibliophile.java diff --git a/Mage.Sets/src/mage/cards/v/VoraciousBibliophile.java b/Mage.Sets/src/mage/cards/v/VoraciousBibliophile.java new file mode 100644 index 00000000000..af94a1ac61e --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VoraciousBibliophile.java @@ -0,0 +1,126 @@ +package mage.cards.v; + +import java.util.Collection; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.Target; + +/** + * + * @author Jmlundeen + */ +public final class VoraciousBibliophile extends CardImpl { + + public VoraciousBibliophile(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Whenever you cast a spell with one or more targets, draw that many cards. + this.addAbility(new VoraciousBibliophileTriggeredAbility()); + } + + private VoraciousBibliophile(final VoraciousBibliophile card) { + super(card); + } + + @Override + public VoraciousBibliophile copy() { + return new VoraciousBibliophile(this); + } +} + +class VoraciousBibliophileTriggeredAbility extends TriggeredAbilityImpl { + + public VoraciousBibliophileTriggeredAbility() { + super(Zone.BATTLEFIELD, new VoraciousBibliophileEffect(), false); + setTriggerPhrase("Whenever you cast a spell with one or more targets, "); + } + + private VoraciousBibliophileTriggeredAbility(final VoraciousBibliophileTriggeredAbility ability) { + super(ability); + } + + @Override + public VoraciousBibliophileTriggeredAbility copy() { + return new VoraciousBibliophileTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getPlayerId().equals(this.getControllerId())) { + return false; + } + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell == null) { + return false; + } + int numTargets = spell.getStackAbility().getTargets().stream() + .map(Target::getTargets) + .mapToInt(Collection::size) + .sum(); + if (numTargets > 0) { + this.getEffects().setValue("numTargets", numTargets); + return true; + } + return false; + } +} + +class VoraciousBibliophileEffect extends OneShotEffect { + + public VoraciousBibliophileEffect() { + super(Outcome.DrawCard); + staticText = "draw that many cards"; + } + + private VoraciousBibliophileEffect(final VoraciousBibliophileEffect effect) { + super(effect); + } + + @Override + public VoraciousBibliophileEffect copy() { + return new VoraciousBibliophileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + + int numTargets = (int) getValue("numTargets"); + controller.drawCards(numTargets, source, game); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index 8e76ca1e6a9..e30bc97279e 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -370,6 +370,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Veyran, Voice of Duality", 310, Rarity.MYTHIC, mage.cards.v.VeyranVoiceOfDuality.class)); cards.add(new SetCardInfo("Victimize", 198, Rarity.UNCOMMON, mage.cards.v.Victimize.class)); cards.add(new SetCardInfo("Viscera Seer", 199, Rarity.COMMON, mage.cards.v.VisceraSeer.class)); + cards.add(new SetCardInfo("Voracious Bibliophile", 23, Rarity.RARE, mage.cards.v.VoraciousBibliophile.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Voracious Bibliophile", 63, Rarity.RARE, mage.cards.v.VoraciousBibliophile.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Wakestone Gargoyle", 137, Rarity.RARE, mage.cards.w.WakestoneGargoyle.class)); cards.add(new SetCardInfo("Walking Bulwark", 334, Rarity.UNCOMMON, mage.cards.w.WalkingBulwark.class)); cards.add(new SetCardInfo("Wall of Blossoms", 277, Rarity.UNCOMMON, mage.cards.w.WallOfBlossoms.class)); -- 2.47.2 From cc444f19b4af77f6df9fa9b42ced42edeb157014 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Sun, 13 Apr 2025 16:25:13 -0500 Subject: [PATCH 24/95] [TDC] Implement Will of the Jeskai --- .../src/mage/cards/w/WillOfTheJeskai.java | 148 ++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 2 + 2 files changed, 150 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WillOfTheJeskai.java diff --git a/Mage.Sets/src/mage/cards/w/WillOfTheJeskai.java b/Mage.Sets/src/mage/cards/w/WillOfTheJeskai.java new file mode 100644 index 00000000000..6d1a27dc70c --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WillOfTheJeskai.java @@ -0,0 +1,148 @@ +package mage.cards.w; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.condition.common.ControlACommanderCondition; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author Jmlundeen + */ +public final class WillOfTheJeskai extends CardImpl { + + public WillOfTheJeskai(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); + + + // Choose one. If you control a commander as you cast this spell, you may choose both instead. + this.getSpellAbility().getModes().setChooseText( + "Choose one. If you control a commander as you cast this spell, you may choose both instead." + ); + this.getSpellAbility().getModes().setMoreCondition(2, ControlACommanderCondition.instance); + + // * Each player may discard their hand and draw five cards. + this.getSpellAbility().addEffect(new WillOfTheJeskaiEffect()); + + // * Each instant and sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost. + Mode mode = new Mode(new WillOfTheJeskaiFlashbackEffect()); + this.getSpellAbility().addMode(mode); + } + + private WillOfTheJeskai(final WillOfTheJeskai card) { + super(card); + } + + @Override + public WillOfTheJeskai copy() { + return new WillOfTheJeskai(this); + } +} + +class WillOfTheJeskaiEffect extends OneShotEffect { + + WillOfTheJeskaiEffect() { + super(Outcome.Benefit); + staticText = "each player may discard their hand and draw five cards"; + } + + private WillOfTheJeskaiEffect(final WillOfTheJeskaiEffect effect) { + super(effect); + } + + @Override + public WillOfTheJeskaiEffect copy() { + return new WillOfTheJeskaiEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List wheelers = new ArrayList<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null && player.chooseUse( + Outcome.DrawCard, "Discard your hand and draw five?", source, game + )) { + game.informPlayers(player.getName() + " chooses to discard their hand and draw five"); + wheelers.add(player); + } + } + for (Player player : wheelers) { + player.discard(player.getHand(), false, source, game); + player.drawCards(5, source, game); + } + return true; + } +} + +class WillOfTheJeskaiFlashbackEffect extends ContinuousEffectImpl { + + WillOfTheJeskaiFlashbackEffect() { + super(Duration.EndOfTurn, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + this.staticText = "each instant and sorcery card in your graveyard gains flashback until end of turn. " + + "The flashback cost is equal to its mana cost"; + } + + private WillOfTheJeskaiFlashbackEffect(final WillOfTheJeskaiFlashbackEffect effect) { + super(effect); + } + + @Override + public WillOfTheJeskaiFlashbackEffect copy() { + return new WillOfTheJeskaiFlashbackEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + if (!getAffectedObjectsSet()) { + return; + } + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return; + } + player.getGraveyard() + .stream() + .map(game::getCard) + .filter(Objects::nonNull) + .filter(card -> card.isInstantOrSorcery(game)) + .forEachOrdered(card -> affectedObjectList.add(new MageObjectReference(card, game))); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + player.getGraveyard() + .stream() + .filter(cardId -> affectedObjectList.contains(new MageObjectReference(cardId, game))) + .forEachOrdered(cardId -> { + Card card = game.getCard(cardId); + if (card == null) { + return; + } + FlashbackAbility ability = new FlashbackAbility(card, card.getManaCost()); + ability.setSourceId(cardId); + ability.setControllerId(card.getOwnerId()); + game.getState().addOtherAbility(card, ability); + }); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index e30bc97279e..61753e291da 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -385,6 +385,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Welcoming Vampire", 140, Rarity.RARE, mage.cards.w.WelcomingVampire.class)); cards.add(new SetCardInfo("Whirlwind of Thought", 311, Rarity.RARE, mage.cards.w.WhirlwindOfThought.class)); cards.add(new SetCardInfo("Will of the Abzan", 31, Rarity.RARE, mage.cards.w.WillOfTheAbzan.class)); + cards.add(new SetCardInfo("Will of the Jeskai", 40, Rarity.RARE, mage.cards.w.WillOfTheJeskai.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Will of the Jeskai", 80, Rarity.RARE, mage.cards.w.WillOfTheJeskai.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Will of the Mardu", 17, Rarity.RARE, mage.cards.w.WillOfTheMardu.class)); cards.add(new SetCardInfo("Will of the Sultai", 49, Rarity.RARE, mage.cards.w.WillOfTheSultai.class)); cards.add(new SetCardInfo("Will of the Temur", 24, Rarity.RARE, mage.cards.w.WillOfTheTemur.class)); -- 2.47.2 From 0ccc5a706b0069d4e86cf6181a6153b10b07fd4e Mon Sep 17 00:00:00 2001 From: theelk801 Date: Sun, 13 Apr 2025 18:53:15 -0400 Subject: [PATCH 25/95] fix verify failure --- .../src/test/java/mage/verify/VerifyCardDataTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index 7013bdd1ab3..a07de2561b0 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -1113,6 +1113,9 @@ public class VerifyCardDataTest { } for (ExpansionSet.SetCardInfo card : set.getSetCardInfo()) { + if (OmenCard.class.isAssignableFrom(card.getCardClass())) { // temporary until mtgjson fixed + continue; + } boolean cardHaveDoubleName = (doubleNames.getOrDefault(card.getName(), 0) > 1); boolean cardHaveVariousSetting = card.getGraphicInfo() != null && card.getGraphicInfo().getUsesVariousArt(); @@ -2149,7 +2152,7 @@ public class VerifyCardDataTest { // - it's can be a keyword action (only mtg rules contains a target word), so add it to the targetedKeywords // * on "must be targeted": // - TODO: enable and research checkMissTargeted - too much errors with it (is it possible to use that checks?) - boolean checkMissNonTargeted = true; // must set withNotTarget(true) + boolean checkMissNonTargeted = !(card instanceof OmenCard); // must set withNotTarget(true) temporarily set to ignore omen cards boolean checkMissTargeted = false; // must be targeted List targetedKeywords = Arrays.asList( "target", @@ -2161,7 +2164,7 @@ public class VerifyCardDataTest { ); // card can contain rules text from both sides, so must search ref card for all sides too String additionalName; - if (card instanceof CardWithSpellOption) { + if (card instanceof AdventureCard) { // temporary to prevent failure due to upstream error additionalName = ((CardWithSpellOption) card).getSpellCard().getName(); } else if (card.isTransformable() && !card.isNightCard()) { additionalName = card.getSecondCardFace().getName(); -- 2.47.2 From 187703a1fc97dc28c2e6110ee1e7e22d4652a334 Mon Sep 17 00:00:00 2001 From: earchip94 <70932732+earchip94@users.noreply.github.com> Date: Sun, 13 Apr 2025 19:56:54 -0500 Subject: [PATCH 26/95] implement [BLB] Clement, the Worrywort (#13444) --------- Co-authored-by: xenohedron <12538125+xenohedron@users.noreply.github.com> --- .../src/mage/cards/c/ClementTheWorrywort.java | 133 ++++++++++++++++++ Mage.Sets/src/mage/sets/Bloomburrow.java | 6 +- .../enters/ClementTheWorrywortTest.java | 105 ++++++++++++++ 3 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/c/ClementTheWorrywort.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/ClementTheWorrywortTest.java diff --git a/Mage.Sets/src/mage/cards/c/ClementTheWorrywort.java b/Mage.Sets/src/mage/cards/c/ClementTheWorrywort.java new file mode 100644 index 00000000000..b65236ddf23 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ClementTheWorrywort.java @@ -0,0 +1,133 @@ +package mage.cards.c; + +import mage.ConditionalMana; +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.abilities.mana.ConditionalColoredManaAbility; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.abilities.mana.conditional.CreatureCastManaCondition; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.Filter; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * + * @author earchip94 + */ +public final class ClementTheWorrywort extends CardImpl { + + private static final FilterPermanent frogFilter = new FilterPermanent(SubType.FROG, "Frogs"); + + public ClementTheWorrywort(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.FROG); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Whenever Clement, the Worrywort or another creature you control enters, return up to one target creature you control with lesser mana value to its owner's hand. + this.addAbility(new ClementTheWorrywortTriggeredAbility()); + + // Frogs you control have "{T}: Add {G} or {U}. Spend this mana only to cast a creature spell." + Ability gMana = new ConditionalColoredManaAbility(new TapSourceCost(), Mana.GreenMana(1), new ClementTheWorrywortManaBuilder()); + Ability bMana = new ConditionalColoredManaAbility(new TapSourceCost(), Mana.BlueMana(1), new ClementTheWorrywortManaBuilder()); + Ability ability = new SimpleStaticAbility( + new GainAbilityControlledEffect(gMana, Duration.WhileOnBattlefield, frogFilter, false) + .setText("Frogs you control have \"{T}: Add {G} or {U}.") + ); + ability.addEffect( + new GainAbilityControlledEffect(bMana, Duration.WhileOnBattlefield, frogFilter, false) + .setText("Spend this mana only to cast a creature spell.\"") + ); + this.addAbility(ability); + } + + private ClementTheWorrywort(final ClementTheWorrywort card) { + super(card); + } + + @Override + public ClementTheWorrywort copy() { + return new ClementTheWorrywort(this); + } +} + +class ClementTheWorrywortTriggeredAbility extends EntersBattlefieldThisOrAnotherTriggeredAbility { + + ClementTheWorrywortTriggeredAbility() { + super(new ReturnToHandTargetEffect().setText("return up to one target creature you control with lesser mana value to its owner's hand"), + StaticFilters.FILTER_PERMANENT_CREATURE, false, true); + } + + ClementTheWorrywortTriggeredAbility(final ClementTheWorrywortTriggeredAbility ability) { + super(ability); + } + + @Override + public ClementTheWorrywortTriggeredAbility copy() { + return new ClementTheWorrywortTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (super.checkTrigger(event, game)) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null) { + return false; + } + int mv = permanent.getManaValue(); + FilterControlledCreaturePermanent filter = + new FilterControlledCreaturePermanent("creature you control with mana value " + (mv - 1) + " or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, mv)); + this.addTarget(new TargetPermanent(0,1, filter)); + return true; + } + return false; + } + +} + +class ClementTheWorrywortConditionalMana extends ConditionalMana { + + ClementTheWorrywortConditionalMana(Mana mana) { + super(mana); + setComparisonScope(Filter.ComparisonScope.Any); + addCondition(new CreatureCastManaCondition()); + } +} + +class ClementTheWorrywortManaBuilder extends ConditionalManaBuilder { + + @Override + public ConditionalMana build(Object... options) { + return new ClementTheWorrywortConditionalMana(this.mana); + } + + @Override + public String getRule() { + return "Spend this mana only to cast a creature spell"; + } +} diff --git a/Mage.Sets/src/mage/sets/Bloomburrow.java b/Mage.Sets/src/mage/sets/Bloomburrow.java index fe6268e05ad..5361eed461b 100644 --- a/Mage.Sets/src/mage/sets/Bloomburrow.java +++ b/Mage.Sets/src/mage/sets/Bloomburrow.java @@ -69,9 +69,9 @@ public final class Bloomburrow extends ExpansionSet { cards.add(new SetCardInfo("Carrot Cake", 7, Rarity.COMMON, mage.cards.c.CarrotCake.class)); cards.add(new SetCardInfo("Charmed Sleep", 388, Rarity.COMMON, mage.cards.c.CharmedSleep.class)); cards.add(new SetCardInfo("Cindering Cutthroat", 208, Rarity.COMMON, mage.cards.c.CinderingCutthroat.class)); - //cards.add(new SetCardInfo("Clement, the Worrywort", 209, Rarity.RARE, mage.cards.c.ClementTheWorrywort.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Clement, the Worrywort", 329, Rarity.RARE, mage.cards.c.ClementTheWorrywort.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Clement, the Worrywort", 347, Rarity.RARE, mage.cards.c.ClementTheWorrywort.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Clement, the Worrywort", 209, Rarity.RARE, mage.cards.c.ClementTheWorrywort.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Clement, the Worrywort", 329, Rarity.RARE, mage.cards.c.ClementTheWorrywort.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Clement, the Worrywort", 347, Rarity.RARE, mage.cards.c.ClementTheWorrywort.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Clifftop Lookout", 168, Rarity.UNCOMMON, mage.cards.c.ClifftopLookout.class)); cards.add(new SetCardInfo("Coiling Rebirth", 309, Rarity.RARE, mage.cards.c.CoilingRebirth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Coiling Rebirth", 86, Rarity.RARE, mage.cards.c.CoilingRebirth.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/ClementTheWorrywortTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/ClementTheWorrywortTest.java new file mode 100644 index 00000000000..e86bc0cc6aa --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/ClementTheWorrywortTest.java @@ -0,0 +1,105 @@ +package org.mage.test.cards.abilities.enters; + +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +import mage.constants.PhaseStep; +import mage.constants.Zone; + +/** + * + * @author earchip94 + */ +public class ClementTheWorrywortTest extends CardTestPlayerBase{ + + private final String frog = "Clement, the Worrywort"; + + @Test + public void castBounce() { + addCard(Zone.HAND, playerA, frog); + addCard(Zone.BATTLEFIELD, playerA, "Spore Frog"); + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, frog); + addTarget(playerA, "Spore Frog"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, 4); + assertHandCount(playerA, 1); + } + + @Test + public void castNoTarget() { + addCard(Zone.HAND, playerA, frog); + addCard(Zone.BATTLEFIELD, playerA, "Haze Frog"); + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, frog); + setStrictChooseMode(false); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, 5); + assertHandCount(playerA, 0); + } + + @Test + public void castOther() { + addCard(Zone.HAND, playerA, "Haze Frog"); + addCard(Zone.BATTLEFIELD, playerA, frog); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Haze Frog"); + setChoice(playerA, "Whenever"); // order triggers + addTarget(playerA, frog); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, 6); + assertHandCount(playerA, 1); + } + + @Test + public void castOtherNoTarget() { + addCard(Zone.HAND, playerA, "Spore Frog"); + addCard(Zone.BATTLEFIELD, playerA, frog); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spore Frog"); + setStrictChooseMode(false); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, 7); + assertHandCount(playerA, 0); + } + + @Test + public void otherPlayerCast() { + addCard(Zone.BATTLEFIELD, playerA, frog); + + addCard(Zone.HAND, playerB, "Llanowar Elves"); + addCard(Zone.BATTLEFIELD, playerB, "Forest"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Llanowar Elves"); + // No choice required + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, 1); + assertPermanentCount(playerB, 2); + assertChoicesCount(playerA, 0); + assertChoicesCount(playerB, 0); + } +} -- 2.47.2 From 6ddbf46e70e22cbcb8d2c9ba711ffee4d491885c Mon Sep 17 00:00:00 2001 From: xenohedron <12538125+xenohedron@users.noreply.github.com> Date: Sun, 13 Apr 2025 21:37:49 -0400 Subject: [PATCH 27/95] implement [BLC] Insatiable Frugivore --- .../src/mage/cards/i/InsatiableFrugivore.java | 106 ++++++++++++++++++ .../src/mage/sets/BloomburrowCommander.java | 1 + 2 files changed, 107 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/i/InsatiableFrugivore.java diff --git a/Mage.Sets/src/mage/cards/i/InsatiableFrugivore.java b/Mage.Sets/src/mage/cards/i/InsatiableFrugivore.java new file mode 100644 index 00000000000..52022f82fd6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InsatiableFrugivore.java @@ -0,0 +1,106 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.common.SacrificeXTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.token.FoodToken; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author xenohedron + */ +public final class InsatiableFrugivore extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.FOOD, "Foods"); + + public InsatiableFrugivore(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.RAT); + this.subtype.add(SubType.BERSERKER); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // When Insatiable Frugivore enters, create a Food token, then you may exile three cards from your graveyard. If you do, repeat this process. + this.addAbility(new EntersBattlefieldTriggeredAbility(new InsatiableFrugivoreEffect())); + + // {3}{B}, Sacrifice X Foods: Creatures you control get +X/+0 and gain menace until end of turn. + Ability ability = new SimpleActivatedAbility( + new BoostControlledEffect(GetXValue.instance, StaticValue.get(0), Duration.EndOfTurn) + .setText("creatures you control get +X/+0"), + new ManaCostsImpl<>("{3}{B}") + ); + ability.addCost(new SacrificeXTargetCost(filter)); + ability.addEffect(new GainAbilityControlledEffect( + new MenaceAbility(false), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE + ).setText("and gain menace until end of turn")); + this.addAbility(ability); + + } + + private InsatiableFrugivore(final InsatiableFrugivore card) { + super(card); + } + + @Override + public InsatiableFrugivore copy() { + return new InsatiableFrugivore(this); + } +} + +class InsatiableFrugivoreEffect extends OneShotEffect { + + InsatiableFrugivoreEffect() { + super(Outcome.Benefit); + this.staticText = "create a Food token, then you may exile three cards from your graveyard. If you do, repeat this process"; + } + + private InsatiableFrugivoreEffect(final InsatiableFrugivoreEffect effect) { + super(effect); + } + + @Override + public InsatiableFrugivoreEffect copy() { + return new InsatiableFrugivoreEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + boolean repeat; + do { + new FoodToken().putOntoBattlefield(1, game, source); + Cost cost = new ExileFromGraveCost(new TargetCardInYourGraveyard(3)); + repeat = cost.canPay(source, source, controller.getId(), game) + && controller.chooseUse(Outcome.Exile, "Exile three cards from your graveyard?", source, game) + && cost.pay(source, game, source, controller.getId(), false); + } while (repeat && controller.canRespond()); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/BloomburrowCommander.java b/Mage.Sets/src/mage/sets/BloomburrowCommander.java index 19461bbc1e5..6b5d49ca2b3 100644 --- a/Mage.Sets/src/mage/sets/BloomburrowCommander.java +++ b/Mage.Sets/src/mage/sets/BloomburrowCommander.java @@ -148,6 +148,7 @@ public final class BloomburrowCommander extends ExpansionSet { cards.add(new SetCardInfo("Illusory Ambusher", 167, Rarity.UNCOMMON, mage.cards.i.IllusoryAmbusher.class)); cards.add(new SetCardInfo("Inferno Titan", 198, Rarity.MYTHIC, mage.cards.i.InfernoTitan.class)); cards.add(new SetCardInfo("Ink-Eyes, Servant of Oni", 77, Rarity.RARE, mage.cards.i.InkEyesServantOfOni.class)); + cards.add(new SetCardInfo("Insatiable Frugivore", 18, Rarity.RARE, mage.cards.i.InsatiableFrugivore.class)); cards.add(new SetCardInfo("Inspiring Overseer", 141, Rarity.COMMON, mage.cards.i.InspiringOverseer.class)); cards.add(new SetCardInfo("Intellectual Offering", 168, Rarity.RARE, mage.cards.i.IntellectualOffering.class)); cards.add(new SetCardInfo("Ishai, Ojutai Dragonspeaker", 89, Rarity.MYTHIC, mage.cards.i.IshaiOjutaiDragonspeaker.class)); -- 2.47.2 From 14d527b1f32a4ff21f6fc2a71b7e72ca31110fe1 Mon Sep 17 00:00:00 2001 From: xenohedron <12538125+xenohedron@users.noreply.github.com> Date: Sun, 13 Apr 2025 21:57:59 -0400 Subject: [PATCH 28/95] implement [ACR] Sokrates, Athenian Teacher --- .../mage/cards/s/SokratesAthenianTeacher.java | 121 ++++++++++++++++++ Mage.Sets/src/mage/sets/AssassinsCreed.java | 8 +- 2 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/s/SokratesAthenianTeacher.java diff --git a/Mage.Sets/src/mage/cards/s/SokratesAthenianTeacher.java b/Mage.Sets/src/mage/cards/s/SokratesAthenianTeacher.java new file mode 100644 index 00000000000..6754abe266c --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SokratesAthenianTeacher.java @@ -0,0 +1,121 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.SourceTappedCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author xenohedron + */ +public final class SokratesAthenianTeacher extends CardImpl { + + public SokratesAthenianTeacher(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // Sokrates, Athenian Teacher has hexproof as long as it's untapped. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect( + HexproofAbility.getInstance(), + Duration.WhileOnBattlefield + ), SourceTappedCondition.UNTAPPED, + "{this} has hexproof as long as it's untapped" + ))); + + // Sokratic Dialogue -- {T}: Until end of turn, target creature gains "If this creature would deal combat damage to a player, prevent that damage. This creature's controller and that player each draw half that many cards, rounded down." + Ability ability = new SimpleActivatedAbility( + new GainAbilityTargetEffect(new SimpleStaticAbility(new SokratesAthenianTeacherEffect()), Duration.EndOfTurn) + .setText("until end of turn, target creature gains \"" + SokratesAthenianTeacherEffect.rule + "\""), + new TapSourceCost() + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability.withFlavorWord("Sokratic Dialogue")); + } + + private SokratesAthenianTeacher(final SokratesAthenianTeacher card) { + super(card); + } + + @Override + public SokratesAthenianTeacher copy() { + return new SokratesAthenianTeacher(this); + } +} + +class SokratesAthenianTeacherEffect extends PreventionEffectImpl { + + static final String rule = "If this creature would deal combat damage to a player, prevent that damage. " + + "This creature's controller and that player each draw half that many cards, rounded down."; + + SokratesAthenianTeacherEffect() { + super(Duration.WhileOnBattlefield, Integer.MAX_VALUE, true, false); + staticText = rule; + } + + private SokratesAthenianTeacherEffect(final SokratesAthenianTeacherEffect effect) { + super(effect); + } + + @Override + public SokratesAthenianTeacherEffect copy() { + return new SokratesAthenianTeacherEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return super.applies(event, source, game) && event.getSourceId().equals(source.getSourceId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + int amount = event.getAmount() / 2; + preventDamageAction(event, source, game); + Permanent creature = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (creature != null) { + Player controller = game.getPlayer(creature.getControllerId()); + if (controller != null) { + controller.drawCards(amount, source, game); + } + } + Player damagedPlayer = game.getPlayer(event.getTargetId()); + if (damagedPlayer != null) { + damagedPlayer.drawCards(amount, source, game); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/AssassinsCreed.java b/Mage.Sets/src/mage/sets/AssassinsCreed.java index 7fb2fcf3838..233facd2f17 100644 --- a/Mage.Sets/src/mage/sets/AssassinsCreed.java +++ b/Mage.Sets/src/mage/sets/AssassinsCreed.java @@ -277,10 +277,10 @@ public final class AssassinsCreed extends ExpansionSet { cards.add(new SetCardInfo("Silent Clearing", 115, Rarity.RARE, mage.cards.s.SilentClearing.class)); cards.add(new SetCardInfo("Smoke Bomb", 259, Rarity.UNCOMMON, mage.cards.s.SmokeBomb.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Smoke Bomb", 75, Rarity.UNCOMMON, mage.cards.s.SmokeBomb.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Sokrates, Athenian Teacher", 121, Rarity.RARE, mage.cards.s.SokratesAthenianTeacher.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Sokrates, Athenian Teacher", 250, Rarity.RARE, mage.cards.s.SokratesAthenianTeacher.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Sokrates, Athenian Teacher", 273, Rarity.RARE, mage.cards.s.SokratesAthenianTeacher.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Sokrates, Athenian Teacher", 67, Rarity.RARE, mage.cards.s.SokratesAthenianTeacher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sokrates, Athenian Teacher", 121, Rarity.RARE, mage.cards.s.SokratesAthenianTeacher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sokrates, Athenian Teacher", 250, Rarity.RARE, mage.cards.s.SokratesAthenianTeacher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sokrates, Athenian Teacher", 273, Rarity.RARE, mage.cards.s.SokratesAthenianTeacher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sokrates, Athenian Teacher", 67, Rarity.RARE, mage.cards.s.SokratesAthenianTeacher.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spartan Veteran", 292, Rarity.COMMON, mage.cards.s.SpartanVeteran.class)); cards.add(new SetCardInfo("Staff of Eden, Vault's Key", 123, Rarity.MYTHIC, mage.cards.s.StaffOfEdenVaultsKey.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Staff of Eden, Vault's Key", 260, Rarity.MYTHIC, mage.cards.s.StaffOfEdenVaultsKey.class, NON_FULL_USE_VARIOUS)); -- 2.47.2 From 9261c1362e8e8af55c672bb08c664fbca3980631 Mon Sep 17 00:00:00 2001 From: earchip94 <70932732+earchip94@users.noreply.github.com> Date: Mon, 14 Apr 2025 00:36:42 -0500 Subject: [PATCH 29/95] implement [DSK] Osseous Sticktwister (#13421) --------- Co-authored-by: xenohedron <12538125+xenohedron@users.noreply.github.com> --- .../src/mage/cards/o/OsseousSticktwister.java | 109 ++++++++++++ .../src/mage/sets/DuskmournHouseOfHorror.java | 2 +- .../damage/OsseousSticktwisterTest.java | 164 ++++++++++++++++++ 3 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/cards/o/OsseousSticktwister.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/OsseousSticktwisterTest.java diff --git a/Mage.Sets/src/mage/cards/o/OsseousSticktwister.java b/Mage.Sets/src/mage/cards/o/OsseousSticktwister.java new file mode 100644 index 00000000000..98f7f263255 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OsseousSticktwister.java @@ -0,0 +1,109 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.condition.common.DeliriumCondition; +import mage.abilities.costs.Cost; +import mage.abilities.costs.OrCost; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.dynamicvalue.common.CardTypesInGraveyardCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author earchip94 + */ +public final class OsseousSticktwister extends CardImpl { + + public OsseousSticktwister(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.SCARECROW); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Delirium -- At the beginning of your end step, if there are four or more card types among cards in your graveyard, each opponent may sacrifice a nonland permanent or discard a card. Then Osseous Sticktwister deals damage equal to its power to each opponent who didn't sacrifice a permanent or discard a card this way. + Ability deliriumAbility = new BeginningOfEndStepTriggeredAbility(TargetController.YOU, new OsseousSticktwisterEffect(), false, DeliriumCondition.instance); + deliriumAbility.setAbilityWord(AbilityWord.DELIRIUM); + deliriumAbility.addHint(CardTypesInGraveyardCount.YOU.getHint()); + this.addAbility(deliriumAbility); + } + + private OsseousSticktwister(final OsseousSticktwister card) { + super(card); + } + + @Override + public OsseousSticktwister copy() { + return new OsseousSticktwister(this); + } +} + +class OsseousSticktwisterEffect extends OneShotEffect { + + OsseousSticktwisterEffect() { + super(Outcome.Benefit); + this.staticText = "each opponent may sacrifice a nonland permanent of their choice or discard a card. Then {this} " + + "deals damage equal to its power to each opponent who didn't sacrifice a permanent or discard a card this way."; + } + + OsseousSticktwisterEffect(final OsseousSticktwisterEffect effect) { + super(effect); + } + + @Override + public OsseousSticktwisterEffect copy() { + return new OsseousSticktwisterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentOrLKI(game); + int dmgAmt = permanent == null ? 0 : permanent.getPower().getValue(); + List playersToDamage = new ArrayList<>(); + String message = "Sacrifice a nonland permanent or discard a card to avoid " + dmgAmt + " damage?"; + for (UUID uuid : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(uuid); + if (opponent == null) { + continue; + } + Cost cost = new OrCost( + "sacrifice a nonland permanent or discard a card", + new SacrificeTargetCost(StaticFilters.FILTER_PERMANENT_NON_LAND), + new DiscardCardCost() + ); + if (!cost.canPay(source, source, opponent.getId(), game) + || !opponent.chooseUse(Outcome.Detriment, message, source, game) + || !cost.pay(source, game, source, opponent.getId(), false)) { + playersToDamage.add(opponent.getId()); + } + } + if (dmgAmt <= 0) { + return true; + } + game.processAction(); + for (UUID uuid : playersToDamage) { + Player opponent = game.getPlayer(uuid); + if (opponent != null) { + opponent.damage(dmgAmt, source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java index 1e66450e904..de5f9106a60 100644 --- a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java +++ b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java @@ -265,7 +265,7 @@ public final class DuskmournHouseOfHorror extends ExpansionSet { cards.add(new SetCardInfo("Optimistic Scavenger", 21, Rarity.UNCOMMON, mage.cards.o.OptimisticScavenger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Optimistic Scavenger", 288, Rarity.UNCOMMON, mage.cards.o.OptimisticScavenger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Orphans of the Wheat", 22, Rarity.UNCOMMON, mage.cards.o.OrphansOfTheWheat.class)); - //cards.add(new SetCardInfo("Osseous Sticktwister", 112, Rarity.UNCOMMON, mage.cards.o.OsseousSticktwister.class)); + cards.add(new SetCardInfo("Osseous Sticktwister", 112, Rarity.UNCOMMON, mage.cards.o.OsseousSticktwister.class)); //cards.add(new SetCardInfo("Overgrown Zealot", 193, Rarity.UNCOMMON, mage.cards.o.OvergrownZealot.class)); cards.add(new SetCardInfo("Overlord of the Balemurk", 113, Rarity.MYTHIC, mage.cards.o.OverlordOfTheBalemurk.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Overlord of the Balemurk", 377, Rarity.MYTHIC, mage.cards.o.OverlordOfTheBalemurk.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/OsseousSticktwisterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/OsseousSticktwisterTest.java new file mode 100644 index 00000000000..60391e9fe85 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/OsseousSticktwisterTest.java @@ -0,0 +1,164 @@ +package org.mage.test.cards.triggers.damage; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author earchip94 + */ +public class OsseousSticktwisterTest extends CardTestPlayerBase { + /** + * Osseous Sticktwister {1}{B} + * Creature 2/2 + * Delirium - At the beginning of your end step, if there are four or more card types among cards in your graveyard, + * each opponent may sacrifice a nonland permanent or discard a card. Then Osseous Sticktwister deals damage + * equal to its power to each opponent who didn't sacrifice a permanent or discard a card this way. + */ + @Test + public void testDelirium() { + addCard(Zone.BATTLEFIELD, playerA, "Osseous Sticktwister", 1); + addCard(Zone.BATTLEFIELD, playerB, "Priest of Titania", 1); + addCard(Zone.HAND, playerB, "Llanowar Elves", 1); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.UNTAP); + execute(); + + // Nothing should happen since delirium is not active. + assertLife(playerA, 20); + assertLife(playerB, 20); + assertPermanentCount(playerA, 1); + assertPermanentCount(playerB, 1); + assertHandCount(playerB, 1); + assertGraveyardCount(playerA, 0); + assertGraveyardCount(playerB, 0); + } + + @Test + public void testDiscard() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Osseous Sticktwister", 1); + addCard(Zone.HAND, playerB, "Priest of Titania", 1); + addCard(Zone.BATTLEFIELD, playerB, "Llanowar Elves", 1); + + // Setup Delirium: + addCard(Zone.GRAVEYARD, playerA, "Sheoldred", 1); // Creature + addCard(Zone.GRAVEYARD, playerA, "Dark Ritual", 1); // Instant + addCard(Zone.GRAVEYARD, playerA, "Damn", 1); // Sorcery + addCard(Zone.GRAVEYARD, playerA, "Phyrexian Arena", 1); // Enchantment + + // End Step Choice. + setChoice(playerB, true); // Pay the non-damage cost. + setChoice(playerB, false); // Discard a card. + setChoice(playerB, "Priest of Titania"); // Discard selection + + setStopAt(2, PhaseStep.UNTAP); + execute(); + + // Delirium active, playerB discarded a card + assertLife(playerA, 20); + assertLife(playerB, 20); + assertPermanentCount(playerA, 1); + assertPermanentCount(playerB, 1); + assertHandCount(playerB, 0); + assertGraveyardCount(playerA, 4); + assertGraveyardCount(playerB, 1); + } + + @Test + public void testSacrifice() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Osseous Sticktwister", 1); + addCard(Zone.HAND, playerB, "Priest of Titania", 1); + addCard(Zone.BATTLEFIELD, playerB, "Llanowar Elves", 1); + + // Setup Delirium: + addCard(Zone.GRAVEYARD, playerA, "Sheoldred", 1); // Creature + addCard(Zone.GRAVEYARD, playerA, "Dark Ritual", 1); // Instant + addCard(Zone.GRAVEYARD, playerA, "Damn", 1); // Sorcery + addCard(Zone.GRAVEYARD, playerA, "Phyrexian Arena", 1); // Enchantment + + // End Step Choice. + setChoice(playerB, true); // Pay the non-damage cost. + setChoice(playerB, true); // Sacrifice a card. + setChoice(playerB, "Llanowar Elves"); // Sacrifice selection + + setStopAt(2, PhaseStep.UNTAP); + execute(); + + // Delirium active, playerB sacrificed a creature + assertLife(playerA, 20); + assertLife(playerB, 20); + assertPermanentCount(playerA, 1); + assertPermanentCount(playerB, 0); + assertHandCount(playerB, 1); + assertGraveyardCount(playerA, 4); + assertGraveyardCount(playerB, 1); + } + + @Test + public void testDamage() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Osseous Sticktwister", 1); + addCard(Zone.HAND, playerB, "Priest Of Titania", 1); + addCard(Zone.BATTLEFIELD, playerB, "Llanowar Elves", 1); + + // Setup Delirium: + addCard(Zone.GRAVEYARD, playerA, "Sheoldred", 1); // Creature + addCard(Zone.GRAVEYARD, playerA, "Dark Ritual", 1); // Instant + addCard(Zone.GRAVEYARD, playerA, "Damn", 1); // Sorcery + addCard(Zone.GRAVEYARD, playerA, "Phyrexian Arena", 1); // Enchantment + + // End Step Choice. + setChoice(playerB, false); // Pay the non-damage cost. + + setStopAt(2, PhaseStep.UNTAP); + execute(); + + // Delirium active, playerB took damage equal to Sticktwister's power, + // playerA gained life equal to damage dealt. + assertLife(playerA, 22); + assertLife(playerB, 18); + assertPermanentCount(playerA, 1); + assertPermanentCount(playerB, 1); + assertHandCount(playerB, 1); + assertGraveyardCount(playerA, 4); + assertGraveyardCount(playerB, 0); + } + + @Test + public void testModifiedDamage() { + setStrictChooseMode(true); + addCard(Zone.BATTLEFIELD, playerA, "Osseous Sticktwister", 1); + addCard(Zone.HAND, playerA, "Unholy Strength", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.HAND, playerB, "Priest Of Titania", 1); + addCard(Zone.BATTLEFIELD, playerB, "Llanowar Elves", 1); + + // Setup Delirium: + addCard(Zone.GRAVEYARD, playerA, "Sheoldred", 1); // Creature + addCard(Zone.GRAVEYARD, playerA, "Dark Ritual", 1); // Instant + addCard(Zone.GRAVEYARD, playerA, "Damn", 1); // Sorcery + addCard(Zone.GRAVEYARD, playerA, "Phyrexian Arena", 1); // Enchantment + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unholy Strength", "Osseous Sticktwister"); + + // End Step Choice. + setChoice(playerB, false); // Pay the non-damage cost. + + setStopAt(2, PhaseStep.UNTAP); + execute(); + + // Delirium active, playerB took damage equal to Sticktwister's power, + // playerA gained life equal to damage dealt. + assertLife(playerA, 24); + assertLife(playerB, 16); + assertPermanentCount(playerA, 3); + assertPermanentCount(playerB, 1); + assertHandCount(playerB, 1); + assertGraveyardCount(playerA, 4); + assertGraveyardCount(playerB, 0); + } +} -- 2.47.2 From 5876040be7536750bffaea76216d3d1ec6258d40 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 14 Apr 2025 10:19:08 -0400 Subject: [PATCH 30/95] fix harmonize implementation (fixes #13531) --- .../src/main/java/mage/abilities/Ability.java | 2 + .../main/java/mage/abilities/AbilityImpl.java | 5 + .../keyword/CastFromGraveyardAbility.java | 1 + .../abilities/keyword/HarmonizeAbility.java | 133 ++++-------------- .../java/mage/game/stack/StackAbility.java | 5 + 5 files changed, 37 insertions(+), 109 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index e1bb0a5618b..512da5e120a 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -549,6 +549,8 @@ public interface Ability extends Controllable, Serializable { */ Ability setCostAdjuster(CostAdjuster costAdjuster); + CostAdjuster getCostAdjuster(); + /** * Prepare {X} settings for announce */ diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 2fd281c59a8..4a01534e11b 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -1761,6 +1761,11 @@ public abstract class AbilityImpl implements Ability { return this; } + @Override + public CostAdjuster getCostAdjuster() { + return costAdjuster; + } + @Override public void adjustX(Game game) { if (costAdjuster != null) { diff --git a/Mage/src/main/java/mage/abilities/keyword/CastFromGraveyardAbility.java b/Mage/src/main/java/mage/abilities/keyword/CastFromGraveyardAbility.java index 73de96cf5e7..54bbd86d884 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CastFromGraveyardAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CastFromGraveyardAbility.java @@ -114,6 +114,7 @@ public abstract class CastFromGraveyardAbility extends SpellAbility { spellAbilityCopy.addCost(this.getCosts().copy()); spellAbilityCopy.addCost(this.getManaCosts().copy()); spellAbilityCopy.setSpellAbilityCastMode(this.getSpellAbilityCastMode()); + spellAbilityCopy.setCostAdjuster(this.getCostAdjuster()); spellAbilityToResolve = spellAbilityCopy; ContinuousEffect effect = new CastFromGraveyardReplacementEffect(); effect.setTargetPointer(new FixedTarget(getSourceId(), game.getState().getZoneChangeCounter(getSourceId()))); diff --git a/Mage/src/main/java/mage/abilities/keyword/HarmonizeAbility.java b/Mage/src/main/java/mage/abilities/keyword/HarmonizeAbility.java index d18de35de8c..e4f7b9936e2 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HarmonizeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HarmonizeAbility.java @@ -4,15 +4,12 @@ import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.SpellAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.VariableCostImpl; -import mage.abilities.costs.VariableCostType; +import mage.abilities.costs.CostAdjuster; import mage.abilities.costs.common.TapTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.cards.Card; -import mage.constants.*; +import mage.constants.Outcome; +import mage.constants.SpellAbilityCastMode; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.PermanentIdPredicate; @@ -23,9 +20,6 @@ import mage.target.TargetPermanent; import mage.target.common.TargetControlledPermanent; import mage.util.CardUtil; -import java.util.Objects; -import java.util.UUID; - /** * @author TheElk801 */ @@ -36,8 +30,7 @@ public class HarmonizeAbility extends CastFromGraveyardAbility { public HarmonizeAbility(Card card, String manaString) { super(card, new ManaCostsImpl<>(manaString), SpellAbilityCastMode.HARMONIZE); - this.addCost(new HarmonizeCost()); - this.addSubAbility(new SimpleStaticAbility(Zone.ALL, new HarmonizeCostReductionEffect()).setRuleVisible(false)); + this.setCostAdjuster(HarmonizeAbilityAdjuster.instance); } private HarmonizeAbility(final HarmonizeAbility ability) { @@ -57,123 +50,45 @@ public class HarmonizeAbility extends CastFromGraveyardAbility { } } -class HarmonizeCostReductionEffect extends CostModificationEffectImpl { - - HarmonizeCostReductionEffect() { - super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); - } - - private HarmonizeCostReductionEffect(final HarmonizeCostReductionEffect effect) { - super(effect); - } +enum HarmonizeAbilityAdjuster implements CostAdjuster { + instance; @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - SpellAbility spellAbility = (SpellAbility) abilityToModify; - int power; + public void reduceCost(Ability ability, Game game) { if (game.inCheckPlayableState()) { - power = game + int amount = game .getBattlefield() .getActivePermanents( StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURE, - source.getControllerId(), source, game - ).stream() + ability.getControllerId(), ability, game + ) + .stream() .map(MageObject::getPower) .mapToInt(MageInt::getValue) .max() .orElse(0); - } else { - power = CardUtil - .castStream(spellAbility.getCosts().stream(), HarmonizeCost.class) - .map(HarmonizeCost::getChosenCreature) - .map(game::getPermanent) - .filter(Objects::nonNull) - .map(MageObject::getPower) - .mapToInt(MageInt::getValue) - .map(x -> Math.max(x, 0)) - .sum(); + CardUtil.reduceCost(ability, amount); + return; } - if (power > 0) { - CardUtil.adjustCost(spellAbility, power); - } - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify instanceof SpellAbility - && abilityToModify.getSourceId().equals(source.getSourceId()); - } - - @Override - public HarmonizeCostReductionEffect copy() { - return new HarmonizeCostReductionEffect(this); - } -} - -class HarmonizeCost extends VariableCostImpl { - - private UUID chosenCreature = null; - - HarmonizeCost() { - super(VariableCostType.ADDITIONAL, "", ""); - } - - private HarmonizeCost(final HarmonizeCost cost) { - super(cost); - this.chosenCreature = cost.chosenCreature; - } - - @Override - public HarmonizeCost copy() { - return new HarmonizeCost(this); - } - - @Override - public void clearPaid() { - super.clearPaid(); - chosenCreature = null; - } - - @Override - public int getMaxValue(Ability source, Game game) { - return game.getBattlefield().contains(StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURE, source, game, 1) ? 1 : 0; - } - - @Override - public int announceXValue(Ability source, Game game) { - Player player = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(ability.getControllerId()); if (player == null || !game.getBattlefield().contains( - StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURE, source, game, 1 + StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURE, ability, game, 1 ) || !player.chooseUse( - Outcome.Benefit, "Tap an untapped creature you control for harmonize?", source, game + Outcome.Tap, "Tap a creature to reduce the cost of this spell?", ability, game )) { - return 0; + return; } TargetPermanent target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURE); target.withNotTarget(true); - target.withChooseHint("for harmonize"); - player.choose(Outcome.PlayForFree, target, source, game); + target.withChooseHint("to pay for harmonize"); + player.choose(Outcome.Tap, target, ability, game); Permanent permanent = game.getPermanent(target.getFirstTarget()); if (permanent == null) { - return 0; + return; } - chosenCreature = permanent.getId(); - return 1; - } - - private FilterControlledPermanent makeFilter() { - FilterControlledPermanent filter = new FilterControlledPermanent("tap the chosen creature"); - filter.add(new PermanentIdPredicate(chosenCreature)); - return filter; - } - - @Override - public Cost getFixedCostsFromAnnouncedValue(int xValue) { - return new TapTargetCost(new TargetControlledPermanent(xValue, xValue, makeFilter(), true)); - } - - public UUID getChosenCreature() { - return chosenCreature; + CardUtil.reduceCost(ability, permanent.getPower().getValue()); + FilterControlledPermanent filter = new FilterControlledPermanent("creature chosen to tap for harmonize"); + filter.add(new PermanentIdPredicate(permanent.getId())); + ability.addCost(new TapTargetCost(new TargetControlledPermanent(filter))); } } diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index 34fb4333ab9..422e3c79585 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -787,6 +787,11 @@ public class StackAbility extends StackObjectImpl implements Ability { return this; } + @Override + public CostAdjuster getCostAdjuster() { + return costAdjuster; + } + @Override public void adjustX(Game game) { if (costAdjuster != null) { -- 2.47.2 From 998e4ca436ede792cdc087b080fead8e81727f76 Mon Sep 17 00:00:00 2001 From: Davey Struijk Date: Mon, 14 Apr 2025 16:46:43 +0200 Subject: [PATCH 31/95] Fix Ainok Wayfarer's mill count (#13537) --- Mage.Sets/src/mage/cards/a/AinokWayfarer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/a/AinokWayfarer.java b/Mage.Sets/src/mage/cards/a/AinokWayfarer.java index 77f093dc213..582b21c075f 100644 --- a/Mage.Sets/src/mage/cards/a/AinokWayfarer.java +++ b/Mage.Sets/src/mage/cards/a/AinokWayfarer.java @@ -28,7 +28,7 @@ public final class AinokWayfarer extends CardImpl { // When this creature enters, mill three cards. You may put a land card from among them into your hand. If you don't, put a +1/+1 counter on this creature. this.addAbility(new EntersBattlefieldTriggeredAbility(new MillThenPutInHandEffect( - 1, StaticFilters.FILTER_CARD_LAND_A, + 3, StaticFilters.FILTER_CARD_LAND_A, new AddCountersSourceEffect(CounterType.P1P1.createInstance(), true) ))); } -- 2.47.2 From 391f0e256b2d27110fda18b71761a2c932c38f8f Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 14 Apr 2025 10:51:01 -0400 Subject: [PATCH 32/95] [TDM] fix Call the Spirit Dragons not working --- Mage.Sets/src/mage/cards/c/CallTheSpiritDragons.java | 1 + Mage/src/main/java/mage/ObjectColor.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/c/CallTheSpiritDragons.java b/Mage.Sets/src/mage/cards/c/CallTheSpiritDragons.java index 6860194ee64..3fdf8f4eaa3 100644 --- a/Mage.Sets/src/mage/cards/c/CallTheSpiritDragons.java +++ b/Mage.Sets/src/mage/cards/c/CallTheSpiritDragons.java @@ -63,6 +63,7 @@ class CallTheSpiritDragonsEffect extends OneShotEffect { SubType.DRAGON, color.getDescription() + " Dragon you control" ); filter.add(new ColorPredicate(color)); + filters.add(filter); } } diff --git a/Mage/src/main/java/mage/ObjectColor.java b/Mage/src/main/java/mage/ObjectColor.java index b4caa2d6a13..950d12a5b94 100644 --- a/Mage/src/main/java/mage/ObjectColor.java +++ b/Mage/src/main/java/mage/ObjectColor.java @@ -16,7 +16,7 @@ public class ObjectColor implements Serializable, Copyable, Compara public static final ObjectColor GREEN = new ObjectColor("G"); public static final ObjectColor COLORLESS = new ObjectColor(); -private static final ListallColors= Arrays.asList(WHITE,BLUE,BLACK,RED,GREEN); + private static final List allColors = Arrays.asList(WHITE, BLUE, BLACK, RED, GREEN); private boolean white; private boolean blue; private boolean black; -- 2.47.2 From b7addc8afcad3de8f2e7988c78fca81c19db373d Mon Sep 17 00:00:00 2001 From: theelk801 Date: Mon, 14 Apr 2025 10:52:20 -0400 Subject: [PATCH 33/95] [TDM] fix Dispelling Exhale not targeting --- Mage.Sets/src/mage/cards/d/DispellingExhale.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/cards/d/DispellingExhale.java b/Mage.Sets/src/mage/cards/d/DispellingExhale.java index 5125df363e4..074f5564df7 100644 --- a/Mage.Sets/src/mage/cards/d/DispellingExhale.java +++ b/Mage.Sets/src/mage/cards/d/DispellingExhale.java @@ -8,6 +8,7 @@ import mage.abilities.keyword.BeholdDragonAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.target.TargetSpell; import java.util.UUID; @@ -29,6 +30,7 @@ public final class DispellingExhale extends CardImpl { BeheldDragonCondition.instance, "counter target spell unless its controller pays {2}. " + "If a Dragon was beheld, counter that spell unless its controller pays {4} instead" )); + this.getSpellAbility().addTarget(new TargetSpell()); } private DispellingExhale(final DispellingExhale card) { -- 2.47.2 From 1fc5c4d3ebb2909c8a8754fe4f79c3855a9c6f52 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Mon, 14 Apr 2025 07:25:46 -0500 Subject: [PATCH 34/95] [TDC] Implement Zurgo Stormrender --- .../src/mage/cards/z/ZurgoStormrender.java | 84 +++++++++++++++++++ .../mage/sets/TarkirDragonstormCommander.java | 1 + 2 files changed, 85 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/z/ZurgoStormrender.java diff --git a/Mage.Sets/src/mage/cards/z/ZurgoStormrender.java b/Mage.Sets/src/mage/cards/z/ZurgoStormrender.java new file mode 100644 index 00000000000..5c2df4f612c --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZurgoStormrender.java @@ -0,0 +1,84 @@ +package mage.cards.z; + +import java.util.Objects; +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.LeavesBattlefieldAllTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.constants.*; +import mage.abilities.keyword.MobilizeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.TargetPointer; + +/** + * + * @author Jmlundeen + */ +public final class ZurgoStormrender extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a creature token you control"); + + static { + filter.add(TokenPredicate.TRUE); + + } + public ZurgoStormrender(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ORC); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Mobilize 1 + this.addAbility(new MobilizeAbility(1)); + + // Whenever a creature token you control leaves the battlefield, draw a card if it was attacking. Otherwise, each opponent loses 1 life. + ConditionalOneShotEffect effect = new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), TargetWasAttackingCondition.instance); + effect.addOtherwiseEffect(new LoseLifeOpponentsEffect(1)); + effect.setText("draw a card if it was attacking. Otherwise, each opponent loses 1 life."); + this.addAbility(new LeavesBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, effect, filter, false, SetTargetPointer.PERMANENT)); + } + + private ZurgoStormrender(final ZurgoStormrender card) { + super(card); + } + + @Override + public ZurgoStormrender copy() { + return new ZurgoStormrender(this); + } +} + +enum TargetWasAttackingCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + TargetPointer target = source.getEffects().stream().map(Effect::getTargetPointer).filter(Objects::nonNull).findFirst().orElse(null); + if (target == null) { + return false; + } + Permanent creature = game.getPermanentOrLKIBattlefield(target.getFirst(game, source)); + if (creature == null) { + return false; + } + return creature.isAttacking(); + } + + @Override + public String toString() { + return "if it was attacking"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index 61753e291da..4cd0bbe460e 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -401,5 +401,6 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Young Pyromancer", 95, Rarity.UNCOMMON, mage.cards.y.YoungPyromancer.class)); cards.add(new SetCardInfo("Zenith Festival", 41, Rarity.RARE, mage.cards.z.ZenithFestival.class)); cards.add(new SetCardInfo("Zetalpa, Primal Dawn", 142, Rarity.RARE, mage.cards.z.ZetalpaPrimalDawn.class)); + cards.add(new SetCardInfo("Zurgo Stormrender", 10, Rarity.MYTHIC, mage.cards.z.ZurgoStormrender.class)); } } -- 2.47.2 From f55ad2de287395233d8bd4351c884ae21dc5c43d Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Mon, 14 Apr 2025 08:19:54 -0500 Subject: [PATCH 35/95] [TDC] Add all variants --- .../mage/sets/TarkirDragonstormCommander.java | 92 ++++++++++++------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java index 4cd0bbe460e..446a9dda51c 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstormCommander.java @@ -21,20 +21,23 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Abrade", 203, Rarity.UNCOMMON, mage.cards.a.Abrade.class)); cards.add(new SetCardInfo("Access Tunnel", 337, Rarity.UNCOMMON, mage.cards.a.AccessTunnel.class)); - cards.add(new SetCardInfo("Adaptive Training Post", 18, Rarity.RARE, mage.cards.a.AdaptiveTrainingPost.class)); + cards.add(new SetCardInfo("Adaptive Training Post", 18, Rarity.RARE, mage.cards.a.AdaptiveTrainingPost.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Adaptive Training Post", 58, Rarity.RARE, mage.cards.a.AdaptiveTrainingPost.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Adarkar Wastes", 338, Rarity.RARE, mage.cards.a.AdarkarWastes.class)); cards.add(new SetCardInfo("Adeline, Resplendent Cathar", 108, Rarity.RARE, mage.cards.a.AdelineResplendentCathar.class)); cards.add(new SetCardInfo("Afterlife from the Loam", 25, Rarity.RARE, mage.cards.a.AfterlifeFromTheLoam.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Afterlife from the Loam", 65, Rarity.RARE, mage.cards.a.AfterlifeFromTheLoam.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ainok Strike Leader", 11, Rarity.RARE, mage.cards.a.AinokStrikeLeader.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ainok Strike Leader", 51, Rarity.RARE, mage.cards.a.AinokStrikeLeader.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Aligned Heart", 12, Rarity.RARE, mage.cards.a.AlignedHeart.class)); + cards.add(new SetCardInfo("Aligned Heart", 12, Rarity.RARE, mage.cards.a.AlignedHeart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aligned Heart", 52, Rarity.RARE, mage.cards.a.AlignedHeart.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Amphin Mutineer", 143, Rarity.RARE, mage.cards.a.AmphinMutineer.class)); cards.add(new SetCardInfo("Ancestral Vision", 144, Rarity.RARE, mage.cards.a.AncestralVision.class)); cards.add(new SetCardInfo("Angel of Invention", 109, Rarity.MYTHIC, mage.cards.a.AngelOfInvention.class)); cards.add(new SetCardInfo("Anguished Unmaking", 279, Rarity.RARE, mage.cards.a.AnguishedUnmaking.class)); cards.add(new SetCardInfo("Arasta of the Endless Web", 244, Rarity.RARE, mage.cards.a.ArastaOfTheEndlessWeb.class)); - cards.add(new SetCardInfo("Arbor Adherent", 42, Rarity.RARE, mage.cards.a.ArborAdherent.class)); + cards.add(new SetCardInfo("Arbor Adherent", 42, Rarity.RARE, mage.cards.a.ArborAdherent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arbor Adherent", 82, Rarity.RARE, mage.cards.a.ArborAdherent.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arboreal Grazer", 245, Rarity.COMMON, mage.cards.a.ArborealGrazer.class)); cards.add(new SetCardInfo("Arcane Signet", 105, Rarity.UNCOMMON, mage.cards.a.ArcaneSignet.class)); cards.add(new SetCardInfo("Archmage Emeritus", 145, Rarity.RARE, mage.cards.a.ArchmageEmeritus.class)); @@ -52,7 +55,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Bastion of Remembrance", 171, Rarity.UNCOMMON, mage.cards.b.BastionOfRemembrance.class)); cards.add(new SetCardInfo("Battlefield Forge", 340, Rarity.RARE, mage.cards.b.BattlefieldForge.class)); cards.add(new SetCardInfo("Beast Within", 249, Rarity.UNCOMMON, mage.cards.b.BeastWithin.class)); - cards.add(new SetCardInfo("Become the Avalanche", 43, Rarity.RARE, mage.cards.b.BecomeTheAvalanche.class)); + cards.add(new SetCardInfo("Become the Avalanche", 43, Rarity.RARE, mage.cards.b.BecomeTheAvalanche.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Become the Avalanche", 83, Rarity.RARE, mage.cards.b.BecomeTheAvalanche.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Beetleback Chief", 205, Rarity.UNCOMMON, mage.cards.b.BeetlebackChief.class)); cards.add(new SetCardInfo("Behind the Scenes", 172, Rarity.UNCOMMON, mage.cards.b.BehindTheScenes.class)); cards.add(new SetCardInfo("Betor, Ancestor's Voice", 1, Rarity.MYTHIC, mage.cards.b.BetorAncestorsVoice.class)); @@ -62,12 +66,16 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Blasphemous Act", 207, Rarity.RARE, mage.cards.b.BlasphemousAct.class)); cards.add(new SetCardInfo("Blight Pile", 174, Rarity.UNCOMMON, mage.cards.b.BlightPile.class)); cards.add(new SetCardInfo("Bojuka Bog", 341, Rarity.COMMON, mage.cards.b.BojukaBog.class)); - cards.add(new SetCardInfo("Bone Devourer", 26, Rarity.RARE, mage.cards.b.BoneDevourer.class)); + cards.add(new SetCardInfo("Bone Devourer", 26, Rarity.RARE, mage.cards.b.BoneDevourer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bone Devourer", 66, Rarity.RARE, mage.cards.b.BoneDevourer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Boros Signet", 314, Rarity.UNCOMMON, mage.cards.b.BorosSignet.class)); cards.add(new SetCardInfo("Bountiful Landscape", 342, Rarity.COMMON, mage.cards.b.BountifulLandscape.class)); - cards.add(new SetCardInfo("Broodcaller Scourge", 44, Rarity.RARE, mage.cards.b.BroodcallerScourge.class)); - cards.add(new SetCardInfo("Caldera Pyremaw", 33, Rarity.RARE, mage.cards.c.CalderaPyremaw.class)); - cards.add(new SetCardInfo("Canopy Gargantuan", 45, Rarity.RARE, mage.cards.c.CanopyGargantuan.class)); + cards.add(new SetCardInfo("Broodcaller Scourge", 44, Rarity.RARE, mage.cards.b.BroodcallerScourge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Broodcaller Scourge", 84, Rarity.RARE, mage.cards.b.BroodcallerScourge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Caldera Pyremaw", 33, Rarity.RARE, mage.cards.c.CalderaPyremaw.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Caldera Pyremaw", 73, Rarity.RARE, mage.cards.c.CalderaPyremaw.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Canopy Gargantuan", 45, Rarity.RARE, mage.cards.c.CanopyGargantuan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Canopy Gargantuan", 85, Rarity.RARE, mage.cards.c.CanopyGargantuan.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Canopy Vista", 343, Rarity.RARE, mage.cards.c.CanopyVista.class)); cards.add(new SetCardInfo("Canyon Slough", 344, Rarity.RARE, mage.cards.c.CanyonSlough.class)); cards.add(new SetCardInfo("Carven Caryatid", 250, Rarity.UNCOMMON, mage.cards.c.CarvenCaryatid.class)); @@ -82,7 +90,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Cinder Glade", 350, Rarity.RARE, mage.cards.c.CinderGlade.class)); cards.add(new SetCardInfo("Clifftop Retreat", 351, Rarity.RARE, mage.cards.c.ClifftopRetreat.class)); cards.add(new SetCardInfo("Colfenor's Urn", 315, Rarity.RARE, mage.cards.c.ColfenorsUrn.class)); - cards.add(new SetCardInfo("Colossal Grave-Reaver", 50, Rarity.RARE, mage.cards.c.ColossalGraveReaver.class)); + cards.add(new SetCardInfo("Colossal Grave-Reaver", 50, Rarity.RARE, mage.cards.c.ColossalGraveReaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Colossal Grave-Reaver", 90, Rarity.RARE, mage.cards.c.ColossalGraveReaver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Command Beacon", 352, Rarity.RARE, mage.cards.c.CommandBeacon.class)); cards.add(new SetCardInfo("Command Tower", 107, Rarity.COMMON, mage.cards.c.CommandTower.class)); cards.add(new SetCardInfo("Commander's Insignia", 111, Rarity.RARE, mage.cards.c.CommandersInsignia.class)); @@ -100,7 +109,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Darkwater Catacombs", 355, Rarity.RARE, mage.cards.d.DarkwaterCatacombs.class)); cards.add(new SetCardInfo("Dauthi Voidwalker", 176, Rarity.RARE, mage.cards.d.DauthiVoidwalker.class)); cards.add(new SetCardInfo("Deadly Dispute", 177, Rarity.COMMON, mage.cards.d.DeadlyDispute.class)); - cards.add(new SetCardInfo("Deceptive Frostkite", 19, Rarity.RARE, mage.cards.d.DeceptiveFrostkite.class)); + cards.add(new SetCardInfo("Deceptive Frostkite", 19, Rarity.RARE, mage.cards.d.DeceptiveFrostkite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Deceptive Frostkite", 59, Rarity.RARE, mage.cards.d.DeceptiveFrostkite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Deceptive Landscape", 356, Rarity.COMMON, mage.cards.d.DeceptiveLandscape.class)); cards.add(new SetCardInfo("Deep Analysis", 150, Rarity.COMMON, mage.cards.d.DeepAnalysis.class)); cards.add(new SetCardInfo("Despark", 284, Rarity.UNCOMMON, mage.cards.d.Despark.class)); @@ -126,7 +136,7 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Emeria Angel", 114, Rarity.RARE, mage.cards.e.EmeriaAngel.class)); cards.add(new SetCardInfo("Eshki, Temur's Roar", 3, Rarity.MYTHIC, mage.cards.e.EshkiTemursRoar.class)); cards.add(new SetCardInfo("Exotic Orchard", 360, Rarity.RARE, mage.cards.e.ExoticOrchard.class)); - cards.add(new SetCardInfo("Expansion // Explosion", 287, Rarity.RARE, mage.cards.e.ExpansionExplosion.class)); + cards.add(new SetCardInfo("Expansion // Explosion", 287, Rarity.RARE, mage.cards.e.ExpansionExplosion.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Expel the Interlopers", 115, Rarity.RARE, mage.cards.e.ExpelTheInterlopers.class)); cards.add(new SetCardInfo("Expressive Iteration", 288, Rarity.UNCOMMON, mage.cards.e.ExpressiveIteration.class)); cards.add(new SetCardInfo("Faeburrow Elder", 289, Rarity.RARE, mage.cards.f.FaeburrowElder.class)); @@ -139,7 +149,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Fetid Heath", 362, Rarity.RARE, mage.cards.f.FetidHeath.class)); cards.add(new SetCardInfo("Fetid Pools", 363, Rarity.RARE, mage.cards.f.FetidPools.class)); cards.add(new SetCardInfo("Flooded Grove", 364, Rarity.RARE, mage.cards.f.FloodedGrove.class)); - cards.add(new SetCardInfo("Floral Evoker", 46, Rarity.RARE, mage.cards.f.FloralEvoker.class)); + cards.add(new SetCardInfo("Floral Evoker", 46, Rarity.RARE, mage.cards.f.FloralEvoker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Floral Evoker", 86, Rarity.RARE, mage.cards.f.FloralEvoker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forbidden Alchemy", 152, Rarity.COMMON, mage.cards.f.ForbiddenAlchemy.class)); cards.add(new SetCardInfo("Foreboding Landscape", 365, Rarity.COMMON, mage.cards.f.ForebodingLandscape.class)); cards.add(new SetCardInfo("Fortified Village", 366, Rarity.RARE, mage.cards.f.FortifiedVillage.class)); @@ -151,7 +162,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Glacial Fortress", 367, Rarity.RARE, mage.cards.g.GlacialFortress.class)); cards.add(new SetCardInfo("Glorybringer", 215, Rarity.RARE, mage.cards.g.Glorybringer.class)); cards.add(new SetCardInfo("Goblin Electromancer", 99, Rarity.COMMON, mage.cards.g.GoblinElectromancer.class)); - cards.add(new SetCardInfo("Goldlust Triad", 34, Rarity.RARE, mage.cards.g.GoldlustTriad.class)); + cards.add(new SetCardInfo("Goldlust Triad", 34, Rarity.RARE, mage.cards.g.GoldlustTriad.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Goldlust Triad", 74, Rarity.RARE, mage.cards.g.GoldlustTriad.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Goldnight Commander", 117, Rarity.UNCOMMON, mage.cards.g.GoldnightCommander.class)); cards.add(new SetCardInfo("Golgari Rot Farm", 368, Rarity.UNCOMMON, mage.cards.g.GolgariRotFarm.class)); cards.add(new SetCardInfo("Grand Crescendo", 118, Rarity.RARE, mage.cards.g.GrandCrescendo.class)); @@ -160,7 +172,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Grenzo, Havoc Raiser", 216, Rarity.RARE, mage.cards.g.GrenzoHavocRaiser.class)); cards.add(new SetCardInfo("Grisly Salvage", 290, Rarity.COMMON, mage.cards.g.GrislySalvage.class)); cards.add(new SetCardInfo("Guttersnipe", 217, Rarity.UNCOMMON, mage.cards.g.Guttersnipe.class)); - cards.add(new SetCardInfo("Hammerhead Tyrant", 21, Rarity.RARE, mage.cards.h.HammerheadTyrant.class)); + cards.add(new SetCardInfo("Hammerhead Tyrant", 21, Rarity.RARE, mage.cards.h.HammerheadTyrant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hammerhead Tyrant", 61, Rarity.RARE, mage.cards.h.HammerheadTyrant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Harbinger of the Hunt", 291, Rarity.RARE, mage.cards.h.HarbingerOfTheHunt.class)); cards.add(new SetCardInfo("Harrow", 258, Rarity.COMMON, mage.cards.h.Harrow.class)); cards.add(new SetCardInfo("Haughty Djinn", 154, Rarity.RARE, mage.cards.h.HaughtyDjinn.class)); @@ -176,15 +189,18 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Ikra Shidiqi, the Usurper", 100, Rarity.MYTHIC, mage.cards.i.IkraShidiqiTheUsurper.class)); cards.add(new SetCardInfo("Indomitable Ancients", 121, Rarity.RARE, mage.cards.i.IndomitableAncients.class)); cards.add(new SetCardInfo("Indulging Patrician", 292, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class)); - cards.add(new SetCardInfo("Infantry Shield", 35, Rarity.RARE, mage.cards.i.InfantryShield.class)); + cards.add(new SetCardInfo("Infantry Shield", 35, Rarity.RARE, mage.cards.i.InfantryShield.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Infantry Shield", 75, Rarity.RARE, mage.cards.i.InfantryShield.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Infernal Grasp", 182, Rarity.UNCOMMON, mage.cards.i.InfernalGrasp.class)); - cards.add(new SetCardInfo("Ironwill Forger", 13, Rarity.RARE, mage.cards.i.IronwillForger.class)); + cards.add(new SetCardInfo("Ironwill Forger", 13, Rarity.RARE, mage.cards.i.IronwillForger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ironwill Forger", 53, Rarity.RARE, mage.cards.i.IronwillForger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Irrigated Farmland", 372, Rarity.RARE, mage.cards.i.IrrigatedFarmland.class)); cards.add(new SetCardInfo("Isolated Chapel", 373, Rarity.RARE, mage.cards.i.IsolatedChapel.class)); cards.add(new SetCardInfo("Izzet Signet", 320, Rarity.COMMON, mage.cards.i.IzzetSignet.class)); cards.add(new SetCardInfo("Jaddi Offshoot", 260, Rarity.UNCOMMON, mage.cards.j.JaddiOffshoot.class)); cards.add(new SetCardInfo("Jarad, Golgari Lich Lord", 293, Rarity.MYTHIC, mage.cards.j.JaradGolgariLichLord.class)); - cards.add(new SetCardInfo("Jaws of Defeat", 27, Rarity.RARE, mage.cards.j.JawsOfDefeat.class)); + cards.add(new SetCardInfo("Jaws of Defeat", 27, Rarity.RARE, mage.cards.j.JawsOfDefeat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jaws of Defeat", 67, Rarity.RARE, mage.cards.j.JawsOfDefeat.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Junji, the Midnight Sky", 183, Rarity.MYTHIC, mage.cards.j.JunjiTheMidnightSky.class)); cards.add(new SetCardInfo("Karplusan Forest", 374, Rarity.RARE, mage.cards.k.KarplusanForest.class)); cards.add(new SetCardInfo("Kaya, Geist Hunter", 294, Rarity.MYTHIC, mage.cards.k.KayaGeistHunter.class)); @@ -254,16 +270,19 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Putrefy", 300, Rarity.UNCOMMON, mage.cards.p.Putrefy.class)); cards.add(new SetCardInfo("Radiant Grove", 385, Rarity.COMMON, mage.cards.r.RadiantGrove.class)); cards.add(new SetCardInfo("Rampant Growth", 265, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); - cards.add(new SetCardInfo("Rampart Architect", 47, Rarity.RARE, mage.cards.r.RampartArchitect.class)); + cards.add(new SetCardInfo("Rampart Architect", 47, Rarity.RARE, mage.cards.r.RampartArchitect.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rampart Architect", 87, Rarity.RARE, mage.cards.r.RampartArchitect.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rapacious Dragon", 229, Rarity.UNCOMMON, mage.cards.r.RapaciousDragon.class)); cards.add(new SetCardInfo("Rapid Hybridization", 162, Rarity.UNCOMMON, mage.cards.r.RapidHybridization.class)); cards.add(new SetCardInfo("Reality Shift", 163, Rarity.UNCOMMON, mage.cards.r.RealityShift.class)); cards.add(new SetCardInfo("Reassembling Skeleton", 195, Rarity.UNCOMMON, mage.cards.r.ReassemblingSkeleton.class)); - cards.add(new SetCardInfo("Redoubled Stormsinger", 37, Rarity.RARE, mage.cards.r.RedoubledStormsinger.class)); + cards.add(new SetCardInfo("Redoubled Stormsinger", 37, Rarity.RARE, mage.cards.r.RedoubledStormsinger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Redoubled Stormsinger", 77, Rarity.RARE, mage.cards.r.RedoubledStormsinger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Reflections of Littjara", 164, Rarity.RARE, mage.cards.r.ReflectionsOfLittjara.class)); cards.add(new SetCardInfo("Release the Dogs", 127, Rarity.UNCOMMON, mage.cards.r.ReleaseTheDogs.class)); cards.add(new SetCardInfo("Reliquary Tower", 386, Rarity.UNCOMMON, mage.cards.r.ReliquaryTower.class)); - cards.add(new SetCardInfo("Reunion of the House", 15, Rarity.RARE, mage.cards.r.ReunionOfTheHouse.class)); + cards.add(new SetCardInfo("Reunion of the House", 15, Rarity.RARE, mage.cards.r.ReunionOfTheHouse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reunion of the House", 55, Rarity.RARE, mage.cards.r.ReunionOfTheHouse.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rhox Faithmender", 128, Rarity.RARE, mage.cards.r.RhoxFaithmender.class)); cards.add(new SetCardInfo("Rite of Replication", 165, Rarity.RARE, mage.cards.r.RiteOfReplication.class)); cards.add(new SetCardInfo("River Kelpie", 166, Rarity.RARE, mage.cards.r.RiverKelpie.class)); @@ -326,7 +345,8 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Tasigur, the Golden Fang", 197, Rarity.RARE, mage.cards.t.TasigurTheGoldenFang.class)); cards.add(new SetCardInfo("Taurean Mauler", 238, Rarity.RARE, mage.cards.t.TaureanMauler.class)); cards.add(new SetCardInfo("Tear Asunder", 273, Rarity.UNCOMMON, mage.cards.t.TearAsunder.class)); - cards.add(new SetCardInfo("Tempest Technique", 16, Rarity.RARE, mage.cards.t.TempestTechnique.class)); + cards.add(new SetCardInfo("Tempest Technique", 16, Rarity.RARE, mage.cards.t.TempestTechnique.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tempest Technique", 56, Rarity.RARE, mage.cards.t.TempestTechnique.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Temple of Abandon", 400, Rarity.RARE, mage.cards.t.TempleOfAbandon.class)); cards.add(new SetCardInfo("Temple of Enlightenment", 401, Rarity.RARE, mage.cards.t.TempleOfEnlightenment.class)); cards.add(new SetCardInfo("Temple of Epiphany", 402, Rarity.RARE, mage.cards.t.TempleOfEpiphany.class)); @@ -351,12 +371,15 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Thundermane Dragon", 78, Rarity.RARE, mage.cards.t.ThundermaneDragon.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Time Wipe", 308, Rarity.RARE, mage.cards.t.TimeWipe.class)); cards.add(new SetCardInfo("Timeless Witness", 274, Rarity.UNCOMMON, mage.cards.t.TimelessWitness.class)); - cards.add(new SetCardInfo("Tip the Scales", 29, Rarity.RARE, mage.cards.t.TipTheScales.class)); + cards.add(new SetCardInfo("Tip the Scales", 29, Rarity.RARE, mage.cards.t.TipTheScales.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tip the Scales", 69, Rarity.RARE, mage.cards.t.TipTheScales.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tocasia's Welcome", 135, Rarity.RARE, mage.cards.t.TocasiasWelcome.class)); cards.add(new SetCardInfo("Tower Defense", 275, Rarity.UNCOMMON, mage.cards.t.TowerDefense.class)); cards.add(new SetCardInfo("Towering Titan", 276, Rarity.MYTHIC, mage.cards.t.ToweringTitan.class)); - cards.add(new SetCardInfo("Transcendent Dragon", 22, Rarity.RARE, mage.cards.t.TranscendentDragon.class)); - cards.add(new SetCardInfo("Transforming Flourish", 39, Rarity.RARE, mage.cards.t.TransformingFlourish.class)); + cards.add(new SetCardInfo("Transcendent Dragon", 22, Rarity.RARE, mage.cards.t.TranscendentDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Transcendent Dragon", 62, Rarity.RARE, mage.cards.t.TranscendentDragon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Transforming Flourish", 39, Rarity.RARE, mage.cards.t.TransformingFlourish.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Transforming Flourish", 79, Rarity.RARE, mage.cards.t.TransformingFlourish.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Treasure Cruise", 169, Rarity.COMMON, mage.cards.t.TreasureCruise.class)); cards.add(new SetCardInfo("Tree of Redemption", 97, Rarity.MYTHIC, mage.cards.t.TreeOfRedemption.class)); cards.add(new SetCardInfo("Twilight Drover", 136, Rarity.RARE, mage.cards.t.TwilightDrover.class)); @@ -381,25 +404,32 @@ public final class TarkirDragonstormCommander extends ExpansionSet { cards.add(new SetCardInfo("Wall of Roots", 278, Rarity.COMMON, mage.cards.w.WallOfRoots.class)); cards.add(new SetCardInfo("Wayfarer's Bauble", 335, Rarity.COMMON, mage.cards.w.WayfarersBauble.class)); cards.add(new SetCardInfo("Weathered Sentinels", 336, Rarity.RARE, mage.cards.w.WeatheredSentinels.class)); - cards.add(new SetCardInfo("Welcome the Dead", 30, Rarity.RARE, mage.cards.w.WelcomeTheDead.class)); + cards.add(new SetCardInfo("Welcome the Dead", 30, Rarity.RARE, mage.cards.w.WelcomeTheDead.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Welcome the Dead", 70, Rarity.RARE, mage.cards.w.WelcomeTheDead.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Welcoming Vampire", 140, Rarity.RARE, mage.cards.w.WelcomingVampire.class)); cards.add(new SetCardInfo("Whirlwind of Thought", 311, Rarity.RARE, mage.cards.w.WhirlwindOfThought.class)); - cards.add(new SetCardInfo("Will of the Abzan", 31, Rarity.RARE, mage.cards.w.WillOfTheAbzan.class)); + cards.add(new SetCardInfo("Will of the Abzan", 31, Rarity.RARE, mage.cards.w.WillOfTheAbzan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Will of the Abzan", 71, Rarity.RARE, mage.cards.w.WillOfTheAbzan.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Will of the Jeskai", 40, Rarity.RARE, mage.cards.w.WillOfTheJeskai.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Will of the Jeskai", 80, Rarity.RARE, mage.cards.w.WillOfTheJeskai.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Will of the Mardu", 17, Rarity.RARE, mage.cards.w.WillOfTheMardu.class)); - cards.add(new SetCardInfo("Will of the Sultai", 49, Rarity.RARE, mage.cards.w.WillOfTheSultai.class)); - cards.add(new SetCardInfo("Will of the Temur", 24, Rarity.RARE, mage.cards.w.WillOfTheTemur.class)); + cards.add(new SetCardInfo("Will of the Mardu", 17, Rarity.RARE, mage.cards.w.WillOfTheMardu.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Will of the Mardu", 57, Rarity.RARE, mage.cards.w.WillOfTheMardu.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Will of the Sultai", 49, Rarity.RARE, mage.cards.w.WillOfTheSultai.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Will of the Sultai", 89, Rarity.RARE, mage.cards.w.WillOfTheSultai.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Will of the Temur", 24, Rarity.RARE, mage.cards.w.WillOfTheTemur.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Will of the Temur", 64, Rarity.RARE, mage.cards.w.WillOfTheTemur.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Windbrisk Heights", 411, Rarity.RARE, mage.cards.w.WindbriskHeights.class)); cards.add(new SetCardInfo("Wingmantle Chaplain", 141, Rarity.UNCOMMON, mage.cards.w.WingmantleChaplain.class)); - cards.add(new SetCardInfo("Within Range", 32, Rarity.RARE, mage.cards.w.WithinRange.class)); + cards.add(new SetCardInfo("Within Range", 32, Rarity.RARE, mage.cards.w.WithinRange.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Within Range", 72, Rarity.RARE, mage.cards.w.WithinRange.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Woe Strider", 201, Rarity.RARE, mage.cards.w.WoeStrider.class)); cards.add(new SetCardInfo("Wonder", 170, Rarity.UNCOMMON, mage.cards.w.Wonder.class)); cards.add(new SetCardInfo("Woodland Cemetery", 412, Rarity.RARE, mage.cards.w.WoodlandCemetery.class)); cards.add(new SetCardInfo("Yahenni, Undying Partisan", 202, Rarity.RARE, mage.cards.y.YahenniUndyingPartisan.class)); cards.add(new SetCardInfo("Yavimaya Coast", 413, Rarity.RARE, mage.cards.y.YavimayaCoast.class)); cards.add(new SetCardInfo("Young Pyromancer", 95, Rarity.UNCOMMON, mage.cards.y.YoungPyromancer.class)); - cards.add(new SetCardInfo("Zenith Festival", 41, Rarity.RARE, mage.cards.z.ZenithFestival.class)); + cards.add(new SetCardInfo("Zenith Festival", 41, Rarity.RARE, mage.cards.z.ZenithFestival.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zenith Festival", 81, Rarity.RARE, mage.cards.z.ZenithFestival.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Zetalpa, Primal Dawn", 142, Rarity.RARE, mage.cards.z.ZetalpaPrimalDawn.class)); cards.add(new SetCardInfo("Zurgo Stormrender", 10, Rarity.MYTHIC, mage.cards.z.ZurgoStormrender.class)); } -- 2.47.2 From ad5093e728a467ec8b964c99d8460879a7b31e8a Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Mon, 14 Apr 2025 08:20:38 -0500 Subject: [PATCH 36/95] [TDM] Add fake double faced variants --- .../dl/sources/ScryfallImageSupportCards.java | 9 +++++++++ Mage.Sets/src/mage/sets/TarkirDragonstorm.java | 15 +++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java index 8ca97816777..c4832570889 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java @@ -701,6 +701,15 @@ public class ScryfallImageSupportCards { put("REX/Mountain/24b", "https://api.scryfall.com/cards/rex/24/en?format=image&face=back"); put("REX/Plains/21b", "https://api.scryfall.com/cards/rex/21/en?format=image&face=back"); put("REX/Swamp/23b", "https://api.scryfall.com/cards/rex/23/en?format=image&face=back"); + + // TDM - fake double faced cards + put("TDM/Bloomvine Regent/381b", "https://api.scryfall.com/cards/tdm/381/en?format=image&face=back"); + put("TDM/Clarion Conqueror/377b", "https://api.scryfall.com/cards/tdm/377/en?format=image&face=back"); + put("TDM/Magmatic Hellkite/380b", "https://api.scryfall.com/cards/tdm/380/en?format=image&face=back"); + put("TDM/Marang River Regent/378b", "https://api.scryfall.com/cards/tdm/378/en?format=image&face=back"); + put("TDM/Scavenger Regent/379b", "https://api.scryfall.com/cards/tdm/379/en?format=image&face=back"); + put("TDM/Ugin, Eye of the Storms/382b", "https://api.scryfall.com/cards/tdm/382/en?format=image&face=back"); + } }; diff --git a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java index 4121a149f73..8dd846cb18d 100644 --- a/Mage.Sets/src/mage/sets/TarkirDragonstorm.java +++ b/Mage.Sets/src/mage/sets/TarkirDragonstorm.java @@ -59,8 +59,9 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Betor, Kin to All", 353, Rarity.MYTHIC, mage.cards.b.BetorKinToAll.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bewildering Blizzard", 38, Rarity.UNCOMMON, mage.cards.b.BewilderingBlizzard.class)); cards.add(new SetCardInfo("Bloodfell Caves", 250, Rarity.COMMON, mage.cards.b.BloodfellCaves.class)); - cards.add(new SetCardInfo("Bloomvine Regent", 136, Rarity.RARE, mage.cards.b.BloomvineRegent.class)); - cards.add(new SetCardInfo("Bloomvine Regent", 381, Rarity.RARE, mage.cards.b.BloomvineRegent.class)); + cards.add(new SetCardInfo("Bloomvine Regent", 136, Rarity.RARE, mage.cards.b.BloomvineRegent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloomvine Regent", 381, Rarity.RARE, mage.cards.b.BloomvineRegent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bloomvine Regent", "381b", Rarity.RARE, mage.cards.b.BloomvineRegent.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Blossoming Sands", 251, Rarity.COMMON, mage.cards.b.BlossomingSands.class)); cards.add(new SetCardInfo("Bone-Cairn Butcher", 173, Rarity.UNCOMMON, mage.cards.b.BoneCairnButcher.class)); cards.add(new SetCardInfo("Boulderborn Dragon", 239, Rarity.COMMON, mage.cards.b.BoulderbornDragon.class, NON_FULL_USE_VARIOUS)); @@ -230,8 +231,9 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Magmatic Hellkite", 301, Rarity.RARE, mage.cards.m.MagmaticHellkite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Magmatic Hellkite", 380, Rarity.RARE, mage.cards.m.MagmaticHellkite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mammoth Bellow", 205, Rarity.UNCOMMON, mage.cards.m.MammothBellow.class)); - cards.add(new SetCardInfo("Marang River Regent", 378, Rarity.RARE, mage.cards.m.MarangRiverRegent.class)); - cards.add(new SetCardInfo("Marang River Regent", 51, Rarity.RARE, mage.cards.m.MarangRiverRegent.class)); + cards.add(new SetCardInfo("Marang River Regent", 51, Rarity.RARE, mage.cards.m.MarangRiverRegent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Marang River Regent", 378, Rarity.RARE, mage.cards.m.MarangRiverRegent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Marang River Regent", "378b", Rarity.RARE, mage.cards.m.MarangRiverRegent.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mardu Devotee", 16, Rarity.COMMON, mage.cards.m.MarduDevotee.class)); cards.add(new SetCardInfo("Mardu Monument", 245, Rarity.UNCOMMON, mage.cards.m.MarduMonument.class)); cards.add(new SetCardInfo("Mardu Siegebreaker", 206, Rarity.RARE, mage.cards.m.MarduSiegebreaker.class, NON_FULL_USE_VARIOUS)); @@ -326,8 +328,9 @@ public final class TarkirDragonstorm extends ExpansionSet { cards.add(new SetCardInfo("Sarkhan, Dragon Ascendant", 302, Rarity.RARE, mage.cards.s.SarkhanDragonAscendant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sarkhan, Dragon Ascendant", 403, Rarity.MYTHIC, mage.cards.s.SarkhanDragonAscendant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sarkhan, Dragon Ascendant", 413, Rarity.MYTHIC, mage.cards.s.SarkhanDragonAscendant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Scavenger Regent", 379, Rarity.RARE, mage.cards.s.ScavengerRegent.class)); - cards.add(new SetCardInfo("Scavenger Regent", 90, Rarity.RARE, mage.cards.s.ScavengerRegent.class)); + cards.add(new SetCardInfo("Scavenger Regent", 90, Rarity.RARE, mage.cards.s.ScavengerRegent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scavenger Regent", 379, Rarity.RARE, mage.cards.s.ScavengerRegent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scavenger Regent", "379b", Rarity.RARE, mage.cards.s.ScavengerRegent.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Scoured Barrens", 267, Rarity.COMMON, mage.cards.s.ScouredBarrens.class)); cards.add(new SetCardInfo("Seize Opportunity", 119, Rarity.COMMON, mage.cards.s.SeizeOpportunity.class)); cards.add(new SetCardInfo("Severance Priest", 222, Rarity.RARE, mage.cards.s.SeverancePriest.class, NON_FULL_USE_VARIOUS)); -- 2.47.2 From 0f0026c375204d7bbedddb432b79c75bb2fe1ce4 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Mon, 14 Apr 2025 09:57:36 -0500 Subject: [PATCH 37/95] fix verify error with reversible omen cards --- .../src/test/java/mage/verify/VerifyCardDataTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index a07de2561b0..35d4b607008 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -1663,6 +1663,12 @@ public class VerifyCardDataTest { private void check(Card card, int cardIndex) { MtgJsonCard ref = MtgJsonService.cardFromSet(card.getExpansionSetCode(), card.getName(), card.getCardNumber()); if (ref != null) { + if (card instanceof SpellOptionCard && ref.layout.equals("reversible_card")) { + // TODO: Remove when MtgJson updated + // workaround for reversible omen cards e.g. Bloomvine Regent // Claim Territory // Bloomvine Regent + // both sides have main card info + return; + } checkAll(card, ref, cardIndex); } else if (!CHECK_ONLY_ABILITIES_TEXT) { warn(card, "Can't find card in mtgjson to verify"); -- 2.47.2 From 74d2265c124b6368a7b02f26809cc48b4e4a3cab Mon Sep 17 00:00:00 2001 From: xenohedron Date: Mon, 14 Apr 2025 11:19:57 -0400 Subject: [PATCH 38/95] refactor: find targeting stack object (#13534) * refactor: simplify finding targeting stack object related to #11185, 8e1805c * clarify docs --------- Co-authored-by: xenohedron <12538125+xenohedron@users.noreply.github.com> --- .../mage/cards/a/AgrusKosEternalSoldier.java | 5 +- .../src/mage/cards/p/PawpatchRecruit.java | 5 +- .../src/mage/cards/s/SurrakElusiveHunter.java | 6 +- .../BecomesTargetAnyTriggeredAbility.java | 5 +- ...BecomesTargetAttachedTriggeredAbility.java | 5 +- ...comesTargetControllerTriggeredAbility.java | 5 +- .../BecomesTargetSourceTriggeredAbility.java | 5 +- .../mage/abilities/keyword/WardAbility.java | 5 +- Mage/src/main/java/mage/util/CardUtil.java | 55 ++++++------------- ...rOfTimesPermanentTargetedATurnWatcher.java | 4 +- 10 files changed, 27 insertions(+), 73 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java b/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java index 7253374c3df..c2090ee031a 100644 --- a/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java +++ b/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java @@ -90,13 +90,10 @@ class AgrusKosEternalSoldierTriggeredAbility extends TriggeredAbilityImpl { if (!event.getTargetId().equals(getSourceId())) { return false; } - StackObject targetingObject = CardUtil.getTargetingStackObject(this.getId().toString(), event, game); + StackObject targetingObject = CardUtil.findTargetingStackObject(this.getId().toString(), event, game); if (targetingObject == null || targetingObject instanceof Spell) { return false; } - if (CardUtil.checkTargetedEventAlreadyUsed(this.getId().toString(), targetingObject, event, game)) { - return false; - } Set targets = targetingObject .getStackAbility() .getTargets() diff --git a/Mage.Sets/src/mage/cards/p/PawpatchRecruit.java b/Mage.Sets/src/mage/cards/p/PawpatchRecruit.java index 0a72089951c..68b1b9ac44e 100644 --- a/Mage.Sets/src/mage/cards/p/PawpatchRecruit.java +++ b/Mage.Sets/src/mage/cards/p/PawpatchRecruit.java @@ -96,13 +96,10 @@ class PawpatchRecruitTriggeredAbility extends TriggeredAbilityImpl { if (permanent == null || !filterTarget.match(permanent, getControllerId(), this, game)) { return false; } - StackObject targetingObject = CardUtil.getTargetingStackObject(this.getId().toString(), event, game); + StackObject targetingObject = CardUtil.findTargetingStackObject(this.getId().toString(), event, game); if (targetingObject == null || !filterStack.match(targetingObject, getControllerId(), this, game)) { return false; } - if (CardUtil.checkTargetedEventAlreadyUsed(this.getId().toString(), targetingObject, event, game)) { - return false; - } this.getTargets().clear(); FilterControlledPermanent filter = new FilterControlledCreaturePermanent(); filter.add(Predicates.not(new MageObjectReferencePredicate(event.getTargetId(), game))); diff --git a/Mage.Sets/src/mage/cards/s/SurrakElusiveHunter.java b/Mage.Sets/src/mage/cards/s/SurrakElusiveHunter.java index 42b189c9e3c..074aa4a8807 100644 --- a/Mage.Sets/src/mage/cards/s/SurrakElusiveHunter.java +++ b/Mage.Sets/src/mage/cards/s/SurrakElusiveHunter.java @@ -90,9 +90,7 @@ class SurrakElusiveHunterTriggeredAbility extends TriggeredAbilityImpl { if (!checkTargeted(event.getTargetId(), game)) { return false; } - StackObject targetingObject = CardUtil.getTargetingStackObject(this.getId().toString(), event, game); - return targetingObject != null - && game.getOpponents(getControllerId()).contains(targetingObject.getControllerId()) - && !CardUtil.checkTargetedEventAlreadyUsed(this.getId().toString(), targetingObject, event, game); + StackObject targetingObject = CardUtil.findTargetingStackObject(this.getId().toString(), event, game); + return targetingObject != null && game.getOpponents(getControllerId()).contains(targetingObject.getControllerId()); } } diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java index 13377578b94..01816dc7446 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java @@ -70,13 +70,10 @@ public class BecomesTargetAnyTriggeredAbility extends TriggeredAbilityImpl { if (permanent == null || !filterTarget.match(permanent, getControllerId(), this, game)) { return false; } - StackObject targetingObject = CardUtil.getTargetingStackObject(this.getId().toString(), event, game); + StackObject targetingObject = CardUtil.findTargetingStackObject(this.getId().toString(), event, game); if (targetingObject == null || !filterStack.match(targetingObject, getControllerId(), this, game)) { return false; } - if (CardUtil.checkTargetedEventAlreadyUsed(this.getId().toString(), targetingObject, event, game)) { - return false; - } switch (setTargetPointer) { case PERMANENT: this.getAllEffects().setTargetPointer(new FixedTarget(permanent.getId(), game)); diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java index 2c4c8d13a85..54481419723 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java @@ -54,13 +54,10 @@ public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl if (enchantment == null || enchantment.getAttachedTo() == null || !event.getTargetId().equals(enchantment.getAttachedTo())) { return false; } - StackObject targetingObject = CardUtil.getTargetingStackObject(this.getId().toString(), event, game); + StackObject targetingObject = CardUtil.findTargetingStackObject(this.getId().toString(), event, game); if (targetingObject == null || !filter.match(targetingObject, getControllerId(), this, game)) { return false; } - if (CardUtil.checkTargetedEventAlreadyUsed(this.getId().toString(), targetingObject, event, game)) { - return false; - } switch (setTargetPointer) { case PLAYER: this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getControllerId(), game)); diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java index 9b60880a21d..c6948f53828 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java @@ -63,13 +63,10 @@ public class BecomesTargetControllerTriggeredAbility extends TriggeredAbilityImp return false; } } - StackObject targetingObject = CardUtil.getTargetingStackObject(this.getId().toString(), event, game); + StackObject targetingObject = CardUtil.findTargetingStackObject(this.getId().toString(), event, game); if (targetingObject == null || !filterStack.match(targetingObject, getControllerId(), this, game)) { return false; } - if (CardUtil.checkTargetedEventAlreadyUsed(this.getId().toString(), targetingObject, event, game)) { - return false; - } switch (setTargetPointer) { case SPELL: this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java index 0b410c7e078..0f61e332e7d 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java @@ -57,13 +57,10 @@ public class BecomesTargetSourceTriggeredAbility extends TriggeredAbilityImpl { if (!event.getTargetId().equals(getSourceId())) { return false; } - StackObject targetingObject = CardUtil.getTargetingStackObject(this.getId().toString(), event, game); + StackObject targetingObject = CardUtil.findTargetingStackObject(this.getId().toString(), event, game); if (targetingObject == null || !filter.match(targetingObject, getControllerId(), this, game)) { return false; } - if (CardUtil.checkTargetedEventAlreadyUsed(this.getId().toString(), targetingObject, event, game)) { - return false; - } switch (setTargetPointer) { case PLAYER: this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getControllerId(), game)); diff --git a/Mage/src/main/java/mage/abilities/keyword/WardAbility.java b/Mage/src/main/java/mage/abilities/keyword/WardAbility.java index c68ff475567..01db3372ce5 100644 --- a/Mage/src/main/java/mage/abilities/keyword/WardAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/WardAbility.java @@ -77,13 +77,10 @@ public class WardAbility extends TriggeredAbilityImpl { if (!getSourceId().equals(event.getTargetId())) { return false; } - StackObject targetingObject = CardUtil.getTargetingStackObject(this.getId().toString(), event, game); + StackObject targetingObject = CardUtil.findTargetingStackObject(this.getId().toString(), event, game); if (targetingObject == null || !game.getOpponents(getControllerId()).contains(targetingObject.getControllerId())) { return false; } - if (CardUtil.checkTargetedEventAlreadyUsed(this.getId().toString(), targetingObject, event, game)) { - return false; - } getEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); return true; } diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index eb2a2e8d513..e7bb1732f4d 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1128,14 +1128,19 @@ public final class CardUtil { /** * For finding the spell or ability on the stack for "becomes the target" triggers. - * + * Also ensures that spells/abilities that target the same object twice only trigger each "becomes the target" ability once. + * If this is the first attempt at triggering for a given ability targeting a given object, + * this method records that in the game state for later checks by this same method, to not return the same object again. + * + * @param checkingReference must be unique for each usage (this.getId().toString() of the TriggeredAbility, or this.getKey() of the watcher) * @param event the GameEvent.EventType.TARGETED from checkTrigger() or watch() * @param game the Game from checkTrigger() or watch() - * @return the StackObject which targeted the source, or null if not found + * @return the StackObject which targeted the source, or null if already used or not found */ - public static StackObject getTargetingStackObject(String checkingReference, GameEvent event, Game game) { + public static StackObject findTargetingStackObject(String checkingReference, GameEvent event, Game game) { // In case of multiple simultaneous triggered abilities from the same source, - // need to get the actual one that targeted, see #8026, #8378 + // need to get the actual one that targeted, see #8026, #8378, rulings for Battle Mammoth + // In case of copied triggered abilities, need to trigger on each independently, see #13498 // Also avoids triggering on cancelled selections, see #8802 String stateKey = "targetedMap" + checkingReference; Map> targetMap = (Map>) game.getState().getValue(stateKey); @@ -1148,50 +1153,22 @@ public final class CardUtil { Set targetingObjects = targetMap.computeIfAbsent(event.getTargetId(), k -> new HashSet<>()); for (StackObject stackObject : game.getStack()) { Ability stackAbility = stackObject.getStackAbility(); - if (stackAbility == null || !stackAbility.getSourceId().equals(event.getSourceId()) || targetingObjects.contains(stackObject.getId())) { + if (stackAbility == null || !stackAbility.getSourceId().equals(event.getSourceId())) { continue; } if (CardUtil.getAllSelectedTargets(stackAbility, game).contains(event.getTargetId())) { + if (!targetingObjects.add(stackObject.getId())) { + continue; // The trigger/watcher already recorded that target of the stack object, check for another + } + // Otherwise, store this combination of trigger/watcher + target + stack object + targetMap.put(event.getTargetId(), targetingObjects); + game.getState().setValue(stateKey, targetMap); return stackObject; } } return null; } - /** - * For ensuring that spells/abilities that target the same object twice only trigger each "becomes the target" ability once. - * If this is the first attempt at triggering for a given ability targeting a given object, - * this method records that in the game state for later checks by this same method. - * - * @param checkingReference must be unique for each usage (this.id.toString() of the TriggeredAbility, or this.getKey() of the watcher) - * @param targetingObject from getTargetingStackObject - * @param event the GameEvent.EventType.TARGETED from checkTrigger() or watch() - * @param game the Game from checkTrigger() or watch() - * @return true if already triggered/watched, false if this is the first/only trigger/watch - */ - public static boolean checkTargetedEventAlreadyUsed(String checkingReference, StackObject targetingObject, GameEvent event, Game game) { - String stateKey = "targetedMap" + checkingReference; - // If a spell or ability an opponent controls targets a single permanent you control more than once, - // Battle Mammoth's triggered ability will trigger only once. - // However, if a spell or ability an opponent controls targets multiple permanents you control, - // Battle Mammoth's triggered ability will trigger once for each of those permanents. (2021-02-05) - Map> targetMap = (Map>) game.getState().getValue(stateKey); - // targetMap: key - targetId; value - Set of stackObject Ids - if (targetMap == null) { - targetMap = new HashMap<>(); - } else { - targetMap = new HashMap<>(targetMap); // must have new object reference if saved back to game state - } - Set targetingObjects = targetMap.computeIfAbsent(event.getTargetId(), k -> new HashSet<>()); - if (!targetingObjects.add(targetingObject.getId())) { - return true; // The trigger/watcher already recorded that target of the stack object - } - // Otherwise, store this combination of trigger/watcher + target + stack object - targetMap.put(event.getTargetId(), targetingObjects); - game.getState().setValue(stateKey, targetMap); - return false; - } - /** * For overriding `canTarget()` with usages such as "any number of target cards with total mana value X or less". * Call this after super.canTarget() returns true. diff --git a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java index accc6b4628c..b8e031bca49 100644 --- a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java @@ -29,8 +29,8 @@ public class NumberOfTimesPermanentTargetedATurnWatcher extends Watcher { if (event.getType() != GameEvent.EventType.TARGETED) { return; } - StackObject targetingObject = CardUtil.getTargetingStackObject(this.getKey(), event, game); - if (targetingObject == null || CardUtil.checkTargetedEventAlreadyUsed(this.getKey(), targetingObject, event, game)) { + StackObject targetingObject = CardUtil.findTargetingStackObject(this.getKey(), event, game); + if (targetingObject == null) { return; } Permanent permanent = game.getPermanent(event.getTargetId()); -- 2.47.2 From b32fd1b9d71c76e9c5cbe295c180249e889d6c5b Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Mon, 14 Apr 2025 16:25:53 -0500 Subject: [PATCH 39/95] Rename Universes Within cards Update test referencing Forge, Neverwinter Charlatan [STX] Implement Bohn, Beguiling Balladeer [STX] Implement Casal, Lurkwood Pathfinder [STX] Implement Themberchaud [STX] Implement Jurin, Leading the Charge [STX] Implement Evin, Waterdeep Opportunist [STX] Implement Mathise, Surge Channeler [STX] Implement Rashel, Fist of Torm --- .../BohnBeguilingBalladeer.java} | 12 +++++------ .../CasalLurkwoodPathfinder.java} | 14 ++++++------- .../CasalPathbreakerOwlbear.java} | 12 +++++------ .../EvinWaterdeepOpportunist.java} | 12 +++++------ .../JurinLeadingTheCharge.java} | 12 +++++------ .../MathiseSurgeChanneler.java} | 21 ++++++++----------- .../RashelFistOfTorm.java} | 12 +++++------ Mage.Sets/src/mage/sets/SecretLairDrop.java | 14 ++++++------- Mage.Sets/src/mage/sets/UniversesWithin.java | 8 +++++++ .../dies/SacrificeDiesTriggerTest.java | 10 ++++----- 10 files changed, 66 insertions(+), 61 deletions(-) rename Mage.Sets/src/mage/cards/{e/EdginLarcenousLutenist.java => b/BohnBeguilingBalladeer.java} (94%) rename Mage.Sets/src/mage/cards/{d/DoricNaturesWarden.java => c/CasalLurkwoodPathfinder.java} (83%) rename Mage.Sets/src/mage/cards/{d/DoricOwlbearAvenger.java => c/CasalPathbreakerOwlbear.java} (86%) rename Mage.Sets/src/mage/cards/{f/ForgeNeverwinterCharlatan.java => e/EvinWaterdeepOpportunist.java} (88%) rename Mage.Sets/src/mage/cards/{h/HolgaRelentlessRager.java => j/JurinLeadingTheCharge.java} (91%) rename Mage.Sets/src/mage/cards/{s/SimonWildMagicSorcerer.java => m/MathiseSurgeChanneler.java} (79%) rename Mage.Sets/src/mage/cards/{x/XenkPaladinUnbroken.java => r/RashelFistOfTorm.java} (80%) diff --git a/Mage.Sets/src/mage/cards/e/EdginLarcenousLutenist.java b/Mage.Sets/src/mage/cards/b/BohnBeguilingBalladeer.java similarity index 94% rename from Mage.Sets/src/mage/cards/e/EdginLarcenousLutenist.java rename to Mage.Sets/src/mage/cards/b/BohnBeguilingBalladeer.java index ada220bef82..2bae253674d 100644 --- a/Mage.Sets/src/mage/cards/e/EdginLarcenousLutenist.java +++ b/Mage.Sets/src/mage/cards/b/BohnBeguilingBalladeer.java @@ -1,4 +1,4 @@ -package mage.cards.e; +package mage.cards.b; import mage.MageInt; import mage.abilities.Ability; @@ -22,9 +22,9 @@ import java.util.UUID; /** * @author TheElk801, jeffwadsworth */ -public final class EdginLarcenousLutenist extends CardImpl { +public final class BohnBeguilingBalladeer extends CardImpl { - public EdginLarcenousLutenist(UUID ownerId, CardSetInfo setInfo) { + public BohnBeguilingBalladeer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); this.supertype.add(SuperType.LEGENDARY); @@ -42,13 +42,13 @@ public final class EdginLarcenousLutenist extends CardImpl { this.addAbility(ability); } - private EdginLarcenousLutenist(final EdginLarcenousLutenist card) { + private BohnBeguilingBalladeer(final BohnBeguilingBalladeer card) { super(card); } @Override - public EdginLarcenousLutenist copy() { - return new EdginLarcenousLutenist(this); + public BohnBeguilingBalladeer copy() { + return new BohnBeguilingBalladeer(this); } } diff --git a/Mage.Sets/src/mage/cards/d/DoricNaturesWarden.java b/Mage.Sets/src/mage/cards/c/CasalLurkwoodPathfinder.java similarity index 83% rename from Mage.Sets/src/mage/cards/d/DoricNaturesWarden.java rename to Mage.Sets/src/mage/cards/c/CasalLurkwoodPathfinder.java index f165e05f96b..27e9538d02f 100644 --- a/Mage.Sets/src/mage/cards/d/DoricNaturesWarden.java +++ b/Mage.Sets/src/mage/cards/c/CasalLurkwoodPathfinder.java @@ -1,4 +1,4 @@ -package mage.cards.d; +package mage.cards.c; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; @@ -22,7 +22,7 @@ import java.util.UUID; /** * @author TheElk801 */ -public final class DoricNaturesWarden extends CardImpl { +public final class CasalLurkwoodPathfinder extends CardImpl { private static final FilterLandCard filter = new FilterLandCard("Forest card"); @@ -30,7 +30,7 @@ public final class DoricNaturesWarden extends CardImpl { filter.add(SubType.FOREST.getPredicate()); } - public DoricNaturesWarden(UUID ownerId, CardSetInfo setInfo) { + public CasalLurkwoodPathfinder(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); this.supertype.add(SuperType.LEGENDARY); @@ -38,7 +38,7 @@ public final class DoricNaturesWarden extends CardImpl { this.subtype.add(SubType.DRUID); this.power = new MageInt(3); this.toughness = new MageInt(3); - this.secondSideCardClazz = mage.cards.d.DoricOwlbearAvenger.class; + this.secondSideCardClazz = CasalPathbreakerOwlbear.class; // Vigilance this.addAbility(VigilanceAbility.getInstance()); @@ -55,12 +55,12 @@ public final class DoricNaturesWarden extends CardImpl { ))); } - private DoricNaturesWarden(final DoricNaturesWarden card) { + private CasalLurkwoodPathfinder(final CasalLurkwoodPathfinder card) { super(card); } @Override - public DoricNaturesWarden copy() { - return new DoricNaturesWarden(this); + public CasalLurkwoodPathfinder copy() { + return new CasalLurkwoodPathfinder(this); } } diff --git a/Mage.Sets/src/mage/cards/d/DoricOwlbearAvenger.java b/Mage.Sets/src/mage/cards/c/CasalPathbreakerOwlbear.java similarity index 86% rename from Mage.Sets/src/mage/cards/d/DoricOwlbearAvenger.java rename to Mage.Sets/src/mage/cards/c/CasalPathbreakerOwlbear.java index d0b2a139437..6d62625d256 100644 --- a/Mage.Sets/src/mage/cards/d/DoricOwlbearAvenger.java +++ b/Mage.Sets/src/mage/cards/c/CasalPathbreakerOwlbear.java @@ -1,4 +1,4 @@ -package mage.cards.d; +package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; @@ -19,7 +19,7 @@ import java.util.UUID; /** * @author TheElk801 */ -public final class DoricOwlbearAvenger extends CardImpl { +public final class CasalPathbreakerOwlbear extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); @@ -27,7 +27,7 @@ public final class DoricOwlbearAvenger extends CardImpl { filter.add(SuperType.LEGENDARY.getPredicate()); } - public DoricOwlbearAvenger(UUID ownerId, CardSetInfo setInfo) { + public CasalPathbreakerOwlbear(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.supertype.add(SuperType.LEGENDARY); @@ -57,12 +57,12 @@ public final class DoricOwlbearAvenger extends CardImpl { this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect())); } - private DoricOwlbearAvenger(final DoricOwlbearAvenger card) { + private CasalPathbreakerOwlbear(final CasalPathbreakerOwlbear card) { super(card); } @Override - public DoricOwlbearAvenger copy() { - return new DoricOwlbearAvenger(this); + public CasalPathbreakerOwlbear copy() { + return new CasalPathbreakerOwlbear(this); } } diff --git a/Mage.Sets/src/mage/cards/f/ForgeNeverwinterCharlatan.java b/Mage.Sets/src/mage/cards/e/EvinWaterdeepOpportunist.java similarity index 88% rename from Mage.Sets/src/mage/cards/f/ForgeNeverwinterCharlatan.java rename to Mage.Sets/src/mage/cards/e/EvinWaterdeepOpportunist.java index 19279bf8c0a..79ecc5d9f5b 100644 --- a/Mage.Sets/src/mage/cards/f/ForgeNeverwinterCharlatan.java +++ b/Mage.Sets/src/mage/cards/e/EvinWaterdeepOpportunist.java @@ -1,4 +1,4 @@ -package mage.cards.f; +package mage.cards.e; import mage.MageInt; import mage.abilities.common.SacrificeOneOrMorePermanentsTriggeredAbility; @@ -26,13 +26,13 @@ import java.util.UUID; /** * @author TheElk801 */ -public final class ForgeNeverwinterCharlatan extends CardImpl { +public final class EvinWaterdeepOpportunist extends CardImpl { private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.TREASURE)); private static final DynamicValue twiceXValue = new MultipliedValue(xValue, 2); private static final Hint hint = new ValueHint("Treasures you control", xValue); - public ForgeNeverwinterCharlatan(UUID ownerId, CardSetInfo setInfo) { + public EvinWaterdeepOpportunist(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.supertype.add(SuperType.LEGENDARY); @@ -60,12 +60,12 @@ public final class ForgeNeverwinterCharlatan extends CardImpl { ).setTriggersLimitEachTurn(1)); } - private ForgeNeverwinterCharlatan(final ForgeNeverwinterCharlatan card) { + private EvinWaterdeepOpportunist(final EvinWaterdeepOpportunist card) { super(card); } @Override - public ForgeNeverwinterCharlatan copy() { - return new ForgeNeverwinterCharlatan(this); + public EvinWaterdeepOpportunist copy() { + return new EvinWaterdeepOpportunist(this); } } diff --git a/Mage.Sets/src/mage/cards/h/HolgaRelentlessRager.java b/Mage.Sets/src/mage/cards/j/JurinLeadingTheCharge.java similarity index 91% rename from Mage.Sets/src/mage/cards/h/HolgaRelentlessRager.java rename to Mage.Sets/src/mage/cards/j/JurinLeadingTheCharge.java index f1f2c53089f..28f712cba0b 100644 --- a/Mage.Sets/src/mage/cards/h/HolgaRelentlessRager.java +++ b/Mage.Sets/src/mage/cards/j/JurinLeadingTheCharge.java @@ -1,4 +1,4 @@ -package mage.cards.h; +package mage.cards.j; import mage.MageInt; import mage.abilities.Ability; @@ -24,9 +24,9 @@ import java.util.UUID; /** * @author TheElk801 */ -public final class HolgaRelentlessRager extends CardImpl { +public final class JurinLeadingTheCharge extends CardImpl { - public HolgaRelentlessRager(UUID ownerId, CardSetInfo setInfo) { + public JurinLeadingTheCharge(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); this.supertype.add(SuperType.LEGENDARY); @@ -45,13 +45,13 @@ public final class HolgaRelentlessRager extends CardImpl { this.addAbility(new AttacksTriggeredAbility(new HolgaRelentlessRagerEffect())); } - private HolgaRelentlessRager(final HolgaRelentlessRager card) { + private JurinLeadingTheCharge(final JurinLeadingTheCharge card) { super(card); } @Override - public HolgaRelentlessRager copy() { - return new HolgaRelentlessRager(this); + public JurinLeadingTheCharge copy() { + return new JurinLeadingTheCharge(this); } } diff --git a/Mage.Sets/src/mage/cards/s/SimonWildMagicSorcerer.java b/Mage.Sets/src/mage/cards/m/MathiseSurgeChanneler.java similarity index 79% rename from Mage.Sets/src/mage/cards/s/SimonWildMagicSorcerer.java rename to Mage.Sets/src/mage/cards/m/MathiseSurgeChanneler.java index 649581c65d7..8afdaa4a4a4 100644 --- a/Mage.Sets/src/mage/cards/s/SimonWildMagicSorcerer.java +++ b/Mage.Sets/src/mage/cards/m/MathiseSurgeChanneler.java @@ -1,4 +1,4 @@ -package mage.cards.s; +package mage.cards.m; import mage.MageInt; import mage.abilities.common.SpellCastControllerTriggeredAbility; @@ -8,10 +8,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.RollDieWithResultTableEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.constants.*; import mage.filter.FilterSpell; import mage.filter.common.FilterInstantOrSorcerySpell; import mage.filter.predicate.mageobject.ManaValuePredicate; @@ -21,7 +18,7 @@ import java.util.UUID; /** * @author TheElk801 */ -public final class SimonWildMagicSorcerer extends CardImpl { +public final class MathiseSurgeChanneler extends CardImpl { private static final FilterSpell filter = new FilterInstantOrSorcerySpell("an instant or sorcery spell with mana value 3 or greater"); @@ -29,7 +26,7 @@ public final class SimonWildMagicSorcerer extends CardImpl { filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 2)); } - public SimonWildMagicSorcerer(UUID ownerId, CardSetInfo setInfo) { + public MathiseSurgeChanneler(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.supertype.add(SuperType.LEGENDARY); @@ -41,7 +38,7 @@ public final class SimonWildMagicSorcerer extends CardImpl { // Whenever you cast an instant or sorcery spell with mana value 3 or greater, roll a d20. RollDieWithResultTableEffect effect = new RollDieWithResultTableEffect(20); - this.addAbility(new SpellCastControllerTriggeredAbility(effect, filter, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(effect, filter, false, SetTargetPointer.SPELL)); // 1-9 | Each player draws a card. effect.addTableEntry(1, 9, new DrawCardAllEffect(1)); @@ -50,15 +47,15 @@ public final class SimonWildMagicSorcerer extends CardImpl { effect.addTableEntry(10, 19, new DrawCardSourceControllerEffect(1, true)); // 20 | Copy that spell. You may choose new targets for the copy. - effect.addTableEntry(20, 20, new CopyTargetStackObjectEffect()); + effect.addTableEntry(20, 20, new CopyTargetStackObjectEffect(true)); } - private SimonWildMagicSorcerer(final SimonWildMagicSorcerer card) { + private MathiseSurgeChanneler(final MathiseSurgeChanneler card) { super(card); } @Override - public SimonWildMagicSorcerer copy() { - return new SimonWildMagicSorcerer(this); + public MathiseSurgeChanneler copy() { + return new MathiseSurgeChanneler(this); } } diff --git a/Mage.Sets/src/mage/cards/x/XenkPaladinUnbroken.java b/Mage.Sets/src/mage/cards/r/RashelFistOfTorm.java similarity index 80% rename from Mage.Sets/src/mage/cards/x/XenkPaladinUnbroken.java rename to Mage.Sets/src/mage/cards/r/RashelFistOfTorm.java index 1cf41ef1534..8b8bf86bc6e 100644 --- a/Mage.Sets/src/mage/cards/x/XenkPaladinUnbroken.java +++ b/Mage.Sets/src/mage/cards/r/RashelFistOfTorm.java @@ -1,4 +1,4 @@ -package mage.cards.x; +package mage.cards.r; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; @@ -18,11 +18,11 @@ import java.util.UUID; /** * @author TheElk801 */ -public final class XenkPaladinUnbroken extends CardImpl { +public final class RashelFistOfTorm extends CardImpl { private static final FilterPermanent filter = new FilterPermanent(SubType.AURA, "Auras"); - public XenkPaladinUnbroken(UUID ownerId, CardSetInfo setInfo) { + public RashelFistOfTorm(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); this.supertype.add(SuperType.LEGENDARY); @@ -40,12 +40,12 @@ public final class XenkPaladinUnbroken extends CardImpl { ))); } - private XenkPaladinUnbroken(final XenkPaladinUnbroken card) { + private RashelFistOfTorm(final RashelFistOfTorm card) { super(card); } @Override - public XenkPaladinUnbroken copy() { - return new XenkPaladinUnbroken(this); + public RashelFistOfTorm copy() { + return new RashelFistOfTorm(this); } } diff --git a/Mage.Sets/src/mage/sets/SecretLairDrop.java b/Mage.Sets/src/mage/sets/SecretLairDrop.java index 63423a26ba4..9c3a49d6b7c 100644 --- a/Mage.Sets/src/mage/sets/SecretLairDrop.java +++ b/Mage.Sets/src/mage/sets/SecretLairDrop.java @@ -1085,13 +1085,13 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Azusa, Lost but Seeking", 1234, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Teysa Karlov", 1235, Rarity.RARE, mage.cards.t.TeysaKarlov.class)); cards.add(new SetCardInfo("Paradise Mantle", 1236, Rarity.RARE, mage.cards.p.ParadiseMantle.class)); - cards.add(new SetCardInfo("Xenk, Paladin Unbroken", 1237, Rarity.RARE, mage.cards.x.XenkPaladinUnbroken.class)); - cards.add(new SetCardInfo("Simon, Wild Magic Sorcerer", 1238, Rarity.RARE, mage.cards.s.SimonWildMagicSorcerer.class)); - cards.add(new SetCardInfo("Forge, Neverwinter Charlatan", 1239, Rarity.RARE, mage.cards.f.ForgeNeverwinterCharlatan.class)); - cards.add(new SetCardInfo("Holga, Relentless Rager", 1240, Rarity.RARE, mage.cards.h.HolgaRelentlessRager.class)); - cards.add(new SetCardInfo("Doric, Nature's Warden", 1241, Rarity.RARE, mage.cards.d.DoricNaturesWarden.class)); - cards.add(new SetCardInfo("Doric, Owlbear Avenger", 1241, Rarity.RARE, mage.cards.d.DoricOwlbearAvenger.class)); - cards.add(new SetCardInfo("Edgin, Larcenous Lutenist", 1242, Rarity.RARE, mage.cards.e.EdginLarcenousLutenist.class)); + cards.add(new SetCardInfo("Rashel, Fist of Torm", 1237, Rarity.RARE, mage.cards.r.RashelFistOfTorm.class)); + cards.add(new SetCardInfo("Mathise, Surge Channeler", 1238, Rarity.RARE, mage.cards.m.MathiseSurgeChanneler.class)); + cards.add(new SetCardInfo("Evin, Waterdeep Opportunist", 1239, Rarity.RARE, mage.cards.e.EvinWaterdeepOpportunist.class)); + cards.add(new SetCardInfo("Jurin, Leading the Charge", 1240, Rarity.RARE, mage.cards.j.JurinLeadingTheCharge.class)); + cards.add(new SetCardInfo("Casal, Lurkwood Pathfinder", 1241, Rarity.RARE, mage.cards.c.CasalLurkwoodPathfinder.class)); + cards.add(new SetCardInfo("Casal, Pathbreaker Owlbear", 1241, Rarity.RARE, mage.cards.c.CasalPathbreakerOwlbear.class)); + cards.add(new SetCardInfo("Bohn, Beguiling Balladeer", 1242, Rarity.RARE, mage.cards.b.BohnBeguilingBalladeer.class)); cards.add(new SetCardInfo("Ugin, the Ineffable", 1243, Rarity.RARE, mage.cards.u.UginTheIneffable.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sorin, Imperious Bloodlord", 1244, Rarity.MYTHIC, mage.cards.s.SorinImperiousBloodlord.class)); cards.add(new SetCardInfo("Sarkhan, Dragonsoul", 1245, Rarity.MYTHIC, mage.cards.s.SarkhanDragonsoul.class)); diff --git a/Mage.Sets/src/mage/sets/UniversesWithin.java b/Mage.Sets/src/mage/sets/UniversesWithin.java index 1e748c59444..4c79b8ba2de 100644 --- a/Mage.Sets/src/mage/sets/UniversesWithin.java +++ b/Mage.Sets/src/mage/sets/UniversesWithin.java @@ -23,10 +23,14 @@ public final class UniversesWithin extends ExpansionSet { cards.add(new SetCardInfo("Aisha of Sparks and Smoke", 12, Rarity.RARE, mage.cards.a.AishaOfSparksAndSmoke.class)); cards.add(new SetCardInfo("Arvinox, the Mind Flail", 1, Rarity.MYTHIC, mage.cards.a.ArvinoxTheMindFlail.class)); cards.add(new SetCardInfo("Baldin, Century Herdmaster", 10, Rarity.RARE, mage.cards.b.BaldinCenturyHerdmaster.class)); + cards.add(new SetCardInfo("Bohn, Beguiling Balladeer", 30, Rarity.RARE, mage.cards.b.BohnBeguilingBalladeer.class)); cards.add(new SetCardInfo("Bjorna, Nightfall Alchemist", 2, Rarity.RARE, mage.cards.b.BjornaNightfallAlchemist.class)); + cards.add(new SetCardInfo("Casal, Lurkwood Pathfinder", 29, Rarity.RARE, mage.cards.c.CasalLurkwoodPathfinder.class)); + cards.add(new SetCardInfo("Casal, Pathbreaker Owlbear", 29, Rarity.RARE, mage.cards.c.CasalPathbreakerOwlbear.class)); cards.add(new SetCardInfo("Cecily, Haunted Mage", 3, Rarity.RARE, mage.cards.c.CecilyHauntedMage.class)); cards.add(new SetCardInfo("Elmar, Ulvenwald Informant", 4, Rarity.RARE, mage.cards.e.ElmarUlvenwaldInformant.class)); cards.add(new SetCardInfo("Enkira, Hostile Scavenger", 20, Rarity.MYTHIC, mage.cards.e.EnkiraHostileScavenger.class)); + cards.add(new SetCardInfo("Evin, Waterdeep Opportunist", 26, Rarity.RARE, mage.cards.e.EvinWaterdeepOpportunist.class)); cards.add(new SetCardInfo("Gisa's Favorite Shovel", 19, Rarity.MYTHIC, mage.cards.g.GisasFavoriteShovel.class)); cards.add(new SetCardInfo("Gregor, Shrewd Magistrate", 21, Rarity.MYTHIC, mage.cards.g.GregorShrewdMagistrate.class)); cards.add(new SetCardInfo("Greymond, Avacyn's Stalwart", 18, Rarity.MYTHIC, mage.cards.g.GreymondAvacynsStalwart.class)); @@ -35,11 +39,15 @@ public final class UniversesWithin extends ExpansionSet { cards.add(new SetCardInfo("Havengul Laboratory", 9, Rarity.RARE, mage.cards.h.HavengulLaboratory.class)); cards.add(new SetCardInfo("Havengul Mystery", 9, Rarity.RARE, mage.cards.h.HavengulMystery.class)); cards.add(new SetCardInfo("Immard, the Stormcleaver", 14, Rarity.RARE, mage.cards.i.ImmardTheStormcleaver.class)); + cards.add(new SetCardInfo("Jurin, Leading the Charge", 27, Rarity.RARE, mage.cards.j.JurinLeadingTheCharge.class)); cards.add(new SetCardInfo("Maarika, Brutal Gladiator", 15, Rarity.RARE, mage.cards.m.MaarikaBrutalGladiator.class)); cards.add(new SetCardInfo("Malik, Grim Manipulator", 23, Rarity.MYTHIC, mage.cards.m.MalikGrimManipulator.class)); + cards.add(new SetCardInfo("Mathise, Surge Channeler", 25, Rarity.RARE, mage.cards.m.MathiseSurgeChanneler.class)); cards.add(new SetCardInfo("Othelm, Sigardian Outcast", 6, Rarity.RARE, mage.cards.o.OthelmSigardianOutcast.class)); + cards.add(new SetCardInfo("Rashel, Fist of Torm", 24, Rarity.RARE, mage.cards.r.RashelFistOfTorm.class)); cards.add(new SetCardInfo("Sophina, Spearsage Deserter", 7, Rarity.RARE, mage.cards.s.SophinaSpearsageDeserter.class)); cards.add(new SetCardInfo("Tadeas, Juniper Ascendant", 16, Rarity.RARE, mage.cards.t.TadeasJuniperAscendant.class)); + cards.add(new SetCardInfo("Themberchaud", 28, Rarity.RARE, mage.cards.t.Themberchaud.class)); cards.add(new SetCardInfo("The Howling Abomination", 13, Rarity.RARE, mage.cards.t.TheHowlingAbomination.class)); cards.add(new SetCardInfo("Vikya, Scorching Stalwart", 11, Rarity.RARE, mage.cards.v.VikyaScorchingStalwart.class)); cards.add(new SetCardInfo("Wernog, Rider's Chaplain", 8, Rarity.RARE, mage.cards.w.WernogRidersChaplain.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/SacrificeDiesTriggerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/SacrificeDiesTriggerTest.java index 82b1912c671..9cfdfc2c9f7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/SacrificeDiesTriggerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/SacrificeDiesTriggerTest.java @@ -491,10 +491,10 @@ public class SacrificeDiesTriggerTest extends CardTestPlayerBase { } @Test - public void test_BatchEvent_ForgeNeverwinterCharlatan_SacrificeAnother() { + public void test_BatchEvent_EvinWaterdeepOpportunist_SacrificeAnother() { // Whenever one or more players sacrifice one or more creatures, you create a tapped Treasure token. // This ability triggers only once each turn. - addCard(Zone.BATTLEFIELD, playerA, "Forge, Neverwinter Charlatan"); + addCard(Zone.BATTLEFIELD, playerA, "Evin, Waterdeep Opportunist"); // // {2}, {T}, Sacrifice a creature: Draw a card. addCard(Zone.BATTLEFIELD, playerA, "Phyrexian Vault", 1); @@ -514,10 +514,10 @@ public class SacrificeDiesTriggerTest extends CardTestPlayerBase { } @Test - public void test_BatchEvent_ForgeNeverwinterCharlatan_SacrificeItself() { + public void test_BatchEvent_EvinWaterdeepOpportunist_SacrificeItself() { // Whenever one or more players sacrifice one or more creatures, you create a tapped Treasure token. // This ability triggers only once each turn. - addCard(Zone.BATTLEFIELD, playerA, "Forge, Neverwinter Charlatan"); + addCard(Zone.BATTLEFIELD, playerA, "Evin, Waterdeep Opportunist"); // // {2}, {T}, Sacrifice a creature: Draw a card. addCard(Zone.BATTLEFIELD, playerA, "Phyrexian Vault", 1); @@ -525,7 +525,7 @@ public class SacrificeDiesTriggerTest extends CardTestPlayerBase { // sacrifice itself activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}, Sacrifice"); - setChoice(playerA, "Forge, Neverwinter Charlatan"); // to sacrifice + setChoice(playerA, "Evin, Waterdeep Opportunist"); // to sacrifice setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); -- 2.47.2 From 3885e72aad1438792e1255de70315d543158d43f Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Mon, 14 Apr 2025 16:58:40 -0500 Subject: [PATCH 40/95] Fix Assimilation Aegis * Token copies were not able to select a creature from exile to copy --- Mage.Sets/src/mage/cards/a/AssimilationAegis.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/a/AssimilationAegis.java b/Mage.Sets/src/mage/cards/a/AssimilationAegis.java index 7278b40b107..aedcca1fbf7 100644 --- a/Mage.Sets/src/mage/cards/a/AssimilationAegis.java +++ b/Mage.Sets/src/mage/cards/a/AssimilationAegis.java @@ -88,7 +88,7 @@ class AssimilationAegisEffect extends OneShotEffect { if (player == null) { return false; } - UUID exileId = CardUtil.getExileZoneId(game, source); + UUID exileId = CardUtil.getCardExileZoneId(game, source); TargetCard target = new TargetCardInExile(StaticFilters.FILTER_CARD_CREATURE, exileId); target.withNotTarget(true); if (!target.choose(Outcome.Benefit, player.getId(), source.getId(), source, game)) { -- 2.47.2 From 6bf8aedce76b81f98404f369aa197ad87def1201 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Mon, 14 Apr 2025 17:02:48 -0500 Subject: [PATCH 41/95] Fix Adventure/Omen Permanent View * Adventure/Omen Permanents no longer show rule text from the spell ability * Updated CardView variable for SpellOptionCard --- Mage.Common/src/main/java/mage/view/CardView.java | 14 +++++++------- .../src/main/java/mage/view/PermanentView.java | 7 +++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index 84d55c232fb..a516227d2e7 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -439,14 +439,14 @@ public class CardView extends SimpleCardView { 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(); + SpellOptionCard spellOptionCard = mainCard.getSpellCard(); + rightSplitName = spellOptionCard.getName(); + rightSplitCostsStr = String.join("", spellOptionCard.getManaCostSymbols()); + rightSplitRules = spellOptionCard.getRules(game); + rightSplitTypeLine = getCardTypeLine(game, spellOptionCard); + fullCardName = mainCard.getName() + MockCard.CARD_WITH_SPELL_OPTION_NAME_SEPARATOR + spellOptionCard.getName(); this.manaCostLeftStr = mainCard.getManaCostSymbols(); - this.manaCostRightStr = splitCardSpell.getManaCostSymbols(); + this.manaCostRightStr = spellOptionCard.getManaCostSymbols(); } else if (card instanceof MockCard) { // deck editor cards fullCardName = ((MockCard) card).getFullName(true); diff --git a/Mage.Common/src/main/java/mage/view/PermanentView.java b/Mage.Common/src/main/java/mage/view/PermanentView.java index 6d3ef741d93..09fc5fc37b0 100644 --- a/Mage.Common/src/main/java/mage/view/PermanentView.java +++ b/Mage.Common/src/main/java/mage/view/PermanentView.java @@ -1,6 +1,7 @@ package mage.view; import mage.cards.Card; +import mage.cards.CardWithSpellOption; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; @@ -79,6 +80,12 @@ public class PermanentView extends CardView { this.alternateName = original.getName(); } } + + // for adventure/omen cards, remove spell card rules + if (card instanceof CardWithSpellOption) { + this.rules.removeIf(rule -> rule.startsWith("Adventure") || rule.startsWith("Omen")); + } + if (permanent.getOwnerId() != null && !permanent.getOwnerId().equals(permanent.getControllerId())) { Player owner = game.getPlayer(permanent.getOwnerId()); if (owner != null) { -- 2.47.2 From 72ff560fb5faaacae5519433af122a4862639e01 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Mon, 14 Apr 2025 23:02:58 -0500 Subject: [PATCH 42/95] [SLD] Add Missing Variants --- Mage.Sets/src/mage/sets/SecretLairDrop.java | 206 +++++++++++++++++--- 1 file changed, 184 insertions(+), 22 deletions(-) diff --git a/Mage.Sets/src/mage/sets/SecretLairDrop.java b/Mage.Sets/src/mage/sets/SecretLairDrop.java index 9c3a49d6b7c..7c82db22481 100644 --- a/Mage.Sets/src/mage/sets/SecretLairDrop.java +++ b/Mage.Sets/src/mage/sets/SecretLairDrop.java @@ -71,7 +71,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Swamp", 48, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 49, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Forest", 50, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Captain Sisay", 51, Rarity.MYTHIC, mage.cards.c.CaptainSisay.class)); + cards.add(new SetCardInfo("Captain Sisay", 51, Rarity.MYTHIC, mage.cards.c.CaptainSisay.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Meren of Clan Nel Toth", 52, Rarity.MYTHIC, mage.cards.m.MerenOfClanNelToth.class)); cards.add(new SetCardInfo("Narset, Enlightened Master", 53, Rarity.MYTHIC, mage.cards.n.NarsetEnlightenedMaster.class)); cards.add(new SetCardInfo("Oona, Queen of the Fae", 54, Rarity.MYTHIC, mage.cards.o.OonaQueenOfTheFae.class)); @@ -111,7 +111,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Domri Rade", 88, Rarity.MYTHIC, mage.cards.d.DomriRade.class)); cards.add(new SetCardInfo("Tamiyo, Field Researcher", 89, Rarity.MYTHIC, mage.cards.t.TamiyoFieldResearcher.class)); cards.add(new SetCardInfo("Vraska, Golgari Queen", 90, Rarity.MYTHIC, mage.cards.v.VraskaGolgariQueen.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swan Song", 91, Rarity.RARE, mage.cards.s.SwanSong.class)); + cards.add(new SetCardInfo("Swan Song", 91, Rarity.RARE, mage.cards.s.SwanSong.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Birds of Paradise", 92, Rarity.RARE, mage.cards.b.BirdsOfParadise.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gilded Goose", 93, Rarity.RARE, mage.cards.g.GildedGoose.class)); cards.add(new SetCardInfo("Baleful Strix", 94, Rarity.RARE, mage.cards.b.BalefulStrix.class)); @@ -132,7 +132,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Forest", 109, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swords to Plowshares", 110, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Opt", 111, Rarity.RARE, mage.cards.o.Opt.class)); - cards.add(new SetCardInfo("Fatal Push", 112, Rarity.RARE, mage.cards.f.FatalPush.class)); + cards.add(new SetCardInfo("Fatal Push", 112, Rarity.RARE, mage.cards.f.FatalPush.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Anger of the Gods", 113, Rarity.RARE, mage.cards.a.AngerOfTheGods.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Explore", 114, Rarity.RARE, mage.cards.e.Explore.class)); cards.add(new SetCardInfo("Glen Elendra Archmage", 115, Rarity.RARE, mage.cards.g.GlenElendraArchmage.class)); @@ -214,7 +214,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Darksteel Ingot", 204, Rarity.RARE, mage.cards.d.DarksteelIngot.class)); cards.add(new SetCardInfo("Gilded Lotus", 205, Rarity.RARE, mage.cards.g.GildedLotus.class)); cards.add(new SetCardInfo("Exquisite Blood", 206, Rarity.RARE, mage.cards.e.ExquisiteBlood.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Night's Whisper", 207, Rarity.RARE, mage.cards.n.NightsWhisper.class)); + cards.add(new SetCardInfo("Night's Whisper", 207, Rarity.RARE, mage.cards.n.NightsWhisper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Phyrexian Tower", 208, Rarity.RARE, mage.cards.p.PhyrexianTower.class)); cards.add(new SetCardInfo("Elesh Norn, Grand Cenobite", 209, Rarity.MYTHIC, mage.cards.e.EleshNornGrandCenobite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jin-Gitaxias, Core Augur", 210, Rarity.MYTHIC, mage.cards.j.JinGitaxiasCoreAugur.class)); @@ -241,7 +241,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Liliana, Death's Majesty", 232, Rarity.MYTHIC, mage.cards.l.LilianaDeathsMajesty.class)); cards.add(new SetCardInfo("Rise of the Dark Realms", 233, Rarity.MYTHIC, mage.cards.r.RiseOfTheDarkRealms.class)); cards.add(new SetCardInfo("Brazen Borrower", 234, Rarity.MYTHIC, mage.cards.b.BrazenBorrower.class)); - cards.add(new SetCardInfo("Vindictive Lich", 235, Rarity.RARE, mage.cards.v.VindictiveLich.class)); + cards.add(new SetCardInfo("Vindictive Lich", 235, Rarity.RARE, mage.cards.v.VindictiveLich.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Meandering Towershell", 236, Rarity.RARE, mage.cards.m.MeanderingTowershell.class)); cards.add(new SetCardInfo("Ohran Frostfang", 237, Rarity.RARE, mage.cards.o.OhranFrostfang.class)); cards.add(new SetCardInfo("Thragtusk", 238, Rarity.RARE, mage.cards.t.Thragtusk.class)); @@ -283,7 +283,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Sanctum Prelate", 278, Rarity.MYTHIC, mage.cards.s.SanctumPrelate.class)); cards.add(new SetCardInfo("Carpet of Flowers", 279, Rarity.RARE, mage.cards.c.CarpetOfFlowers.class)); cards.add(new SetCardInfo("Sphere of Safety", 280, Rarity.RARE, mage.cards.s.SphereOfSafety.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Karmic Guide", 281, Rarity.RARE, mage.cards.k.KarmicGuide.class)); + cards.add(new SetCardInfo("Karmic Guide", 281, Rarity.RARE, mage.cards.k.KarmicGuide.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mesa Enchantress", 282, Rarity.RARE, mage.cards.m.MesaEnchantress.class)); cards.add(new SetCardInfo("Archaeomancer", 283, Rarity.RARE, mage.cards.a.Archaeomancer.class)); cards.add(new SetCardInfo("Bloom Tender", 284, Rarity.RARE, mage.cards.b.BloomTender.class)); @@ -324,7 +324,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Protean Hulk", 319, Rarity.RARE, mage.cards.p.ProteanHulk.class)); cards.add(new SetCardInfo("Gishath, Sun's Avatar", 320, Rarity.MYTHIC, mage.cards.g.GishathSunsAvatar.class)); cards.add(new SetCardInfo("Dismember", 321, Rarity.RARE, mage.cards.d.Dismember.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Blasphemous Act", 322, Rarity.RARE, mage.cards.b.BlasphemousAct.class)); + cards.add(new SetCardInfo("Blasphemous Act", 322, Rarity.RARE, mage.cards.b.BlasphemousAct.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Beast Within", 323, Rarity.RARE, mage.cards.b.BeastWithin.class)); cards.add(new SetCardInfo("Grafdigger's Cage", 324, Rarity.RARE, mage.cards.g.GrafdiggersCage.class)); cards.add(new SetCardInfo("Snow-Covered Plains", 325, Rarity.LAND, mage.cards.s.SnowCoveredPlains.class, FULL_ART_BFZ_VARIOUS)); @@ -409,7 +409,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Peek", 401, Rarity.RARE, mage.cards.p.Peek.class)); cards.add(new SetCardInfo("Greed", 402, Rarity.RARE, mage.cards.g.Greed.class)); cards.add(new SetCardInfo("Curiosity", 403, Rarity.RARE, mage.cards.c.Curiosity.class)); - cards.add(new SetCardInfo("Vandalblast", 404, Rarity.RARE, mage.cards.v.Vandalblast.class)); + cards.add(new SetCardInfo("Vandalblast", 404, Rarity.RARE, mage.cards.v.Vandalblast.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Last Chance", 405, Rarity.RARE, mage.cards.l.LastChance.class)); cards.add(new SetCardInfo("Mystic Remora", 406, Rarity.RARE, mage.cards.m.MysticRemora.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Retreat to Coralhelm", 407, Rarity.RARE, mage.cards.r.RetreatToCoralhelm.class)); @@ -451,7 +451,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Etherium Sculptor", 443, Rarity.RARE, mage.cards.e.EtheriumSculptor.class)); cards.add(new SetCardInfo("Grim Tutor", 444, Rarity.MYTHIC, mage.cards.g.GrimTutor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Triumph of the Hordes", 445, Rarity.RARE, mage.cards.t.TriumphOfTheHordes.class)); - cards.add(new SetCardInfo("Smuggler's Copter", 446, Rarity.RARE, mage.cards.s.SmugglersCopter.class)); + cards.add(new SetCardInfo("Smuggler's Copter", 446, Rarity.RARE, mage.cards.s.SmugglersCopter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Planar Bridge", 447, Rarity.MYTHIC, mage.cards.p.PlanarBridge.class)); cards.add(new SetCardInfo("Plains", 448, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Island", 449, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); @@ -631,11 +631,11 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Basal Sliver", 629, Rarity.RARE, mage.cards.b.BasalSliver.class)); cards.add(new SetCardInfo("Dregscape Sliver", 631, Rarity.RARE, mage.cards.d.DregscapeSliver.class)); cards.add(new SetCardInfo("Leeching Sliver", 632, Rarity.RARE, mage.cards.l.LeechingSliver.class)); - cards.add(new SetCardInfo("Plague Sliver", "633Ph", Rarity.RARE, mage.cards.p.PlagueSliver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plague Sliver", 633, Rarity.RARE, mage.cards.p.PlagueSliver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plague Sliver", "633Ph", Rarity.RARE, mage.cards.p.PlagueSliver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Syphon Sliver", 634, Rarity.RARE, mage.cards.s.SyphonSliver.class)); - cards.add(new SetCardInfo("Toxin Sliver", "635Ph", Rarity.RARE, mage.cards.t.ToxinSliver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Toxin Sliver", 635, Rarity.RARE, mage.cards.t.ToxinSliver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Toxin Sliver", "635Ph", Rarity.RARE, mage.cards.t.ToxinSliver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Belligerent Sliver", 636, Rarity.RARE, mage.cards.b.BelligerentSliver.class)); cards.add(new SetCardInfo("Blur Sliver", 637, Rarity.RARE, mage.cards.b.BlurSliver.class)); cards.add(new SetCardInfo("Fury Sliver", 638, Rarity.RARE, mage.cards.f.FurySliver.class)); @@ -657,8 +657,8 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Quick Sliver", 655, Rarity.RARE, mage.cards.q.QuickSliver.class)); cards.add(new SetCardInfo("Root Sliver", 656, Rarity.RARE, mage.cards.r.RootSliver.class)); cards.add(new SetCardInfo("Tempered Sliver", 657, Rarity.RARE, mage.cards.t.TemperedSliver.class)); - cards.add(new SetCardInfo("Virulent Sliver", "659Ph", Rarity.RARE, mage.cards.v.VirulentSliver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Virulent Sliver", 659, Rarity.RARE, mage.cards.v.VirulentSliver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Virulent Sliver", "659Ph", Rarity.RARE, mage.cards.v.VirulentSliver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cloudshredder Sliver", 660, Rarity.RARE, mage.cards.c.CloudshredderSliver.class)); cards.add(new SetCardInfo("Crystalline Sliver", 661, Rarity.RARE, mage.cards.c.CrystallineSliver.class)); cards.add(new SetCardInfo("Frenetic Sliver", 662, Rarity.RARE, mage.cards.f.FreneticSliver.class)); @@ -680,8 +680,8 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Torbran, Thane of Red Fell", 678, Rarity.RARE, mage.cards.t.TorbranThaneOfRedFell.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ghost Quarter", 679, Rarity.RARE, mage.cards.g.GhostQuarter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowborn Apostle", 680, Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Shadowborn Apostle", "681Ph", Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowborn Apostle", 681, Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadowborn Apostle", "681Ph", Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowborn Apostle", 682, Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowborn Apostle", 683, Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowborn Apostle", 684, Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); @@ -762,6 +762,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Relentless Rats", 757, Rarity.RARE, mage.cards.r.RelentlessRats.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Command Tower", 758, Rarity.RARE, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Skemfar Shadowsage", 759, Rarity.RARE, mage.cards.s.SkemfarShadowsage.class)); + cards.add(new SetCardInfo("Elvish Champion", 761, Rarity.RARE, mage.cards.e.ElvishChampion.class)); cards.add(new SetCardInfo("Elvish Vanguard", 762, Rarity.RARE, mage.cards.e.ElvishVanguard.class)); cards.add(new SetCardInfo("Elvish Visionary", 763, Rarity.RARE, mage.cards.e.ElvishVisionary.class)); cards.add(new SetCardInfo("Evolution Sage", 765, Rarity.RARE, mage.cards.e.EvolutionSage.class)); @@ -795,9 +796,11 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Mana Vault", "796*", Rarity.MYTHIC, mage.cards.m.ManaVault.class)); cards.add(new SetCardInfo("Seraph Sanctuary", 797, Rarity.RARE, mage.cards.s.SeraphSanctuary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Coveted Jewel", 799, Rarity.RARE, mage.cards.c.CovetedJewel.class)); + cards.add(new SetCardInfo("Llanowar Elves", 800, Rarity.RARE, mage.cards.l.LlanowarElves.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spiteful Prankster", 801, Rarity.RARE, mage.cards.s.SpitefulPrankster.class)); cards.add(new SetCardInfo("Haystack", 802, Rarity.RARE, mage.cards.h.Haystack.class)); cards.add(new SetCardInfo("Sonic Screwdriver", 803, Rarity.RARE, mage.cards.s.SonicScrewdriver.class)); + cards.add(new SetCardInfo("Beloved Princess", 804, Rarity.RARE, mage.cards.b.BelovedPrincess.class)); cards.add(new SetCardInfo("Elvish Mystic", 805, Rarity.RARE, mage.cards.e.ElvishMystic.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Command Tower", 806, Rarity.RARE, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Chandra, Flamecaller", 807, Rarity.MYTHIC, mage.cards.c.ChandraFlamecaller.class)); @@ -805,6 +808,9 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Immerwolf", 809, Rarity.RARE, mage.cards.i.Immerwolf.class)); cards.add(new SetCardInfo("Thrun, the Last Troll", 810, Rarity.MYTHIC, mage.cards.t.ThrunTheLastTroll.class)); cards.add(new SetCardInfo("Elesh Norn, Grand Cenobite", 811, Rarity.MYTHIC, mage.cards.e.EleshNornGrandCenobite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Seven Dwarves", 813, Rarity.RARE, mage.cards.s.SevenDwarves.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Seven Dwarves", 814, Rarity.RARE, mage.cards.s.SevenDwarves.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Seven Dwarves", 815, Rarity.COMMON, mage.cards.s.SevenDwarves.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arcane Signet", 820, Rarity.RARE, mage.cards.a.ArcaneSignet.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arcane Signet", "820*", Rarity.RARE, mage.cards.a.ArcaneSignet.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Echo of Eons", 821, Rarity.RARE, mage.cards.e.EchoOfEons.class)); @@ -816,31 +822,59 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Norin the Wary", 827, Rarity.RARE, mage.cards.n.NorinTheWary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Norin the Wary", "827b", Rarity.RARE, mage.cards.n.NorinTheWary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Keen Duelist", 828, Rarity.RARE, mage.cards.k.KeenDuelist.class)); + cards.add(new SetCardInfo("Champion of the Perished", 837, Rarity.RARE, mage.cards.c.ChampionOfThePerished.class)); + cards.add(new SetCardInfo("Corpse Connoisseur", 838, Rarity.RARE, mage.cards.c.CorpseConnoisseur.class)); + cards.add(new SetCardInfo("Cryptbreaker", 839, Rarity.RARE, mage.cards.c.Cryptbreaker.class)); + cards.add(new SetCardInfo("Diregraf Colossus", 840, Rarity.RARE, mage.cards.d.DiregrafColossus.class)); + cards.add(new SetCardInfo("Fleshbag Marauder", 841, Rarity.RARE, mage.cards.f.FleshbagMarauder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Geralf's Messenger", 842, Rarity.RARE, mage.cards.g.GeralfsMessenger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Haakon, Stromgald Scourge", 843, Rarity.RARE, mage.cards.h.HaakonStromgaldScourge.class)); + cards.add(new SetCardInfo("Headless Rider", 844, Rarity.RARE, mage.cards.h.HeadlessRider.class)); + cards.add(new SetCardInfo("Liliana's Standard Bearer", 845, Rarity.RARE, mage.cards.l.LilianasStandardBearer.class)); + cards.add(new SetCardInfo("Pontiff of Blight", 848, Rarity.RARE, mage.cards.p.PontiffOfBlight.class)); + cards.add(new SetCardInfo("Ravenous Rotbelly", 849, Rarity.RARE, mage.cards.r.RavenousRotbelly.class)); + cards.add(new SetCardInfo("Relentless Dead", 850, Rarity.MYTHIC, mage.cards.r.RelentlessDead.class)); + cards.add(new SetCardInfo("Tomb Tyrant", 854, Rarity.RARE, mage.cards.t.TombTyrant.class)); + cards.add(new SetCardInfo("Tormod, the Desecrator", 855, Rarity.RARE, mage.cards.t.TormodTheDesecrator.class)); + cards.add(new SetCardInfo("Vindictive Lich", 856, Rarity.RARE, mage.cards.v.VindictiveLich.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Diregraf Captain", 858, Rarity.RARE, mage.cards.d.DiregrafCaptain.class)); + cards.add(new SetCardInfo("Nekusar, the Mindrazer", 860, Rarity.MYTHIC, mage.cards.n.NekusarTheMindrazer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Varina, Lich Queen", 861, Rarity.MYTHIC, mage.cards.v.VarinaLichQueen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wilhelt, the Rotcleaver", 862, Rarity.MYTHIC, mage.cards.w.WilheltTheRotcleaver.class)); cards.add(new SetCardInfo("Masterwork of Ingenuity", 863, Rarity.RARE, mage.cards.m.MasterworkOfIngenuity.class)); cards.add(new SetCardInfo("Sculpting Steel", 864, Rarity.RARE, mage.cards.s.SculptingSteel.class)); cards.add(new SetCardInfo("Unnatural Growth", 865, Rarity.RARE, mage.cards.u.UnnaturalGrowth.class)); cards.add(new SetCardInfo("Regrowth", 866, Rarity.RARE, mage.cards.r.Regrowth.class)); cards.add(new SetCardInfo("Nature's Lore", 867, Rarity.RARE, mage.cards.n.NaturesLore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Harmless Offering", 868, Rarity.RARE, mage.cards.h.HarmlessOffering.class)); + cards.add(new SetCardInfo("Abundant Growth", 870, Rarity.RARE, mage.cards.a.AbundantGrowth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Soul-Guide Lantern", 871, Rarity.RARE, mage.cards.s.SoulGuideLantern.class)); cards.add(new SetCardInfo("Yargle and Multani", 872, Rarity.RARE, mage.cards.y.YargleAndMultani.class)); cards.add(new SetCardInfo("Dark Deal", 873, Rarity.RARE, mage.cards.d.DarkDeal.class)); cards.add(new SetCardInfo("Archivist of Oghma", 874, Rarity.RARE, mage.cards.a.ArchivistOfOghma.class)); cards.add(new SetCardInfo("Battle Angels of Tyr", 875, Rarity.RARE, mage.cards.b.BattleAngelsOfTyr.class)); cards.add(new SetCardInfo("Xorn", 876, Rarity.RARE, mage.cards.x.Xorn.class)); - cards.add(new SetCardInfo("Abundant Growth", 870, Rarity.RARE, mage.cards.a.AbundantGrowth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Druid of Purification", 877, Rarity.RARE, mage.cards.d.DruidOfPurification.class)); cards.add(new SetCardInfo("Prosperous Innkeeper", 878, Rarity.RARE, mage.cards.p.ProsperousInnkeeper.class)); cards.add(new SetCardInfo("Minsc & Boo, Timeless Heroes", 879, Rarity.MYTHIC, mage.cards.m.MinscBooTimelessHeroes.class)); cards.add(new SetCardInfo("Stuffy Doll", 880, Rarity.RARE, mage.cards.s.StuffyDoll.class)); + cards.add(new SetCardInfo("Silence", 881, Rarity.RARE, mage.cards.s.Silence.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Culling the Weak", 883, Rarity.RARE, mage.cards.c.CullingTheWeak.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fatal Push", 884, Rarity.RARE, mage.cards.f.FatalPush.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Young Wolf", 885, Rarity.RARE, mage.cards.y.YoungWolf.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Solve the Equation", 886, Rarity.RARE, mage.cards.s.SolveTheEquation.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Enduring Ideal", 887, Rarity.RARE, mage.cards.e.EnduringIdeal.class)); + cards.add(new SetCardInfo("Helpful Hunter", 895, Rarity.RARE, mage.cards.h.HelpfulHunter.class)); + cards.add(new SetCardInfo("Spirited Companion", 896, Rarity.RARE, mage.cards.s.SpiritedCompanion.class)); cards.add(new SetCardInfo("The Scarab God", 900, Rarity.MYTHIC, mage.cards.t.TheScarabGod.class)); cards.add(new SetCardInfo("Lightning Bolt", 901, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Deadeye Navigator", 902, Rarity.RARE, mage.cards.d.DeadeyeNavigator.class)); cards.add(new SetCardInfo("The Locust God", 903, Rarity.MYTHIC, mage.cards.t.TheLocustGod.class)); cards.add(new SetCardInfo("The Scorpion God", 904, Rarity.MYTHIC, mage.cards.t.TheScorpionGod.class)); cards.add(new SetCardInfo("Ignoble Hierarch", 906, Rarity.RARE, mage.cards.i.IgnobleHierarch.class)); cards.add(new SetCardInfo("Seedborn Muse", 907, Rarity.RARE, mage.cards.s.SeedbornMuse.class)); cards.add(new SetCardInfo("Arcane Signet", 908, Rarity.RARE, mage.cards.a.ArcaneSignet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sol Ring", 910, Rarity.RARE, mage.cards.s.SolRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elspeth, Knight-Errant", 1001, Rarity.MYTHIC, mage.cards.e.ElspethKnightErrant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Patron Wizard", 1002, Rarity.RARE, mage.cards.p.PatronWizard.class)); cards.add(new SetCardInfo("Berserk", 1003, Rarity.MYTHIC, mage.cards.b.Berserk.class, NON_FULL_USE_VARIOUS)); @@ -970,7 +1004,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Rapid Hybridization", 1126, Rarity.RARE, mage.cards.r.RapidHybridization.class)); cards.add(new SetCardInfo("Demonic Consultation", 1127, Rarity.RARE, mage.cards.d.DemonicConsultation.class)); cards.add(new SetCardInfo("Winds of Change", 1128, Rarity.RARE, mage.cards.w.WindsOfChange.class)); - cards.add(new SetCardInfo("Llanowar Elves", 1129, Rarity.RARE, mage.cards.l.LlanowarElves.class)); + cards.add(new SetCardInfo("Llanowar Elves", 1129, Rarity.RARE, mage.cards.l.LlanowarElves.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 1130, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Island", 1131, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swamp", 1132, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); @@ -1115,7 +1149,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("The World Tree", 1266, Rarity.RARE, mage.cards.t.TheWorldTree.class)); cards.add(new SetCardInfo("Higure, the Still Wind", 1267, Rarity.RARE, mage.cards.h.HigureTheStillWind.class)); cards.add(new SetCardInfo("Nezahal, Primal Tide", 1268, Rarity.RARE, mage.cards.n.NezahalPrimalTide.class)); - cards.add(new SetCardInfo("Dragonlord Kolaghan", 1269, Rarity.MYTHIC, mage.cards.d.DragonlordKolaghan.class)); + cards.add(new SetCardInfo("Dragonlord Kolaghan", 1269, Rarity.MYTHIC, mage.cards.d.DragonlordKolaghan.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mina and Denn, Wildborn", 1270, Rarity.RARE, mage.cards.m.MinaAndDennWildborn.class)); cards.add(new SetCardInfo("Xantcha, Sleeper Agent", 1271, Rarity.RARE, mage.cards.x.XantchaSleeperAgent.class)); cards.add(new SetCardInfo("Misdirection", 1272, Rarity.RARE, mage.cards.m.Misdirection.class)); @@ -1146,8 +1180,8 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Syr Konrad, the Grim", 1297, Rarity.RARE, mage.cards.s.SyrKonradTheGrim.class)); cards.add(new SetCardInfo("Underworld Dreams", 1298, Rarity.RARE, mage.cards.u.UnderworldDreams.class)); cards.add(new SetCardInfo("Waste Not", 1299, Rarity.RARE, mage.cards.w.WasteNot.class)); - cards.add(new SetCardInfo("Wheel of Misfortune", 1300, Rarity.RARE, mage.cards.w.WheelOfMisfortune.class)); - cards.add(new SetCardInfo("Nekusar, the Mindrazer", 1301, Rarity.MYTHIC, mage.cards.n.NekusarTheMindrazer.class)); + cards.add(new SetCardInfo("Wheel of Misfortune", 1300, Rarity.RARE, mage.cards.w.WheelOfMisfortune.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nekusar, the Mindrazer", 1301, Rarity.MYTHIC, mage.cards.n.NekusarTheMindrazer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nemesis of Reason", 1302, Rarity.RARE, mage.cards.n.NemesisOfReason.class)); cards.add(new SetCardInfo("Gaea's Blessing", 1303, Rarity.RARE, mage.cards.g.GaeasBlessing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Twilight Prophet", 1304, Rarity.MYTHIC, mage.cards.t.TwilightProphet.class)); @@ -1166,6 +1200,11 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Arbor Elf", 1317, Rarity.RARE, mage.cards.a.ArborElf.class)); cards.add(new SetCardInfo("Terastodon", 1318, Rarity.RARE, mage.cards.t.Terastodon.class)); cards.add(new SetCardInfo("Maelstrom Wanderer", 1319, Rarity.MYTHIC, mage.cards.m.MaelstromWanderer.class)); + cards.add(new SetCardInfo("Ancient Amphitheater", 1320, Rarity.RARE, mage.cards.a.AncientAmphitheater.class)); + cards.add(new SetCardInfo("Auntie's Hovel", 1321, Rarity.RARE, mage.cards.a.AuntiesHovel.class)); + cards.add(new SetCardInfo("Gilt-Leaf Palace", 1322, Rarity.RARE, mage.cards.g.GiltLeafPalace.class)); + cards.add(new SetCardInfo("Secluded Glen", 1323, Rarity.RARE, mage.cards.s.SecludedGlen.class)); + cards.add(new SetCardInfo("Wanderwine Hub", 1324, Rarity.RARE, mage.cards.w.WanderwineHub.class)); cards.add(new SetCardInfo("Estrid's Invocation", 1325, Rarity.RARE, mage.cards.e.EstridsInvocation.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Estrid's Invocation", "1325b", Rarity.RARE, mage.cards.e.EstridsInvocation.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Steely Resolve", 1326, Rarity.RARE, mage.cards.s.SteelyResolve.class, NON_FULL_USE_VARIOUS)); @@ -1198,7 +1237,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Puresteel Paladin", 1352, Rarity.RARE, mage.cards.p.PuresteelPaladin.class)); cards.add(new SetCardInfo("Vanquish the Horde", 1353, Rarity.RARE, mage.cards.v.VanquishTheHorde.class)); cards.add(new SetCardInfo("Zombie Apocalypse", 1354, Rarity.RARE, mage.cards.z.ZombieApocalypse.class)); - cards.add(new SetCardInfo("Varina, Lich Queen", 1355, Rarity.MYTHIC, mage.cards.v.VarinaLichQueen.class)); + cards.add(new SetCardInfo("Varina, Lich Queen", 1355, Rarity.MYTHIC, mage.cards.v.VarinaLichQueen.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Field of the Dead", 1356, Rarity.RARE, mage.cards.f.FieldOfTheDead.class)); cards.add(new SetCardInfo("Mountain", 1358, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 1359, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); @@ -1289,6 +1328,10 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Abrade", 1425, Rarity.RARE, mage.cards.a.Abrade.class)); cards.add(new SetCardInfo("Mass Hysteria", 1426, Rarity.RARE, mage.cards.m.MassHysteria.class)); cards.add(new SetCardInfo("Terminate", 1427, Rarity.RARE, mage.cards.t.Terminate.class)); + cards.add(new SetCardInfo("Thalia, Heretic Cathar", 1428, Rarity.RARE, mage.cards.t.ThaliaHereticCathar.class)); + cards.add(new SetCardInfo("Clever Impersonator", 1429, Rarity.MYTHIC, mage.cards.c.CleverImpersonator.class)); + cards.add(new SetCardInfo("Hedron Crab", 1430, Rarity.RARE, mage.cards.h.HedronCrab.class)); + cards.add(new SetCardInfo("Pitiless Plunderer", 1431, Rarity.RARE, mage.cards.p.PitilessPlunderer.class)); cards.add(new SetCardInfo("Mycosynth Golem", 1433, Rarity.RARE, mage.cards.m.MycosynthGolem.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mycosynth Golem", "1433*", Rarity.RARE, mage.cards.m.MycosynthGolem.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mycosynth Lattice", 1434, Rarity.MYTHIC, mage.cards.m.MycosynthLattice.class, NON_FULL_USE_VARIOUS)); @@ -1296,7 +1339,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Mycosynth Wellspring", 1435, Rarity.RARE, mage.cards.m.MycosynthWellspring.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mycosynth Wellspring", "1435*", Rarity.RARE, mage.cards.m.MycosynthWellspring.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sisay, Weatherlight Captain", 1444, Rarity.RARE, mage.cards.s.SisayWeatherlightCaptain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Silence", 1445, Rarity.RARE, mage.cards.s.Silence.class)); + cards.add(new SetCardInfo("Silence", 1445, Rarity.RARE, mage.cards.s.Silence.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Battle of Wits", 1446, Rarity.RARE, mage.cards.b.BattleOfWits.class)); cards.add(new SetCardInfo("Baral, Chief of Compliance", 1447, Rarity.RARE, mage.cards.b.BaralChiefOfCompliance.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Pack Rat", 1448, Rarity.RARE, mage.cards.p.PackRat.class, NON_FULL_USE_VARIOUS)); @@ -1425,6 +1468,10 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Price of Glory", 1525, Rarity.RARE, mage.cards.p.PriceOfGlory.class)); cards.add(new SetCardInfo("Reckless Fireweaver", 1526, Rarity.RARE, mage.cards.r.RecklessFireweaver.class)); cards.add(new SetCardInfo("Akroma's Memorial", 1527, Rarity.MYTHIC, mage.cards.a.AkromasMemorial.class)); + cards.add(new SetCardInfo("Kokusho, the Evening Star", 1528, Rarity.RARE, mage.cards.k.KokushoTheEveningStar.class)); + cards.add(new SetCardInfo("Skyline Despot", 1529, Rarity.RARE, mage.cards.s.SkylineDespot.class)); + cards.add(new SetCardInfo("Niv-Mizzet, Parun", 1530, Rarity.RARE, mage.cards.n.NivMizzetParun.class)); + cards.add(new SetCardInfo("Scion of the Ur-Dragon", 1531, Rarity.RARE, mage.cards.s.ScionOfTheUrDragon.class)); cards.add(new SetCardInfo("Bojuka Bog", 1532, Rarity.RARE, mage.cards.b.BojukaBog.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bojuka Bog", "1532*", Rarity.RARE, mage.cards.b.BojukaBog.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Command Beacon", 1533, Rarity.RARE, mage.cards.c.CommandBeacon.class, NON_FULL_USE_VARIOUS)); @@ -1453,6 +1500,10 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Simian Spirit Guide", "1548*", Rarity.RARE, mage.cards.s.SimianSpiritGuide.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Prince of Thralls", 1549, Rarity.MYTHIC, mage.cards.p.PrinceOfThralls.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Prince of Thralls", "1549*", Rarity.MYTHIC, mage.cards.p.PrinceOfThralls.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sun Titan", 1550, Rarity.RARE, mage.cards.s.SunTitan.class)); + cards.add(new SetCardInfo("Breeches, Eager Pillager", 1551, Rarity.RARE, mage.cards.b.BreechesEagerPillager.class)); + cards.add(new SetCardInfo("Deflecting Swat", 1552, Rarity.RARE, mage.cards.d.DeflectingSwat.class)); + cards.add(new SetCardInfo("Llanowar Elves", 1553, Rarity.RARE, mage.cards.l.LlanowarElves.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rin and Seri, Inseparable", 1554, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rin and Seri, Inseparable", "1554b", Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jetmir, Nexus of Revels", 1555, Rarity.MYTHIC, mage.cards.j.JetmirNexusOfRevels.class, NON_FULL_USE_VARIOUS)); @@ -1464,6 +1515,10 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Queen Marchesa", 1559, Rarity.MYTHIC, mage.cards.q.QueenMarchesa.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ramses, Assassin Lord", 1560, Rarity.RARE, mage.cards.r.RamsesAssassinLord.class)); cards.add(new SetCardInfo("Admiral Beckett Brass", 1561, Rarity.MYTHIC, mage.cards.a.AdmiralBeckettBrass.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skithiryx, the Blight Dragon", 1562, Rarity.MYTHIC, mage.cards.s.SkithiryxTheBlightDragon.class)); + cards.add(new SetCardInfo("Kaalia of the Vast", 1563, Rarity.MYTHIC, mage.cards.k.KaaliaOfTheVast.class)); + cards.add(new SetCardInfo("Angel of Despair", 1564, Rarity.UNCOMMON, mage.cards.a.AngelOfDespair.class)); + cards.add(new SetCardInfo("Master of Cruelties", 1565, Rarity.MYTHIC, mage.cards.m.MasterOfCruelties.class)); cards.add(new SetCardInfo("Gonti, Lord of Luxury", 1566, Rarity.RARE, mage.cards.g.GontiLordOfLuxury.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gonti, Lord of Luxury", "1566*", Rarity.RARE, mage.cards.g.GontiLordOfLuxury.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vilis, Broker of Blood", 1567, Rarity.RARE, mage.cards.v.VilisBrokerOfBlood.class, NON_FULL_USE_VARIOUS)); @@ -1491,9 +1546,13 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("The Meep", 1581, Rarity.RARE, mage.cards.t.TheMeep.class)); cards.add(new SetCardInfo("The Fourteenth Doctor", 1583, Rarity.RARE, mage.cards.t.TheFourteenthDoctor.class)); cards.add(new SetCardInfo("Elspeth Tirel", 1585, Rarity.MYTHIC, mage.cards.e.ElspethTirel.class)); + cards.add(new SetCardInfo("Giada, Font of Hope", 1586, Rarity.RARE, mage.cards.g.GiadaFontOfHope.class)); cards.add(new SetCardInfo("Shelter", 1587, Rarity.RARE, mage.cards.s.Shelter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shelter", "1587*", Rarity.RARE, mage.cards.s.Shelter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Youthful Valkyrie", 1588, Rarity.RARE, mage.cards.y.YouthfulValkyrie.class)); + cards.add(new SetCardInfo("Counterspell", 1589, Rarity.RARE, mage.cards.c.Counterspell.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jace, Unraveler of Secrets", 1590, Rarity.MYTHIC, mage.cards.j.JaceUnravelerOfSecrets.class)); + cards.add(new SetCardInfo("Swan Song", 1591, Rarity.RARE, mage.cards.s.SwanSong.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Diabolic Tutor", 1592, Rarity.RARE, mage.cards.d.DiabolicTutor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Liliana of the Dark Realms", 1593, Rarity.MYTHIC, mage.cards.l.LilianaOfTheDarkRealms.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Chandra's Ignition", 1594, Rarity.RARE, mage.cards.c.ChandrasIgnition.class, NON_FULL_USE_VARIOUS)); @@ -1506,12 +1565,14 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Freyalise, Llanowar's Fury", 1598, Rarity.MYTHIC, mage.cards.f.FreyaliseLlanowarsFury.class)); cards.add(new SetCardInfo("Child of Alara", 1599, Rarity.MYTHIC, mage.cards.c.ChildOfAlara.class)); cards.add(new SetCardInfo("The Royal Scions", 1600, Rarity.MYTHIC, mage.cards.t.TheRoyalScions.class)); + cards.add(new SetCardInfo("Brago, King Eternal", 1601, Rarity.RARE, mage.cards.b.BragoKingEternal.class)); cards.add(new SetCardInfo("Feather, the Redeemed", 1602, Rarity.RARE, mage.cards.f.FeatherTheRedeemed.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Feather, the Redeemed", "1602*", Rarity.RARE, mage.cards.f.FeatherTheRedeemed.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Song of Creation", 1603, Rarity.RARE, mage.cards.s.SongOfCreation.class)); cards.add(new SetCardInfo("Sol Ring", 1604, Rarity.RARE, mage.cards.s.SolRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Inspiring Vantage", 1605, Rarity.RARE, mage.cards.i.InspiringVantage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Inspiring Vantage", "1605*", Rarity.RARE, mage.cards.i.InspiringVantage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scrying Sheets", 1606, Rarity.RARE, mage.cards.s.ScryingSheets.class)); cards.add(new SetCardInfo("Thespian's Stage", 1607, Rarity.RARE, mage.cards.t.ThespiansStage.class)); cards.add(new SetCardInfo("Avabruck Caretaker", 1608, Rarity.MYTHIC, mage.cards.a.AvabruckCaretaker.class)); cards.add(new SetCardInfo("Hollowhenge Huntmaster", 1608, Rarity.MYTHIC, mage.cards.h.HollowhengeHuntmaster.class)); @@ -1647,6 +1708,10 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Preordain", 1719, Rarity.RARE, mage.cards.p.Preordain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sphinx of the Second Sun", 1720, Rarity.MYTHIC, mage.cards.s.SphinxOfTheSecondSun.class)); cards.add(new SetCardInfo("Teferi's Ageless Insight", 1721, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class)); + cards.add(new SetCardInfo("Witch of the Moors", 1722, Rarity.RARE, mage.cards.w.WitchOfTheMoors.class)); + cards.add(new SetCardInfo("Bear Umbra", 1723, Rarity.RARE, mage.cards.b.BearUmbra.class)); + cards.add(new SetCardInfo("Realmwalker", 1724, Rarity.RARE, mage.cards.r.Realmwalker.class)); + cards.add(new SetCardInfo("Solemn Simulacrum", 1725, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Captain America, First Avenger", 1726, Rarity.MYTHIC, mage.cards.c.CaptainAmericaFirstAvenger.class)); cards.add(new SetCardInfo("Sigarda's Aid", 1727, Rarity.RARE, mage.cards.s.SigardasAid.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Flawless Maneuver", 1728, Rarity.RARE, mage.cards.f.FlawlessManeuver.class)); @@ -1672,6 +1737,10 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Primal Vigor", 1749, Rarity.RARE, mage.cards.p.PrimalVigor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Heroic Intervention", 1750, Rarity.RARE, mage.cards.h.HeroicIntervention.class)); cards.add(new SetCardInfo("Karn's Bastion", 1751, Rarity.RARE, mage.cards.k.KarnsBastion.class)); + cards.add(new SetCardInfo("Deadly Rollick", 1754, Rarity.RARE, mage.cards.d.DeadlyRollick.class)); + cards.add(new SetCardInfo("Saw in Half", 1755, Rarity.RARE, mage.cards.s.SawInHalf.class)); + cards.add(new SetCardInfo("Blasphemous Act", 1756, Rarity.RARE, mage.cards.b.BlasphemousAct.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vandalblast", 1757, Rarity.RARE, mage.cards.v.Vandalblast.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Phyrexian Metamorph", 1758, Rarity.RARE, mage.cards.p.PhyrexianMetamorph.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cauldron Familiar", 1759, Rarity.RARE, mage.cards.c.CauldronFamiliar.class)); cards.add(new SetCardInfo("Dauthi Voidwalker", 1760, Rarity.RARE, mage.cards.d.DauthiVoidwalker.class)); @@ -1727,11 +1796,16 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Varragoth, Bloodsky Sire", 1809, Rarity.RARE, mage.cards.v.VarragothBloodskySire.class)); cards.add(new SetCardInfo("Twinflame", 1810, Rarity.RARE, mage.cards.t.Twinflame.class)); cards.add(new SetCardInfo("Genesis Chamber", 1811, Rarity.RARE, mage.cards.g.GenesisChamber.class)); + cards.add(new SetCardInfo("Silence", 1816, Rarity.RARE, mage.cards.s.Silence.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Winds of Abandon", 1817, Rarity.RARE, mage.cards.w.WindsOfAbandon.class)); + cards.add(new SetCardInfo("Culling the Weak", 1818, Rarity.RARE, mage.cards.c.CullingTheWeak.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fatal Push", 1819, Rarity.RARE, mage.cards.f.FatalPush.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Young Wolf", 1820, Rarity.RARE, mage.cards.y.YoungWolf.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mana Geyser", 1821, Rarity.RARE, mage.cards.m.ManaGeyser.class)); cards.add(new SetCardInfo("Lightning Bolt", 1822, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fierce Guardianship", 1823, Rarity.RARE, mage.cards.f.FierceGuardianship.class)); cards.add(new SetCardInfo("Delayed Blast Fireball", 1824, Rarity.RARE, mage.cards.d.DelayedBlastFireball.class)); - cards.add(new SetCardInfo("Go-Shintai of Life's Origin", 1825, Rarity.MYTHIC, mage.cards.g.GoShintaiOfLifesOrigin.class)); + cards.add(new SetCardInfo("Go-Shintai of Life's Origin", 1825, Rarity.MYTHIC, mage.cards.g.GoShintaiOfLifesOrigin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Approach of the Second Sun", 1826, Rarity.RARE, mage.cards.a.ApproachOfTheSecondSun.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Felidar Sovereign", 1827, Rarity.RARE, mage.cards.f.FelidarSovereign.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Happily Ever After", 1828, Rarity.RARE, mage.cards.h.HappilyEverAfter.class)); @@ -1757,6 +1831,93 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Trace of Abundance", 1849, Rarity.COMMON, mage.cards.t.TraceOfAbundance.class)); cards.add(new SetCardInfo("Crib Swap", 1850, Rarity.UNCOMMON, mage.cards.c.CribSwap.class)); cards.add(new SetCardInfo("Homeward Path", 1851, Rarity.RARE, mage.cards.h.HomewardPath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Go-Shintai of Life's Origin", 1853, Rarity.MYTHIC, mage.cards.g.GoShintaiOfLifesOrigin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aesi, Tyrant of Gyre Strait", 1873, Rarity.MYTHIC, mage.cards.a.AesiTyrantOfGyreStrait.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aesi, Tyrant of Gyre Strait", "1873b", Rarity.MYTHIC, mage.cards.a.AesiTyrantOfGyreStrait.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Anje Falkenrath", 1874, Rarity.MYTHIC, mage.cards.a.AnjeFalkenrath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Anje Falkenrath", "1874b", Rarity.MYTHIC, mage.cards.a.AnjeFalkenrath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chulane, Teller of Tales", 1875, Rarity.MYTHIC, mage.cards.c.ChulaneTellerOfTales.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chulane, Teller of Tales", "1875b", Rarity.MYTHIC, mage.cards.c.ChulaneTellerOfTales.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Radha, Heart of Keld", 1876, Rarity.RARE, mage.cards.r.RadhaHeartOfKeld.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Radha, Heart of Keld", "1876b", Rarity.RARE, mage.cards.r.RadhaHeartOfKeld.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Light-Paws, Emperor's Voice", 1877, Rarity.RARE, mage.cards.l.LightPawsEmperorsVoice.class)); + cards.add(new SetCardInfo("Murktide Regent", 1878, Rarity.MYTHIC, mage.cards.m.MurktideRegent.class)); + cards.add(new SetCardInfo("Lightning Bolt", 1879, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shorikai, Genesis Engine", 1880, Rarity.MYTHIC, mage.cards.s.ShorikaiGenesisEngine.class)); + cards.add(new SetCardInfo("Mulldrifter", 1887, Rarity.RARE, mage.cards.m.Mulldrifter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("All Will Be One", 1888, Rarity.MYTHIC, mage.cards.a.AllWillBeOne.class)); + cards.add(new SetCardInfo("Benevolent Hydra", 1889, Rarity.RARE, mage.cards.b.BenevolentHydra.class)); + cards.add(new SetCardInfo("Forgotten Ancient", 1890, Rarity.RARE, mage.cards.f.ForgottenAncient.class)); + cards.add(new SetCardInfo("Animar, Soul of Elements", 1891, Rarity.MYTHIC, mage.cards.a.AnimarSoulOfElements.class)); + cards.add(new SetCardInfo("Esika's Chariot", 1894, Rarity.RARE, mage.cards.e.EsikasChariot.class)); + cards.add(new SetCardInfo("Karmic Guide", 1911, Rarity.RARE, mage.cards.k.KarmicGuide.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ninja of the Deep Hours", 1912, Rarity.RARE, mage.cards.n.NinjaOfTheDeepHours.class)); + cards.add(new SetCardInfo("Captain Sisay", 1913, Rarity.MYTHIC, mage.cards.c.CaptainSisay.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Selvala, Explorer Returned", 1914, Rarity.RARE, mage.cards.s.SelvalaExplorerReturned.class)); + cards.add(new SetCardInfo("Veyran, Voice of Duality", 1915, Rarity.MYTHIC, mage.cards.v.VeyranVoiceOfDuality.class)); + cards.add(new SetCardInfo("Escape to the Wilds", 1916, Rarity.RARE, mage.cards.e.EscapeToTheWilds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rip Apart", 1917, Rarity.RARE, mage.cards.r.RipApart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Titanic Ultimatum", 1918, Rarity.RARE, mage.cards.t.TitanicUltimatum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arcane Signet", 1919, Rarity.RARE, mage.cards.a.ArcaneSignet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basilisk Collar", 1920, Rarity.RARE, mage.cards.b.BasiliskCollar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Escape to the Wilds", 1921, Rarity.RARE, mage.cards.e.EscapeToTheWilds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rip Apart", 1922, Rarity.RARE, mage.cards.r.RipApart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Titanic Ultimatum", 1923, Rarity.RARE, mage.cards.t.TitanicUltimatum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arcane Signet", 1924, Rarity.RARE, mage.cards.a.ArcaneSignet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Basilisk Collar", 1925, Rarity.RARE, mage.cards.b.BasiliskCollar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skrelv, Defector Mite", 1926, Rarity.RARE, mage.cards.s.SkrelvDefectorMite.class)); + cards.add(new SetCardInfo("Charix, the Raging Isle", 1927, Rarity.RARE, mage.cards.c.CharixTheRagingIsle.class)); + cards.add(new SetCardInfo("Grazilaxx, Illithid Scholar", 1928, Rarity.RARE, mage.cards.g.GrazilaxxIllithidScholar.class)); + cards.add(new SetCardInfo("Toxrill, the Corrosive", 1929, Rarity.MYTHIC, mage.cards.t.ToxrillTheCorrosive.class)); + cards.add(new SetCardInfo("Toski, Bearer of Secrets", 1930, Rarity.MYTHIC, mage.cards.t.ToskiBearerOfSecrets.class)); + cards.add(new SetCardInfo("Barktooth Warbeard", 1931, Rarity.RARE, mage.cards.b.BarktoothWarbeard.class)); + cards.add(new SetCardInfo("Jodah, the Unifier", 1932, Rarity.MYTHIC, mage.cards.j.JodahTheUnifier.class)); + cards.add(new SetCardInfo("Counterspell", 1933, Rarity.RARE, mage.cards.c.Counterspell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Daze", 1934, Rarity.RARE, mage.cards.d.Daze.class)); + cards.add(new SetCardInfo("Inevitable Betrayal", 1935, Rarity.RARE, mage.cards.i.InevitableBetrayal.class)); + cards.add(new SetCardInfo("Force of Despair", 1936, Rarity.RARE, mage.cards.f.ForceOfDespair.class)); + cards.add(new SetCardInfo("Night's Whisper", 1937, Rarity.RARE, mage.cards.n.NightsWhisper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 1939, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 1940, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 1941, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 1942, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 1943, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Big Score", 1955, Rarity.RARE, mage.cards.b.BigScore.class)); + cards.add(new SetCardInfo("Final Fortune", 1956, Rarity.RARE, mage.cards.f.FinalFortune.class)); + cards.add(new SetCardInfo("Heat Shimmer", 1957, Rarity.RARE, mage.cards.h.HeatShimmer.class)); + cards.add(new SetCardInfo("Roiling Vortex", 1958, Rarity.RARE, mage.cards.r.RoilingVortex.class)); + cards.add(new SetCardInfo("Wheel of Misfortune", 1959, Rarity.RARE, mage.cards.w.WheelOfMisfortune.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Parhelion II", 1964, Rarity.RARE, mage.cards.p.ParhelionII.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Parhelion II", "1964b", Rarity.RARE, mage.cards.p.ParhelionII.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mechtitan Core", 1965, Rarity.RARE, mage.cards.m.MechtitanCore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mechtitan Core", "1965b", Rarity.RARE, mage.cards.m.MechtitanCore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Peacewalker Colossus", 1966, Rarity.RARE, mage.cards.p.PeacewalkerColossus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Peacewalker Colossus", "1966b", Rarity.RARE, mage.cards.p.PeacewalkerColossus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reckoner Bankbuster", 1967, Rarity.RARE, mage.cards.r.ReckonerBankbuster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reckoner Bankbuster", "1967b", Rarity.RARE, mage.cards.r.ReckonerBankbuster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Smuggler's Copter", 1968, Rarity.RARE, mage.cards.s.SmugglersCopter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Smuggler's Copter", "1968b", Rarity.RARE, mage.cards.s.SmugglersCopter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mechtitan Core", 1969, Rarity.COMMON, mage.cards.m.MechtitanCore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mechtitan Core", "1969b", Rarity.COMMON, mage.cards.m.MechtitanCore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonlord Atarka", 1970, Rarity.MYTHIC, mage.cards.d.DragonlordAtarka.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonlord Atarka", "1970b", Rarity.MYTHIC, mage.cards.d.DragonlordAtarka.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonlord Dromoka", 1971, Rarity.MYTHIC, mage.cards.d.DragonlordDromoka.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonlord Dromoka", "1971b", Rarity.MYTHIC, mage.cards.d.DragonlordDromoka.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonlord Kolaghan", 1972, Rarity.MYTHIC, mage.cards.d.DragonlordKolaghan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonlord Kolaghan", "1972b", Rarity.MYTHIC, mage.cards.d.DragonlordKolaghan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonlord Ojutai", 1973, Rarity.MYTHIC, mage.cards.d.DragonlordOjutai.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonlord Ojutai", "1973b", Rarity.MYTHIC, mage.cards.d.DragonlordOjutai.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonlord Silumgar", 1974, Rarity.MYTHIC, mage.cards.d.DragonlordSilumgar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragonlord Silumgar", "1974b", Rarity.MYTHIC, mage.cards.d.DragonlordSilumgar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ashaya, Soul of the Wild", 2014, Rarity.MYTHIC, mage.cards.a.AshayaSoulOfTheWild.class)); + cards.add(new SetCardInfo("Elvish Reclaimer", 2015, Rarity.RARE, mage.cards.e.ElvishReclaimer.class)); + cards.add(new SetCardInfo("Harrow", 2016, Rarity.RARE, mage.cards.h.Harrow.class)); + cards.add(new SetCardInfo("World Shaper", 2017, Rarity.RARE, mage.cards.w.WorldShaper.class)); + cards.add(new SetCardInfo("Horn of Greed", 2018, Rarity.RARE, mage.cards.h.HornOfGreed.class)); + cards.add(new SetCardInfo("Smothering Tithe", 7009, Rarity.RARE, mage.cards.s.SmotheringTithe.class)); + cards.add(new SetCardInfo("Counterspell", 7010, Rarity.RARE, mage.cards.c.Counterspell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dismember", 7011, Rarity.RARE, mage.cards.d.Dismember.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Command Tower", 7012, Rarity.RARE, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jace, the Mind Sculptor", 8001, Rarity.MYTHIC, mage.cards.j.JaceTheMindSculptor.class)); cards.add(new SetCardInfo("Doom Blade", 9990, Rarity.RARE, mage.cards.d.DoomBlade.class)); cards.add(new SetCardInfo("Massacre", 9991, Rarity.RARE, mage.cards.m.Massacre.class)); @@ -1765,8 +1926,9 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Mogis, God of Slaughter", 9994, Rarity.RARE, mage.cards.m.MogisGodOfSlaughter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Garruk, Caller of Beasts", 9995, Rarity.MYTHIC, mage.cards.g.GarrukCallerOfBeasts.class)); cards.add(new SetCardInfo("Rograkh, Son of Rohgahh", 9996, Rarity.RARE, mage.cards.r.RograkhSonOfRohgahh.class)); - cards.add(new SetCardInfo("Geralf's Messenger", 9997, Rarity.RARE, mage.cards.g.GeralfsMessenger.class)); + cards.add(new SetCardInfo("Geralf's Messenger", 9997, Rarity.RARE, mage.cards.g.GeralfsMessenger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Empress Galina", 9998, Rarity.RARE, mage.cards.e.EmpressGalina.class)); cards.add(new SetCardInfo("Sisay, Weatherlight Captain", 9999, Rarity.RARE, mage.cards.s.SisayWeatherlightCaptain.class, NON_FULL_USE_VARIOUS)); + } } -- 2.47.2 From ce01f7ae93bf3f6525abd372e88bfe8b4ca18710 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Tue, 15 Apr 2025 07:52:06 -0500 Subject: [PATCH 43/95] [SLP] Add Secret Lair Showdown Variants --- .../src/mage/sets/SecretLairShowdown.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Mage.Sets/src/mage/sets/SecretLairShowdown.java b/Mage.Sets/src/mage/sets/SecretLairShowdown.java index 1c937300ffd..9440e31286e 100644 --- a/Mage.Sets/src/mage/sets/SecretLairShowdown.java +++ b/Mage.Sets/src/mage/sets/SecretLairShowdown.java @@ -29,15 +29,34 @@ public class SecretLairShowdown extends ExpansionSet { cards.add(new SetCardInfo("Explore", 12, Rarity.RARE, mage.cards.e.Explore.class)); cards.add(new SetCardInfo("Expressive Iteration", 13, Rarity.RARE, mage.cards.e.ExpressiveIteration.class)); cards.add(new SetCardInfo("Fatal Push", 3, Rarity.RARE, mage.cards.f.FatalPush.class)); + cards.add(new SetCardInfo("Fauna Shaman", 41, Rarity.RARE, mage.cards.f.FaunaShaman.class)); + cards.add(new SetCardInfo("Force of Despair", 29, Rarity.RARE, mage.cards.f.ForceOfDespair.class)); + cards.add(new SetCardInfo("Garruk Wildspeaker", 42, Rarity.MYTHIC, mage.cards.g.GarrukWildspeaker.class)); cards.add(new SetCardInfo("Goblin Guide", 23, Rarity.RARE, mage.cards.g.GoblinGuide.class)); + cards.add(new SetCardInfo("Island", 32, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Karn Liberated", 36, Rarity.MYTHIC, mage.cards.k.KarnLiberated.class)); + cards.add(new SetCardInfo("Lightning Bolt", 21, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lightning Bolt", 37, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Living End", 30, Rarity.MYTHIC, mage.cards.l.LivingEnd.class)); + cards.add(new SetCardInfo("Mayhem Devil", 28, Rarity.RARE, mage.cards.m.MayhemDevil.class)); cards.add(new SetCardInfo("Murktide Regent", 17, Rarity.MYTHIC, mage.cards.m.MurktideRegent.class)); + cards.add(new SetCardInfo("Nexus of Fate", 27, Rarity.RARE, mage.cards.n.NexusOfFate.class)); + cards.add(new SetCardInfo("Plains", 31, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Ponder", 19, Rarity.RARE, mage.cards.p.Ponder.class)); + cards.add(new SetCardInfo("Prosperous Innkeeper", 40, Rarity.RARE, mage.cards.p.ProsperousInnkeeper.class)); + cards.add(new SetCardInfo("Questing Druid", 38, Rarity.RARE, mage.cards.q.QuestingDruid.class)); cards.add(new SetCardInfo("Ragavan, Nimble Pilferer", 2, Rarity.MYTHIC, mage.cards.r.RagavanNimblePilferer.class)); cards.add(new SetCardInfo("Relentless Rats", 10, Rarity.RARE, mage.cards.r.RelentlessRats.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Relentless Rats", 11, Rarity.RARE, mage.cards.r.RelentlessRats.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Seasoned Pyromancer", 24, Rarity.MYTHIC, mage.cards.s.SeasonedPyromancer.class)); + cards.add(new SetCardInfo("Sleight of Hand", 25, Rarity.RARE, mage.cards.s.SleightOfHand.class)); cards.add(new SetCardInfo("Spell Pierce", 18, Rarity.RARE, mage.cards.s.SpellPierce.class)); cards.add(new SetCardInfo("Springleaf Drum", 22, Rarity.RARE, mage.cards.s.SpringleafDrum.class)); + cards.add(new SetCardInfo("Sudden Edict", 39, Rarity.RARE, mage.cards.s.SuddenEdict.class)); + cards.add(new SetCardInfo("Supreme Verdict", 26, Rarity.RARE, mage.cards.s.SupremeVerdict.class)); + cards.add(new SetCardInfo("Swords to Plowshares", 20, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class)); + cards.add(new SetCardInfo("Tribute to Horobi", 356, Rarity.RARE, mage.cards.t.TributeToHorobi.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Echo of Death's Wail", 356, Rarity.RARE, mage.cards.e.EchoOfDeathsWail.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 6, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class)); cards.add(new SetCardInfo("Unholy Heat", 4, Rarity.RARE, mage.cards.u.UnholyHeat.class)); cards.add(new SetCardInfo("Valakut, the Molten Pinnacle", 14, Rarity.RARE, mage.cards.v.ValakutTheMoltenPinnacle.class)); -- 2.47.2 From e17c773073a9392a79cb03da350e9007176ed5bb Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Tue, 15 Apr 2025 07:53:22 -0500 Subject: [PATCH 44/95] [SLD] Add missing urls for reversible cards --- .../dl/sources/ScryfallImageSupportCards.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java index c4832570889..f598d797970 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java @@ -629,16 +629,24 @@ public class ScryfallImageSupportCards { // SLD // fake double faced cards put("SLD/Adrix and Nev, Twincasters/1544b", "https://api.scryfall.com/cards/sld/1544/en?format=image&face=back"); + put("SLD/Aesi, Tyrant of Gyre Strait/1873b", "https://api.scryfall.com/cards/sld/1873/en?format=image&face=back"); put("SLD/Ajani Goldmane/745b", "https://api.scryfall.com/cards/sld/745/en?format=image&face=back"); put("SLD/Ajani Goldmane/1453b", "https://api.scryfall.com/cards/sld/1453/en?format=image&face=back"); - put("SLD/Anointed Procession/1511b", "https://api.scryfall.com/cards/sld/1453/en?format=image&face=back"); + put("SLD/Anje Falkenrath/1874b", "https://api.scryfall.com/cards/sld/1874/en?format=image&face=back"); + put("SLD/Anointed Procession/1511b", "https://api.scryfall.com/cards/sld/1511/en?format=image&face=back"); put("SLD/Birds of Paradise/1675b", "https://api.scryfall.com/cards/sld/1675/en?format=image&face=back"); put("SLD/Blightsteel Colossus/1079b", "https://api.scryfall.com/cards/sld/1079/en?format=image&face=back"); put("SLD/Chandra Nalaar/748b", "https://api.scryfall.com/cards/sld/748/en?format=image&face=back"); put("SLD/Chandra Nalaar/1456b", "https://api.scryfall.com/cards/sld/1456/en?format=image&face=back"); + put("SLD/Chulane, Teller of Tales/1875b", "https://api.scryfall.com/cards/sld/1875/en?format=image&face=back"); put("SLD/Darksteel Colossus/1081b", "https://api.scryfall.com/cards/sld/1081/en?format=image&face=back"); put("SLD/Death Baron/1458b", "https://api.scryfall.com/cards/sld/1458/en?format=image&face=back"); put("SLD/Doubling Cube/1080b", "https://api.scryfall.com/cards/sld/1080/en?format=image&face=back"); + put("SLD/Dragonlord Atarka/1970b", "https://api.scryfall.com/cards/sld/1970/en?format=image&face=back"); + put("SLD/Dragonlord Dromoka/1971b", "https://api.scryfall.com/cards/sld/1971/en?format=image&face=back"); + put("SLD/Dragonlord Kolaghan/1972b", "https://api.scryfall.com/cards/sld/1972/en?format=image&face=back"); + put("SLD/Dragonlord Ojutai/1973b", "https://api.scryfall.com/cards/sld/1973/en?format=image&face=back"); + put("SLD/Dragonlord Silumgar/1974b", "https://api.scryfall.com/cards/sld/1974/en?format=image&face=back"); put("SLD/Estrid's Invocation/1325b", "https://api.scryfall.com/cards/sld/1325/en?format=image&face=back"); put("SLD/Estrid, the Masked/1327b", "https://api.scryfall.com/cards/sld/1327/en?format=image&face=back"); put("SLD/Etali, Primal Storm/1123b", "https://api.scryfall.com/cards/sld/1123/en?format=image&face=back"); @@ -657,14 +665,21 @@ public class ScryfallImageSupportCards { put("SLD/Krark, the Thumbless/1543b", "https://api.scryfall.com/cards/sld/1543/en?format=image&face=back"); put("SLD/Liliana Vess/747b", "https://api.scryfall.com/cards/sld/747/en?format=image&face=back"); put("SLD/Liliana Vess/1455b", "https://api.scryfall.com/cards/sld/1455/en?format=image&face=back"); + put("SLD/Mechtitan Core/1969b", "https://api.scryfall.com/cards/sld/1969/en?format=image&face=back"); + put("SLD/Mechtitan Core/1965b", "https://api.scryfall.com/cards/sld/1965/en?format=image&face=back"); put("SLD/Norin the Wary/827b", "https://api.scryfall.com/cards/sld/827/en?format=image&face=back"); put("SLD/Noxious Ghoul/1459b", "https://api.scryfall.com/cards/sld/1459/en?format=image&face=back"); put("SLD/Okaun, Eye of Chaos/380b", "https://api.scryfall.com/cards/sld/380/en?format=image&face=back"); put("SLD/Okaun, Eye of Chaos/380*b", "https://api.scryfall.com/cards/sld/380★/en?format=image&face=back"); + put("SLD/Parhelion II/1964b", "https://api.scryfall.com/cards/sld/1964/en?format=image&face=back"); + put("SLD/Peacewalker Colossus/1966b", "https://api.scryfall.com/cards/sld/1966/en?format=image&face=back"); put("SLD/Propaganda/381b", "https://api.scryfall.com/cards/sld/381/en?format=image&face=back"); + put("SLD/Radha, Heart of Keld/1876b", "https://api.scryfall.com/cards/sld/1876/en?format=image&face=back"); + put("SLD/Reckoner Bankbuster/1967b", "https://api.scryfall.com/cards/sld/1967/en?format=image&face=back"); put("SLD/Rin and Seri, Inseparable/1508b", "https://api.scryfall.com/cards/sld/1508/en?format=image&face=back"); put("SLD/Rin and Seri, Inseparable/1554b", "https://api.scryfall.com/cards/sld/1554/en?format=image&face=back"); put("SLD/Sakashima of a Thousand Faces/1541b", "https://api.scryfall.com/cards/sld/1541/en?format=image&face=back"); + put("SLD/Smuggler's Copter/1968b", "https://api.scryfall.com/cards/sld/1968/en?format=image&face=back"); put("SLD/Sol Ring/1512b", "https://api.scryfall.com/cards/sld/1512/en?format=image&face=back"); put("SLD/Steely Resolve/1326b", "https://api.scryfall.com/cards/sld/1326/en?format=image&face=back"); put("SLD/Stitch in Time/382b", "https://api.scryfall.com/cards/sld/382/en?format=image&face=back"); -- 2.47.2 From 7c19a789e13fb8af27adc597caa3549a3c3a737f Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Tue, 15 Apr 2025 09:26:56 -0500 Subject: [PATCH 45/95] fix verify --- Mage.Sets/src/mage/sets/SecretLairShowdown.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/SecretLairShowdown.java b/Mage.Sets/src/mage/sets/SecretLairShowdown.java index 9440e31286e..49044f8cb02 100644 --- a/Mage.Sets/src/mage/sets/SecretLairShowdown.java +++ b/Mage.Sets/src/mage/sets/SecretLairShowdown.java @@ -18,7 +18,7 @@ public class SecretLairShowdown extends ExpansionSet { private SecretLairShowdown() { super("Secret Lair Showdown", "SLP", ExpansionSet.buildDate(2023, 2, 17), SetType.PROMOTIONAL); this.hasBoosters = false; - this.hasBasicLands = false; + this.hasBasicLands = true; cards.add(new SetCardInfo("An Offer You Can't Refuse", 7, Rarity.RARE, mage.cards.a.AnOfferYouCantRefuse.class)); cards.add(new SetCardInfo("Brainstorm", 1, Rarity.RARE, mage.cards.b.Brainstorm.class)); -- 2.47.2 From a1d88e34b0c41d1bcea404b7d348d1780273cae1 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Tue, 15 Apr 2025 10:06:20 -0500 Subject: [PATCH 46/95] Revert Adventure/Omen PermanentView changes --- Mage.Common/src/main/java/mage/view/PermanentView.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Mage.Common/src/main/java/mage/view/PermanentView.java b/Mage.Common/src/main/java/mage/view/PermanentView.java index 09fc5fc37b0..b14cd70d5c2 100644 --- a/Mage.Common/src/main/java/mage/view/PermanentView.java +++ b/Mage.Common/src/main/java/mage/view/PermanentView.java @@ -1,7 +1,6 @@ package mage.view; import mage.cards.Card; -import mage.cards.CardWithSpellOption; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; @@ -81,11 +80,6 @@ public class PermanentView extends CardView { } } - // for adventure/omen cards, remove spell card rules - if (card instanceof CardWithSpellOption) { - this.rules.removeIf(rule -> rule.startsWith("Adventure") || rule.startsWith("Omen")); - } - if (permanent.getOwnerId() != null && !permanent.getOwnerId().equals(permanent.getControllerId())) { Player owner = game.getPlayer(permanent.getOwnerId()); if (owner != null) { -- 2.47.2 From 5bee368b815ec2fd1650506b46056a4c6012c6b5 Mon Sep 17 00:00:00 2001 From: jmlundeen Date: Tue, 15 Apr 2025 10:11:34 -0500 Subject: [PATCH 47/95] Fix verify --- Mage.Sets/src/mage/cards/d/DragonclawStrike.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/d/DragonclawStrike.java b/Mage.Sets/src/mage/cards/d/DragonclawStrike.java index c8318674d70..61ddbf68fd9 100644 --- a/Mage.Sets/src/mage/cards/d/DragonclawStrike.java +++ b/Mage.Sets/src/mage/cards/d/DragonclawStrike.java @@ -22,7 +22,7 @@ import java.util.UUID; public final class DragonclawStrike extends CardImpl { public DragonclawStrike(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2/U}{2/R}{2/G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2/G}{2/U}{2/R}"); // Double the power and toughness of target creature you control until end of turn. Then it fights up to one target creature an opponent controls. this.getSpellAbility().addEffect(new DragonclawStrikeEffect()); -- 2.47.2 From c0c27c81fa5ff18267e45ede792846ba8a46dd0b Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 16 Apr 2025 10:10:11 +0400 Subject: [PATCH 48/95] refactor: simplified code (related to 6bf8aedce76b81f98404f369aa197ad87def1201) --- .../java/mage/abilities/SpellAbility.java | 7 +++-- .../java/mage/cards/AdventureSpellCard.java | 26 +++++----------- .../main/java/mage/cards/OmenSpellCard.java | 30 ++++++------------- 3 files changed, 21 insertions(+), 42 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java index 2c5c03ef220..4ece553bc79 100644 --- a/Mage/src/main/java/mage/abilities/SpellAbility.java +++ b/Mage/src/main/java/mage/abilities/SpellAbility.java @@ -238,9 +238,12 @@ public class SpellAbility extends ActivatedAbilityImpl { @Override public String getRule(boolean all) { if (all) { - return new StringBuilder(super.getRule(all)).append(name).toString(); + // show full rules, e.g. for hand + return super.getRule(true) + this.name; + } else { + // hide spell ability, e.g. for permanent + return super.getRule(false); } - return super.getRule(false); } public String getName() { diff --git a/Mage/src/main/java/mage/cards/AdventureSpellCard.java b/Mage/src/main/java/mage/cards/AdventureSpellCard.java index ed6efa5f1f1..c2c2a45efcb 100644 --- a/Mage/src/main/java/mage/cards/AdventureSpellCard.java +++ b/Mage/src/main/java/mage/cards/AdventureSpellCard.java @@ -1,7 +1,6 @@ package mage.cards; import mage.abilities.Ability; -import mage.abilities.Modes; import mage.abilities.SpellAbility; import mage.abilities.effects.common.ExileAdventureSpellEffect; import mage.constants.CardType; @@ -162,24 +161,13 @@ class AdventureCardSpellAbility extends SpellAbility { @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(" — "); - Modes modes = this.getModes(); - if (modes.size() <= 1) { - sbRule.append(modes.getMode().getEffects().getTextStartingUpperCase(modes.getMode())); - } else { - sbRule.append(getModes().getText()); - } - sbRule.append(" (Then exile this card. You may cast the creature later from exile.)"); - return sbRule.toString(); + // TODO: must hide rules in permanent like SpellAbility, but can't due effects text + return this.nameFull + + " " + + getManaCosts().getText() + + " — " + + super.getRule(false) // without cost + + " (Then exile this card. You may cast the creature later from exile.)"; } @Override diff --git a/Mage/src/main/java/mage/cards/OmenSpellCard.java b/Mage/src/main/java/mage/cards/OmenSpellCard.java index 040bba51217..04fcdd5d725 100644 --- a/Mage/src/main/java/mage/cards/OmenSpellCard.java +++ b/Mage/src/main/java/mage/cards/OmenSpellCard.java @@ -1,7 +1,6 @@ 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; @@ -134,8 +133,8 @@ class OmenCardSpellAbility extends SpellAbility { 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."); + + "Omen (" + cardName + ") " + + "need to call finalizeOmen() at the very end of the card's constructor."); } this.finalized = true; } @@ -147,24 +146,13 @@ class OmenCardSpellAbility extends SpellAbility { @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(" — "); - Modes modes = this.getModes(); - if (modes.size() <= 1) { - sbRule.append(modes.getMode().getEffects().getTextStartingUpperCase(modes.getMode())); - } else { - sbRule.append(getModes().getText()); - } - sbRule.append(" (Then shuffle this card into its owner's library.)"); - return sbRule.toString(); + // TODO: must hide rules in permanent like SpellAbility, but can't due effects text + return this.nameFull + + " " + + getManaCosts().getText() + + " — " + + super.getRule(false) // without cost + + " (Then shuffle this card into its owner's library.)"; } @Override -- 2.47.2 From d4954bf78465428bec9b78f68eb2d90e5bcf7129 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Wed, 16 Apr 2025 12:13:15 -0400 Subject: [PATCH 49/95] rework effects and abilities which care about controlling your own commander (fixes #13542) --- .../src/mage/cards/a/AngelicFieldMarshal.java | 20 +++++------ .../mage/cards/d/DemonOfWailingAgonies.java | 19 +++++------ .../src/mage/cards/l/LoyalApprentice.java | 26 ++++----------- Mage.Sets/src/mage/cards/l/LoyalDrake.java | 22 +++++-------- Mage.Sets/src/mage/cards/l/LoyalGuardian.java | 26 +++++---------- .../src/mage/cards/l/LoyalSubordinate.java | 23 +++++-------- Mage.Sets/src/mage/cards/l/LoyalUnicorn.java | 33 +++++++------------ .../src/mage/cards/s/SiegeGangLieutenant.java | 20 +++++------ .../mage/cards/s/SkyhunterStrikeForce.java | 4 +-- .../src/mage/cards/s/StormsurgeKraken.java | 19 ++++++----- .../src/mage/cards/t/ThunderfootBaloth.java | 28 +++++++--------- .../src/mage/cards/t/TyrantsFamiliar.java | 18 +++++----- .../abilityword/LieutenantAbility.java | 28 ++++++++-------- .../common/CommanderInPlayCondition.java | 26 --------------- .../common/ControlYourCommanderCondition.java | 19 +++++------ 15 files changed, 128 insertions(+), 203 deletions(-) delete mode 100644 Mage/src/main/java/mage/abilities/condition/common/CommanderInPlayCondition.java diff --git a/Mage.Sets/src/mage/cards/a/AngelicFieldMarshal.java b/Mage.Sets/src/mage/cards/a/AngelicFieldMarshal.java index cc7f394cca3..0882651ca32 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicFieldMarshal.java +++ b/Mage.Sets/src/mage/cards/a/AngelicFieldMarshal.java @@ -1,11 +1,9 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.abilityword.LieutenantAbility; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; @@ -13,16 +11,17 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author emerald000 */ public final class AngelicFieldMarshal extends CardImpl { public AngelicFieldMarshal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); this.subtype.add(SubType.ANGEL); this.power = new MageInt(3); @@ -30,11 +29,12 @@ public final class AngelicFieldMarshal extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // Lieutenant - As long as you control your commander, Angelic Field Marshal gets +2/+2 and creatures you control have vigilance. - ContinuousEffect effect = new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, new FilterControlledCreaturePermanent()); - effect.setText("and creatures you control have vigilance"); - this.addAbility(new LieutenantAbility(effect)); + this.addAbility(new LieutenantAbility(new GainAbilityAllEffect( + VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_CONTROLLED_CREATURES + ), "and creatures you control have vigilance")); } private AngelicFieldMarshal(final AngelicFieldMarshal card) { diff --git a/Mage.Sets/src/mage/cards/d/DemonOfWailingAgonies.java b/Mage.Sets/src/mage/cards/d/DemonOfWailingAgonies.java index e50617df326..21560e654f4 100644 --- a/Mage.Sets/src/mage/cards/d/DemonOfWailingAgonies.java +++ b/Mage.Sets/src/mage/cards/d/DemonOfWailingAgonies.java @@ -1,12 +1,9 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.abilityword.LieutenantAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.SacrificeEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.FlyingAbility; @@ -16,14 +13,15 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author emerald000 */ public final class DemonOfWailingAgonies extends CardImpl { public DemonOfWailingAgonies(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.DEMON); this.power = new MageInt(4); @@ -31,12 +29,13 @@ public final class DemonOfWailingAgonies extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // Lieutenant - As long as you control your commander, Demon of Wailing Agonies gets +2/+2 and has "Whenever Demon of Wailing Agonies deals combat damage to a player, that player sacrifices a creature." - Ability gainedAbility = new DealsCombatDamageToAPlayerTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, 1, "that player"), false, true); - ContinuousEffect effect = new GainAbilitySourceEffect(gainedAbility); - effect.setText("and has \"Whenever {this} deals combat damage to a player, that player sacrifices a creature.\""); - this.addAbility(new LieutenantAbility(effect)); + this.addAbility(new LieutenantAbility(new GainAbilitySourceEffect( + new DealsCombatDamageToAPlayerTriggeredAbility(new SacrificeEffect( + StaticFilters.FILTER_PERMANENT_CREATURE, 1, "that player" + ), false, true) + ), "and has \"Whenever {this} deals combat damage to a player, that player sacrifices a creature.\"")); } private DemonOfWailingAgonies(final DemonOfWailingAgonies card) { diff --git a/Mage.Sets/src/mage/cards/l/LoyalApprentice.java b/Mage.Sets/src/mage/cards/l/LoyalApprentice.java index cd2b0f47334..e918b817627 100644 --- a/Mage.Sets/src/mage/cards/l/LoyalApprentice.java +++ b/Mage.Sets/src/mage/cards/l/LoyalApprentice.java @@ -2,12 +2,11 @@ package mage.cards.l; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; -import mage.abilities.condition.common.CommanderInPlayCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.condition.common.ControlYourCommanderCondition; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.HasteAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -16,9 +15,7 @@ import mage.game.permanent.token.ThopterColorlessToken; import mage.game.permanent.token.Token; import mage.target.targetpointer.FixedTargets; -import java.util.Objects; import java.util.UUID; -import java.util.stream.Collectors; /** * @author TheElk801 @@ -37,14 +34,9 @@ public final class LoyalApprentice extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Lieutenant — At the beginning of combat on your turn, if you control your commander, create a 1/1 colorless Thopter artifact creature token with flying. That token gains haste until end of turn. - this.addAbility(new ConditionalTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new LoyalApprenticeEffect() - ), CommanderInPlayCondition.instance, "Lieutenant — " + - "At the beginning of combat on your turn, if you control your commander, " + - "create a 1/1 colorless Thopter artifact creature token with flying. " + - "That token gains haste until end of turn." - )); + this.addAbility(new BeginningOfCombatTriggeredAbility(new LoyalApprenticeEffect()) + .withInterveningIf(ControlYourCommanderCondition.instance) + .setAbilityWord(AbilityWord.LIEUTENANT)); } private LoyalApprentice(final LoyalApprentice card) { @@ -78,13 +70,7 @@ class LoyalApprenticeEffect extends OneShotEffect { token.putOntoBattlefield(1, game, source, source.getControllerId()); game.addEffect(new GainAbilityTargetEffect( HasteAbility.getInstance(), Duration.EndOfTurn - ).setTargetPointer(new FixedTargets( - token.getLastAddedTokenIds() - .stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .collect(Collectors.toList()), game - )), source); + ).setTargetPointer(new FixedTargets(token, game)), source); return true; } } diff --git a/Mage.Sets/src/mage/cards/l/LoyalDrake.java b/Mage.Sets/src/mage/cards/l/LoyalDrake.java index 900dddf2df4..c7be047df24 100644 --- a/Mage.Sets/src/mage/cards/l/LoyalDrake.java +++ b/Mage.Sets/src/mage/cards/l/LoyalDrake.java @@ -1,19 +1,19 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; -import mage.abilities.condition.common.CommanderInPlayCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.condition.common.ControlYourCommanderCondition; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class LoyalDrake extends CardImpl { @@ -29,13 +29,9 @@ public final class LoyalDrake extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Lieutenant — At the beginning of combat on your turn, if you control your commander, draw a card. - this.addAbility(new ConditionalTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new DrawCardSourceControllerEffect(1) - ), CommanderInPlayCondition.instance, - "Lieutenant — At the beginning of combat " - + "on your turn, if you control your commander, draw a card." - )); + this.addAbility(new BeginningOfCombatTriggeredAbility(new DrawCardSourceControllerEffect(1)) + .withInterveningIf(ControlYourCommanderCondition.instance) + .setAbilityWord(AbilityWord.LIEUTENANT)); } private LoyalDrake(final LoyalDrake card) { diff --git a/Mage.Sets/src/mage/cards/l/LoyalGuardian.java b/Mage.Sets/src/mage/cards/l/LoyalGuardian.java index 02ab5f4c48c..cb8c5852eb2 100644 --- a/Mage.Sets/src/mage/cards/l/LoyalGuardian.java +++ b/Mage.Sets/src/mage/cards/l/LoyalGuardian.java @@ -1,21 +1,21 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; -import mage.abilities.condition.common.CommanderInPlayCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.condition.common.ControlYourCommanderCondition; import mage.abilities.effects.common.counter.AddCountersAllEffect; -import mage.constants.SubType; import mage.abilities.keyword.TrampleAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; +import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class LoyalGuardian extends CardImpl { @@ -31,17 +31,9 @@ public final class LoyalGuardian extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Lieutenant — At the beginning of combat on your turn, if you control your commander, put a +1/+1 counter on each creature you control. - this.addAbility(new ConditionalTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new AddCountersAllEffect( - CounterType.P1P1.createInstance(), - StaticFilters.FILTER_CONTROLLED_CREATURE - ) - ), CommanderInPlayCondition.instance, - "Lieutenant — At the beginning of combat " - + "on your turn, if you control your commander, " - + "put a +1/+1 counter on each creature you control." - )); + this.addAbility(new BeginningOfCombatTriggeredAbility(new AddCountersAllEffect( + CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE + )).withInterveningIf(ControlYourCommanderCondition.instance).setAbilityWord(AbilityWord.LIEUTENANT)); } private LoyalGuardian(final LoyalGuardian card) { diff --git a/Mage.Sets/src/mage/cards/l/LoyalSubordinate.java b/Mage.Sets/src/mage/cards/l/LoyalSubordinate.java index 1eea09a2c49..b7cc936eaea 100644 --- a/Mage.Sets/src/mage/cards/l/LoyalSubordinate.java +++ b/Mage.Sets/src/mage/cards/l/LoyalSubordinate.java @@ -1,19 +1,19 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; -import mage.abilities.condition.common.CommanderInPlayCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.condition.common.ControlYourCommanderCondition; import mage.abilities.effects.common.LoseLifeOpponentsEffect; -import mage.constants.SubType; import mage.abilities.keyword.MenaceAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class LoyalSubordinate extends CardImpl { @@ -29,14 +29,9 @@ public final class LoyalSubordinate extends CardImpl { this.addAbility(new MenaceAbility(false)); // Lieutenant — At the beginning of combat on your turn, if you control your commander, each opponent loses 3 life. - this.addAbility(new ConditionalTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new LoseLifeOpponentsEffect(3) - ), CommanderInPlayCondition.instance, - "Lieutenant — At the beginning of combat " - + "on your turn, if you control your commander, " - + "each opponent loses 3 life." - )); + this.addAbility(new BeginningOfCombatTriggeredAbility(new LoseLifeOpponentsEffect(3)) + .withInterveningIf(ControlYourCommanderCondition.instance) + .setAbilityWord(AbilityWord.LIEUTENANT)); } private LoyalSubordinate(final LoyalSubordinate card) { diff --git a/Mage.Sets/src/mage/cards/l/LoyalUnicorn.java b/Mage.Sets/src/mage/cards/l/LoyalUnicorn.java index f2a52c879d7..d9388697afd 100644 --- a/Mage.Sets/src/mage/cards/l/LoyalUnicorn.java +++ b/Mage.Sets/src/mage/cards/l/LoyalUnicorn.java @@ -1,23 +1,23 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; -import mage.abilities.condition.common.CommanderInPlayCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.Ability; +import mage.abilities.condition.common.ControlYourCommanderCondition; import mage.abilities.effects.common.PreventAllDamageToAllEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; -import mage.constants.SubType; import mage.abilities.keyword.VigilanceAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class LoyalUnicorn extends CardImpl { @@ -33,25 +33,14 @@ public final class LoyalUnicorn extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Lieutenant — At the beginning of combat on your turn, if you control your commander, prevent all combat damage that would be dealt to creatures you control this turn. Other creatures you control gain vigilance until end of turn. - TriggeredAbility ability = new BeginningOfCombatTriggeredAbility( - new PreventAllDamageToAllEffect( - Duration.EndOfTurn, - StaticFilters.FILTER_CONTROLLED_CREATURES, - true - ) - ); + Ability ability = new BeginningOfCombatTriggeredAbility(new PreventAllDamageToAllEffect( + Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES, true + )).withInterveningIf(ControlYourCommanderCondition.instance); ability.addEffect(new GainAbilityAllEffect( VigilanceAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES, true )); - this.addAbility(new ConditionalTriggeredAbility( - ability, CommanderInPlayCondition.instance, - "Lieutenant — At the beginning of combat " - + "on your turn, if you control your commander, " - + "prevent all combat damage that would be dealt " - + "to creatures you control this turn. " - + "Other creatures you control gain vigilance until end of turn." - )); + this.addAbility(ability.setAbilityWord(AbilityWord.LIEUTENANT)); } private LoyalUnicorn(final LoyalUnicorn card) { diff --git a/Mage.Sets/src/mage/cards/s/SiegeGangLieutenant.java b/Mage.Sets/src/mage/cards/s/SiegeGangLieutenant.java index 40832f59859..fafe03614a8 100644 --- a/Mage.Sets/src/mage/cards/s/SiegeGangLieutenant.java +++ b/Mage.Sets/src/mage/cards/s/SiegeGangLieutenant.java @@ -2,19 +2,21 @@ package mage.cards.s; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.condition.common.CommanderInPlayCondition; +import mage.abilities.condition.common.ControlYourCommanderCondition; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.HasteAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.token.GoblinToken; @@ -40,13 +42,9 @@ public final class SiegeGangLieutenant extends CardImpl { this.toughness = new MageInt(2); // Lieutenant -- At the beginning of combat on your turn, if you control your commander, create two 1/1 red Goblin creature tokens. Those tokens gain haste until end of turn. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new SiegeGangLieutenantEffect() - ), CommanderInPlayCondition.instance, "At the beginning of combat on your turn, " + - "if you control your commander, create two 1/1 red Goblin creature tokens. " + - "Those tokens gain haste until end of turn." - ).setAbilityWord(AbilityWord.LIEUTENANT)); + this.addAbility(new BeginningOfCombatTriggeredAbility(new SiegeGangLieutenantEffect()) + .withInterveningIf(ControlYourCommanderCondition.instance) + .setAbilityWord(AbilityWord.LIEUTENANT)); // {2}, Sacrifice a Goblin: Siege-Gang Lieutenant deals 1 damage to any target. Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(1), new GenericManaCost(2)); diff --git a/Mage.Sets/src/mage/cards/s/SkyhunterStrikeForce.java b/Mage.Sets/src/mage/cards/s/SkyhunterStrikeForce.java index acb7442d0eb..23c5c57a5c7 100644 --- a/Mage.Sets/src/mage/cards/s/SkyhunterStrikeForce.java +++ b/Mage.Sets/src/mage/cards/s/SkyhunterStrikeForce.java @@ -2,7 +2,7 @@ package mage.cards.s; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.CommanderInPlayCondition; +import mage.abilities.condition.common.ControlYourCommanderCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.FlyingAbility; @@ -41,7 +41,7 @@ public final class SkyhunterStrikeForce extends CardImpl { new GainAbilityControlledEffect( new MeleeAbility(), Duration.WhileOnBattlefield, StaticFilters.FILTER_CONTROLLED_CREATURES, true - ), CommanderInPlayCondition.instance, "as long as you control your commander, " + + ), ControlYourCommanderCondition.instance, "as long as you control your commander, " + "other creatures you control have melee" )).setAbilityWord(AbilityWord.LIEUTENANT)); } diff --git a/Mage.Sets/src/mage/cards/s/StormsurgeKraken.java b/Mage.Sets/src/mage/cards/s/StormsurgeKraken.java index 8014f28336e..3a221a69fb5 100644 --- a/Mage.Sets/src/mage/cards/s/StormsurgeKraken.java +++ b/Mage.Sets/src/mage/cards/s/StormsurgeKraken.java @@ -1,28 +1,27 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.abilityword.LieutenantAbility; import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.HexproofAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class StormsurgeKraken extends CardImpl { public StormsurgeKraken(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.subtype.add(SubType.KRAKEN); this.power = new MageInt(5); @@ -30,11 +29,13 @@ public final class StormsurgeKraken extends CardImpl { // Hexproof this.addAbility(HexproofAbility.getInstance()); - + // Lieutenant - As long as you control your commander, Stormsurge Kraken gets +2/+2 and has "Whenever Stormsurge Kraken becomes blocked, you may draw two cards." - ContinuousEffect effect = new GainAbilitySourceEffect(new BecomesBlockedSourceTriggeredAbility(new DrawCardSourceControllerEffect(2), true), Duration.WhileOnBattlefield); - effect.setText("and has \"Whenever Stormsurge Kraken becomes blocked, you may draw two cards.\""); - this.addAbility(new LieutenantAbility(effect)); + this.addAbility(new LieutenantAbility(new GainAbilitySourceEffect( + new BecomesBlockedSourceTriggeredAbility( + new DrawCardSourceControllerEffect(2), true + ), Duration.WhileOnBattlefield + ), "and has \"Whenever {this} becomes blocked, you may draw two cards.\"")); } private StormsurgeKraken(final StormsurgeKraken card) { diff --git a/Mage.Sets/src/mage/cards/t/ThunderfootBaloth.java b/Mage.Sets/src/mage/cards/t/ThunderfootBaloth.java index e14ba8b37cd..3f76d6069d9 100644 --- a/Mage.Sets/src/mage/cards/t/ThunderfootBaloth.java +++ b/Mage.Sets/src/mage/cards/t/ThunderfootBaloth.java @@ -1,29 +1,27 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.abilityword.LieutenantAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.Effects; import mage.abilities.effects.common.continuous.BoostControlledEffect; -import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author emerald000 */ public final class ThunderfootBaloth extends CardImpl { public ThunderfootBaloth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); this.subtype.add(SubType.BEAST); this.power = new MageInt(5); @@ -31,16 +29,14 @@ public final class ThunderfootBaloth extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); - + // Lieutenant - As long as you control your commander, Thunderfoot Baloth gets +2/+2 and other creatures you control get +2/+2 and have trample. - Effects effects = new Effects(); - Effect effect = new BoostControlledEffect(2, 2, Duration.WhileOnBattlefield, true); - effect.setText("and other creatures you control get +2/+2"); - effects.add(effect); - effect = new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_CONTROLLED_CREATURES, true); - effect.setText("and have trample"); - effects.add(effect); - this.addAbility(new LieutenantAbility(effects)); + this.addAbility(new LieutenantAbility(new BoostControlledEffect( + 2, 2, Duration.WhileOnBattlefield, true + ), "and other creature you control get +2/+2").addLieutenantEffect(new GainAbilityAllEffect( + TrampleAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_CONTROLLED_CREATURES, true + ), "and have trample")); } private ThunderfootBaloth(final ThunderfootBaloth card) { diff --git a/Mage.Sets/src/mage/cards/t/TyrantsFamiliar.java b/Mage.Sets/src/mage/cards/t/TyrantsFamiliar.java index 1819e7c6aa5..9ed9aa5a7b7 100644 --- a/Mage.Sets/src/mage/cards/t/TyrantsFamiliar.java +++ b/Mage.Sets/src/mage/cards/t/TyrantsFamiliar.java @@ -1,13 +1,10 @@ package mage.cards.t; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.abilityword.LieutenantAbility; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.FlyingAbility; @@ -20,6 +17,8 @@ import mage.filter.FilterPermanent; import mage.filter.predicate.permanent.DefendingPlayerControlsSourceAttackingPredicate; import mage.target.TargetPermanent; +import java.util.UUID; + /** * @author emerald000 */ @@ -46,11 +45,14 @@ public final class TyrantsFamiliar extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Lieutenant - As long as you control your commander, Tyrant's Familiar gets +2/+2 and has "Whenever Tyrant's Familiar attacks, it deals 7 damage to target creature defending player controls." - Ability gainedAbility = new AttacksTriggeredAbility(new DamageTargetEffect(7, "it"), false); - gainedAbility.addTarget(new TargetPermanent(filter)); - ContinuousEffect effect = new GainAbilitySourceEffect(gainedAbility); - effect.setText("and has \"Whenever {this} attacks, it deals 7 damage to target creature defending player controls.\""); - this.addAbility(new LieutenantAbility(effect)); + Ability ability = new AttacksTriggeredAbility( + new DamageTargetEffect(7, "it"), false + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(new LieutenantAbility( + new GainAbilitySourceEffect(ability), "and has \"Whenever {this} attacks, " + + "it deals 7 damage to target creature defending player controls.\"" + )); } private TyrantsFamiliar(final TyrantsFamiliar card) { diff --git a/Mage/src/main/java/mage/abilities/abilityword/LieutenantAbility.java b/Mage/src/main/java/mage/abilities/abilityword/LieutenantAbility.java index cb1c049ef02..37e02dfe2a9 100644 --- a/Mage/src/main/java/mage/abilities/abilityword/LieutenantAbility.java +++ b/Mage/src/main/java/mage/abilities/abilityword/LieutenantAbility.java @@ -1,14 +1,11 @@ - - package mage.abilities.abilityword; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.CommanderInPlayCondition; +import mage.abilities.condition.common.ControlYourCommanderCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.Effects; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.constants.AbilityWord; import mage.constants.Duration; import mage.constants.Zone; @@ -18,16 +15,19 @@ import mage.constants.Zone; public class LieutenantAbility extends SimpleStaticAbility { - public LieutenantAbility(ContinuousEffect effect) { - super(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), CommanderInPlayCondition.instance, "Lieutenant — As long as you control your commander, {this} gets +2/+2")); - this.addEffect(new ConditionalContinuousEffect(effect, CommanderInPlayCondition.instance, effect.getText(null))); + public LieutenantAbility(ContinuousEffect effect, String text) { + super(Zone.BATTLEFIELD, new ConditionalContinuousEffect( + new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), + ControlYourCommanderCondition.instance, + "as long as you control your commander, {this} gets +2/+2" + )); + this.setAbilityWord(AbilityWord.LIEUTENANT); + this.addLieutenantEffect(effect, text); } - public LieutenantAbility(Effects effects) { - super(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), CommanderInPlayCondition.instance, "Lieutenant — As long as you control your commander, {this} gets +2/+2")); - for (Effect effect : effects) { - this.addEffect(new ConditionalContinuousEffect((ContinuousEffect) effect, CommanderInPlayCondition.instance, effect.getText(null))); - } + public LieutenantAbility addLieutenantEffect(ContinuousEffect effect, String text) { + this.addEffect(new ConditionalContinuousEffect(effect, ControlYourCommanderCondition.instance, text)); + return this; } protected LieutenantAbility(final LieutenantAbility ability) { @@ -38,4 +38,4 @@ public class LieutenantAbility extends SimpleStaticAbility { public LieutenantAbility copy() { return new LieutenantAbility(this); } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/CommanderInPlayCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CommanderInPlayCondition.java deleted file mode 100644 index 42f79a85e79..00000000000 --- a/Mage/src/main/java/mage/abilities/condition/common/CommanderInPlayCondition.java +++ /dev/null @@ -1,26 +0,0 @@ -package mage.abilities.condition.common; - -import mage.abilities.Ability; -import mage.abilities.condition.Condition; -import mage.game.Game; - -/** - * Checks if the player has its commander in play and controls it - * - * @author LevelX2 - */ -public enum CommanderInPlayCondition implements Condition { - - instance; - - @Override - public boolean apply(Game game, Ability source) { - return ControlYourCommanderCondition.instance.apply(game, source); - } - - @Override - public String toString() { - return "As long as you control your commander"; - } - -} diff --git a/Mage/src/main/java/mage/abilities/condition/common/ControlYourCommanderCondition.java b/Mage/src/main/java/mage/abilities/condition/common/ControlYourCommanderCondition.java index f737febba55..f51c1f2fa7d 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/ControlYourCommanderCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/ControlYourCommanderCondition.java @@ -3,10 +3,10 @@ package mage.abilities.condition.common; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.constants.CommanderCardType; +import mage.game.Controllable; import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.players.Player; -import java.util.Collection; import java.util.Objects; /** @@ -19,21 +19,18 @@ public enum ControlYourCommanderCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - return game.getPlayerList() + Player player = game.getPlayer(source.getControllerId()); + return player != null && game + .getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, true) .stream() - .map(game::getPlayer) - .filter(Objects::nonNull) - .map(player -> game.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, true)) // must search all card parts (example: mdf commander on battlefield) - .flatMap(Collection::stream) .map(game::getPermanent) .filter(Objects::nonNull) - .filter(Permanent::isPhasedIn) - .map(Permanent::getOwnerId) - .anyMatch(source.getControllerId()::equals); + .map(Controllable::getControllerId) + .anyMatch(source::isControlledBy); } @Override public String toString() { - return "If you control your commander"; + return "you control your commander"; } } -- 2.47.2 From d8b2d14bd2fd1c8c4db51d76b0fe4c25b2cdc6d9 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Wed, 16 Apr 2025 12:23:29 -0400 Subject: [PATCH 50/95] [TDM] fix Marang River Regent being able to target itself --- .../src/mage/cards/m/MarangRiverRegent.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MarangRiverRegent.java b/Mage.Sets/src/mage/cards/m/MarangRiverRegent.java index 967ce00e83d..268af85e29f 100644 --- a/Mage.Sets/src/mage/cards/m/MarangRiverRegent.java +++ b/Mage.Sets/src/mage/cards/m/MarangRiverRegent.java @@ -1,29 +1,36 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; -import mage.cards.OmenCard; -import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; -import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.OmenCard; import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; -import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; /** - * * @author Jmlundeen */ public final class MarangRiverRegent extends OmenCard { + private static final FilterPermanent filter = new FilterNonlandPermanent("other target nonland permanents"); + + static { + filter.add(AnotherPredicate.instance); + } + public MarangRiverRegent(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, new CardType[]{CardType.INSTANT}, "{4}{U}{U}", "Coil and Catch", "{3}{U}"); - + this.subtype.add(SubType.DRAGON); this.power = new MageInt(6); this.toughness = new MageInt(7); @@ -33,7 +40,7 @@ public final class MarangRiverRegent extends OmenCard { // When this creature enters, return up to two other target nonland permanents to their owners' hands. Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect()); - ability.addTarget(new TargetNonlandPermanent(0, 2)); + ability.addTarget(new TargetPermanent(0, 2, filter)); this.addAbility(ability); // Coil and Catch -- 2.47.2 From 9e55771b1fa015f76478b066f7ee85ce2a9a222b Mon Sep 17 00:00:00 2001 From: xenohedron <12538125+xenohedron@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:30:07 -0400 Subject: [PATCH 51/95] add test for "becomes blocked" ability --- .../mage/test/combat/BecomesBlockedTest.java | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 Mage.Tests/src/test/java/org/mage/test/combat/BecomesBlockedTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/BecomesBlockedTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/BecomesBlockedTest.java new file mode 100644 index 00000000000..6d30af4adbc --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/combat/BecomesBlockedTest.java @@ -0,0 +1,98 @@ +package org.mage.test.combat; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author xenohedron + */ +public class BecomesBlockedTest extends CardTestPlayerBase { + + private static final String trapRunner = "Trap Runner"; // 2/3 + private static final String blockAbility = "{T}: Target unblocked attacking creature becomes blocked"; + // {T}: Target unblocked attacking creature becomes blocked. + // Activate only during combat after blockers are declared. + // (This ability works on creatures that can’t be blocked.) + + private static final String somberwaldAlpha = "Somberwald Alpha"; // 3/2 + // Whenever a creature you control becomes blocked, it gets +1/+1 until end of turn. + + private static final String slitherBlade = "Slither Blade"; // 1/2 can't be blocked + private static final String forestwalker = "Somberwald Dryad"; // 2/2 forestwalk + + + @Test + public void testCantBeBlockedBecomesBlocked() { + addCard(Zone.BATTLEFIELD, playerA, slitherBlade); + addCard(Zone.BATTLEFIELD, playerA, somberwaldAlpha); + addCard(Zone.BATTLEFIELD, playerB, trapRunner); + + attack(1, playerA, slitherBlade, playerB); + activateAbility(1, PhaseStep.DECLARE_BLOCKERS, playerB, blockAbility, slitherBlade); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 20); + assertPowerToughness(playerA, slitherBlade, 2, 3); + } + + @Test + public void testForestwalkerBecomesBlockedNoForest() { + addCard(Zone.BATTLEFIELD, playerA, forestwalker); + addCard(Zone.BATTLEFIELD, playerA, somberwaldAlpha); + addCard(Zone.BATTLEFIELD, playerB, trapRunner); + + attack(1, playerA, forestwalker, playerB); + activateAbility(1, PhaseStep.DECLARE_BLOCKERS, playerB, blockAbility, forestwalker); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 20); + assertPowerToughness(playerA, forestwalker, 3, 3); + } + + @Test + public void testForestwalkerBecomesBlockedWithForest() { + addCard(Zone.BATTLEFIELD, playerA, forestwalker); + addCard(Zone.BATTLEFIELD, playerA, somberwaldAlpha); + addCard(Zone.BATTLEFIELD, playerB, trapRunner); + addCard(Zone.BATTLEFIELD, playerB, "Forest"); + + attack(1, playerA, forestwalker, playerB); + activateAbility(1, PhaseStep.DECLARE_BLOCKERS, playerB, blockAbility, forestwalker); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 20); + assertPowerToughness(playerA, forestwalker, 3, 3); + } + + @Test + public void testTrampleBlocked() { + String wurm = "Yavimaya Wurm"; // 6/4 trample + + addCard(Zone.BATTLEFIELD, playerA, wurm); + addCard(Zone.BATTLEFIELD, playerA, somberwaldAlpha); + addCard(Zone.BATTLEFIELD, playerB, trapRunner); + addCard(Zone.BATTLEFIELD, playerB, "Forest"); + + attack(1, playerA, wurm, playerB); + activateAbility(1, PhaseStep.DECLARE_BLOCKERS, playerB, blockAbility, wurm); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 13); + assertPowerToughness(playerA, wurm, 7, 5); + } + +} -- 2.47.2 From b6204cbf56e399d5f7ab194adac524966b96f269 Mon Sep 17 00:00:00 2001 From: xenohedron <12538125+xenohedron@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:35:23 -0400 Subject: [PATCH 52/95] fix #13546 (United Battlefront) --- Mage.Sets/src/mage/cards/u/UnitedBattlefront.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/u/UnitedBattlefront.java b/Mage.Sets/src/mage/cards/u/UnitedBattlefront.java index 6822ee42687..f743249a911 100644 --- a/Mage.Sets/src/mage/cards/u/UnitedBattlefront.java +++ b/Mage.Sets/src/mage/cards/u/UnitedBattlefront.java @@ -32,7 +32,7 @@ public final class UnitedBattlefront extends CardImpl { // Look at the top seven cards of your library. Put up to two noncreature, nonland permanent cards with mana value 3 or less from among them onto the battlefield. Put the rest on the bottom of your library in a random order. this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect( - 7, 1, filter, PutCards.BATTLEFIELD, PutCards.BOTTOM_RANDOM + 7, 2, filter, PutCards.BATTLEFIELD, PutCards.BOTTOM_RANDOM )); } -- 2.47.2 From 0e63c0a75a57097aad8e6a897fd3df167323509e Mon Sep 17 00:00:00 2001 From: xenohedron <12538125+xenohedron@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:58:38 -0400 Subject: [PATCH 53/95] refactor: fix name of test class --- ...YourControl.java => ExileAndReturnUnderYourControlTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Mage.Tests/src/test/java/org/mage/test/cards/control/{ExileAndReturnUnderYourControl.java => ExileAndReturnUnderYourControlTest.java} (99%) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/ExileAndReturnUnderYourControl.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/ExileAndReturnUnderYourControlTest.java similarity index 99% rename from Mage.Tests/src/test/java/org/mage/test/cards/control/ExileAndReturnUnderYourControl.java rename to Mage.Tests/src/test/java/org/mage/test/cards/control/ExileAndReturnUnderYourControlTest.java index 6695ce20845..5da74079cf0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/control/ExileAndReturnUnderYourControl.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/ExileAndReturnUnderYourControlTest.java @@ -17,7 +17,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author noxx */ -public class ExileAndReturnUnderYourControl extends CardTestPlayerBase { +public class ExileAndReturnUnderYourControlTest extends CardTestPlayerBase { @Test public void testPermanentControlEffect() { -- 2.47.2 From a5cd541490da0748d81a9f86107f24ea62974223 Mon Sep 17 00:00:00 2001 From: xenohedron <12538125+xenohedron@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:25:48 -0400 Subject: [PATCH 54/95] fix #13547 When drawing from the bottom of the library, don't reveal the card just because the top card is revealed --- Mage/src/main/java/mage/players/PlayerImpl.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 88e36b7c3fb..1ec2badc1b7 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -781,15 +781,17 @@ public abstract class PlayerImpl implements Player, Serializable { Card card = isDrawsFromBottom() ? getLibrary().drawFromBottom(game) : getLibrary().drawFromTop(game); if (card != null) { card.moveToZone(Zone.HAND, source, game, false); // if you want to use event.getSourceId() here then thinks x10 times - if (isTopCardRevealed()) { + if (isTopCardRevealed() && !isDrawsFromBottom()) { game.fireInformEvent(getLogName() + " draws a revealed card (" + card.getLogName() + ')'); } game.fireEvent(new DrewCardEvent(card.getId(), getId(), source, event)); numDrawn++; } } - if (!isTopCardRevealed() && numDrawn > 0) { - game.fireInformEvent(getLogName() + " draws " + CardUtil.numberToText(numDrawn, "a") + " card" + (numDrawn > 1 ? "s" : "")); + if ((!isTopCardRevealed() || isDrawsFromBottom()) && numDrawn > 0) { + game.fireInformEvent(getLogName() + " draws " + CardUtil.numberToText(numDrawn, "a") + + " card" + (numDrawn > 1 ? "s" : "") + + (isDrawsFromBottom() ? " from the bottom of their library" : "")); } // if this method was called from a replacement event, pass the number of cards back through // (uncomment conditions if correct ruling is to only count cards drawn by the same player) -- 2.47.2 From e8b6b0a15822b1fd188212aa2ae321ffa590d729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Krist=C3=B3f?= <20043803+balazskristof@users.noreply.github.com> Date: Thu, 17 Apr 2025 15:47:58 +0200 Subject: [PATCH 55/95] [FIN] Implement Sidequest: Catch a Fish / Cooking Campsite (#13440) --- .../src/mage/cards/c/CookingCampsite.java | 48 +++++++++++ .../src/mage/cards/s/SidequestCatchAFish.java | 86 +++++++++++++++++++ Mage.Sets/src/mage/sets/FinalFantasy.java | 2 + 3 files changed, 136 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/CookingCampsite.java create mode 100644 Mage.Sets/src/mage/cards/s/SidequestCatchAFish.java diff --git a/Mage.Sets/src/mage/cards/c/CookingCampsite.java b/Mage.Sets/src/mage/cards/c/CookingCampsite.java new file mode 100644 index 00000000000..a85066abeb2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CookingCampsite.java @@ -0,0 +1,48 @@ +package mage.cards.c; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +/** + * @author balazskristof + */ +public final class CookingCampsite extends CardImpl { + + public CookingCampsite(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.nightCard = true; + + // {T}: Add {W}. + this.addAbility(new WhiteManaAbility()); + + // {3}, {T}, Sacrifice an artifact: Put a +1/+1 counter on each creature you control. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new AddCountersAllEffect(CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE), new ManaCostsImpl<>("{3}") + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(StaticFilters.FILTER_PERMANENT_ARTIFACT_AN)); + this.addAbility(ability); + } + + private CookingCampsite(final CookingCampsite card) { + super(card); + } + + @Override + public CookingCampsite copy() { + return new CookingCampsite(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SidequestCatchAFish.java b/Mage.Sets/src/mage/cards/s/SidequestCatchAFish.java new file mode 100644 index 00000000000..a8c3b53caec --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SidequestCatchAFish.java @@ -0,0 +1,86 @@ +package mage.cards.s; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.token.FoodToken; +import mage.players.Player; + +/** + * @author balazskristof + */ +public final class SidequestCatchAFish extends CardImpl { + + public SidequestCatchAFish(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + this.secondSideCardClazz = mage.cards.c.CookingCampsite.class; + + // At the beginning of your upkeep, look at the top card of your library. If it's an artifact or creature card, you may reveal it and put it into your hand. If you put a card into your hand this way, create a Food token and transform this enchantment. + this.addAbility(new TransformAbility()); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SidequestCatchAFishEffect())); + } + + private SidequestCatchAFish(final SidequestCatchAFish card) { + super(card); + } + + @Override + public SidequestCatchAFish copy() { + return new SidequestCatchAFish(this); + } +} + +class SidequestCatchAFishEffect extends OneShotEffect { + + SidequestCatchAFishEffect() { + super(Outcome.Benefit); + staticText = "look at the top card of your library. " + + "If it's an artifact or creature card, you may reveal it and put it into your hand. " + + "If you put a card into your hand this way, create a Food token and transform this enchantment."; + } + + private SidequestCatchAFishEffect(final SidequestCatchAFishEffect effect) { + super(effect); + } + + @Override + public SidequestCatchAFishEffect copy() { + return new SidequestCatchAFishEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Card topCard = controller.getLibrary().getFromTop(game); + if (topCard == null) { + return false; + } + controller.lookAtCards("Top card of library", topCard, game); + if (topCard.isArtifact(game) || topCard.isCreature(game)) { + if (controller.chooseUse(Outcome.DrawCard, "Reveal " + topCard.getName() + " and put it into your hand?", source, game)) { + controller.revealCards(source, new CardsImpl(topCard), game); + controller.moveCards(topCard, Zone.HAND, source, game); + new FoodToken().putOntoBattlefield(1, game, source); + new TransformSourceEffect().apply(game, source); + return true; + } + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/FinalFantasy.java b/Mage.Sets/src/mage/sets/FinalFantasy.java index 463445ffd72..f221da50b7b 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasy.java +++ b/Mage.Sets/src/mage/sets/FinalFantasy.java @@ -20,9 +20,11 @@ public final class FinalFantasy extends ExpansionSet { this.blockName = "Final Fantasy"; // for sorting in GUI this.hasBasicLands = false; // temporary + cards.add(new SetCardInfo("Cooking Campsite", 31, Rarity.UNCOMMON, mage.cards.c.CookingCampsite.class)); cards.add(new SetCardInfo("Jumbo Cactuar", 191, Rarity.RARE, mage.cards.j.JumboCactuar.class)); cards.add(new SetCardInfo("Sazh's Chocobo", 200, Rarity.UNCOMMON, mage.cards.s.SazhsChocobo.class)); cards.add(new SetCardInfo("Sephiroth, Planet's Heir", 553, Rarity.MYTHIC, mage.cards.s.SephirothPlanetsHeir.class)); + cards.add(new SetCardInfo("Sidequest: Catch a Fish", 31, Rarity.UNCOMMON, mage.cards.s.SidequestCatchAFish.class)); cards.add(new SetCardInfo("Sin, Spira's Punishment", 242, Rarity.RARE, mage.cards.s.SinSpirasPunishment.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sin, Spira's Punishment", 348, Rarity.RARE, mage.cards.s.SinSpirasPunishment.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sin, Spira's Punishment", 508, Rarity.RARE, mage.cards.s.SinSpirasPunishment.class, NON_FULL_USE_VARIOUS)); -- 2.47.2 From fb86dac69a0f3f584d5b1a9f7f3ed81c6d5ee99b Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 09:22:58 -0400 Subject: [PATCH 56/95] [FIN] update spoiler --- Utils/mtg-cards-data.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 473865c8d09..022eed2dba1 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -57189,12 +57189,14 @@ Summon: Shiva|Final Fantasy|78|U|{3}{U}{U}|Enchantment Creature - Saga Elemental Cecil, Dark Knight|Final Fantasy|91|R|{B}|Legendary Creature - Human Knight|2|3|Deathtouch$Darkness -- Whenever Cecil deals damage, you lose that much life. Then if your life total is less than or equal to half your starting life total, untap Cecil and transform it.| Cecil, Redeemed Paladin|Final Fantasy|91|R||Legendary Creature - Human Knight|4|4|Lifelink$Protect -- Whenever Cecil attacks, other attacking creatures gain indestructible until end of turn.| Tonberry|Final Fantasy|122|U|{B}|Creature - Salamander Horror|2|1|This creature enters tapped with a stun counter on it.$Chef's Knife -- During your turn, this creature has first strike and deathtouch.| +Zell Dincht|Final Fantasy|170|R|{2}{R}|Legendary Creature - Human Monk|0|3|You may play an additional land on each of your turns.$Zell Dincht gets +1/+0 for each land you control.$At the beginning of your end step, return a land you control to its owner's hand.| Jumbo Cactuar|Final Fantasy|191|R|{5}{G}{G}|Creature - Plant|1|7|10,000 Needles -- Whenever this creature attacks, it gets +9999/+0 until end of turn.| Sazh's Chocobo|Final Fantasy|200|U|{G}|Creature - Bird|0|1|Landfall -- Whenever a land you control enters, put a +1/+1 counter on this creature.| Emet-Selch, Unsundered|Final Fantasy|218|M|{1}{U}{B}|Legendary Creature - Elder Wizard|2|4|Vigilance$Whenever Emet-Selch enters or attacks, draw a card, then discard a card.$At the beginning of your upkeep, if there are fourteen or more cards in your graveyard, you may transform Emet-Selch.| Hades, Sorcerer of Eld|Final Fantasy|218|M||Legendary Creature - Avatar|6|6|Vigilance$Echo of the Lost -- During your turn, you may play cards from your graveyard.$If a card or token would be put into your graveyard from anywhere, exile it instead.| Garland, Knight of Cornelia|Final Fantasy|221|U|{B}{R}|Legendary Creature - Human Knight|3|2|Whenever you cast a noncreature spell, surveil 1.${3}{B}{B}{R}{R}: Return this card from your graveyard to the battlefield transformed. Activate only as a sorcery.| Chaos, the Endless|Final Fantasy|221|U||Legendary Creature - Demon|5|5|Flying$When Chaos dies, put it on the bottom of its owner's library.| +Gladiolus Amicitia|Final Fantasy|224|U|{4}{R}{G}|Legendary Creature - Human Warrior|6|6|When Gladiolus Amicitia enters, search your library for a land card, put it onto the battlefield tapped, then shuffle.$Landfall -- Whenever a land you control enters, another target creature you control gets +2/+2 and gains trample until end of turn.| Sin, Spira's Punishment|Final Fantasy|242|R|{4}{B}{G}{U}|Legendary Creature - Leviathan Avatar|7|7|Flying$Whenever Sin enters or attacks, exile a permanent card from your graveyard at random, then create a tapped token that's a copy of that card. If the exiled card is a land card, repeat this process.| Cloud, Planet's Champion|Final Fantasy|552|M|{3}{R}{W}|Legendary Creature - Human Soldier Mercenary|4|4|During your turn, as long as Cloud is equipped, it has double strike and indestructible.$Equip abilities you activate that target Cloud cost {2} less to activate.| Sephiroth, Planet's Heir|Final Fantasy|553|M|{4}{U}{B}|Legendary Creature - Human Avatar Soldier|4|4|Vigilance$When Sephiroth enters, creatures your opponents control get -2/-2 until end of turn.$Whenever a creature an opponent controls dies, put a +1/+1 counter on Sephiroth.| -- 2.47.2 From a64aa3612492f665619d6bcc38be25101ff0fa53 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 09:26:31 -0400 Subject: [PATCH 57/95] [FIN] Implement Zell Dincht --- Mage.Sets/src/mage/cards/z/ZellDincht.java | 60 ++++++++++++++++++++++ Mage.Sets/src/mage/sets/FinalFantasy.java | 1 + 2 files changed, 61 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/z/ZellDincht.java diff --git a/Mage.Sets/src/mage/cards/z/ZellDincht.java b/Mage.Sets/src/mage/cards/z/ZellDincht.java new file mode 100644 index 00000000000..e58ec740387 --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZellDincht.java @@ -0,0 +1,60 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.LandsYouControlCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.ReturnToHandChosenControlledPermanentEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.PlayAdditionalLandsControllerEffect; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZellDincht extends CardImpl { + + public ZellDincht(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MONK); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // You may play an additional land on each of your turns. + this.addAbility(new SimpleStaticAbility( + new PlayAdditionalLandsControllerEffect(1, Duration.WhileOnBattlefield) + )); + + // Zell Dincht gets +1/+0 for each land you control. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( + LandsYouControlCount.instance, StaticValue.get(0), Duration.WhileOnBattlefield + ).setText("{this} gets +1/+0 for each land you control")).addHint(LandsYouControlHint.instance)); + + // At the beginning of your end step, return a land you control to its owner's hand. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new ReturnToHandChosenControlledPermanentEffect(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND) + )); + } + + private ZellDincht(final ZellDincht card) { + super(card); + } + + @Override + public ZellDincht copy() { + return new ZellDincht(this); + } +} diff --git a/Mage.Sets/src/mage/sets/FinalFantasy.java b/Mage.Sets/src/mage/sets/FinalFantasy.java index f221da50b7b..655210b334e 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasy.java +++ b/Mage.Sets/src/mage/sets/FinalFantasy.java @@ -32,5 +32,6 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Stiltzkin, Moogle Merchant", 327, Rarity.RARE, mage.cards.s.StiltzkinMoogleMerchant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Shiva", 78, Rarity.UNCOMMON, mage.cards.s.SummonShiva.class)); cards.add(new SetCardInfo("Tonberry", 122, Rarity.UNCOMMON, mage.cards.t.Tonberry.class)); + cards.add(new SetCardInfo("Zell Dincht", 170, Rarity.RARE, mage.cards.z.ZellDincht.class)); } } -- 2.47.2 From 1ae73e6376c43511bbe699073d23fd5f6c621f5d Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 09:29:22 -0400 Subject: [PATCH 58/95] [FIN] Implement Gladiolus Amicitia --- .../src/mage/cards/g/GladiolusAmicitia.java | 58 +++++++++++++++++++ Mage.Sets/src/mage/sets/FinalFantasy.java | 1 + 2 files changed, 59 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/g/GladiolusAmicitia.java diff --git a/Mage.Sets/src/mage/cards/g/GladiolusAmicitia.java b/Mage.Sets/src/mage/cards/g/GladiolusAmicitia.java new file mode 100644 index 00000000000..5694c998984 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GladiolusAmicitia.java @@ -0,0 +1,58 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.LandfallAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GladiolusAmicitia extends CardImpl { + + public GladiolusAmicitia(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // When Gladiolus Amicitia enters, search your library for a land card, put it onto the battlefield tapped, then shuffle. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_LAND), true) + )); + + // Landfall -- Whenever a land you control enters, another target creature you control gets +2/+2 and gains trample until end of turn. + Ability ability = new LandfallAbility(new BoostTargetEffect(2, 2) + .setText("another target creature you control gets +2/+2")); + ability.addEffect(new GainAbilityTargetEffect(TrampleAbility.getInstance()) + .setText("and gains trample until end of turn")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE_YOU_CONTROL)); + this.addAbility(ability); + } + + private GladiolusAmicitia(final GladiolusAmicitia card) { + super(card); + } + + @Override + public GladiolusAmicitia copy() { + return new GladiolusAmicitia(this); + } +} diff --git a/Mage.Sets/src/mage/sets/FinalFantasy.java b/Mage.Sets/src/mage/sets/FinalFantasy.java index 655210b334e..6f11fd0d46e 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasy.java +++ b/Mage.Sets/src/mage/sets/FinalFantasy.java @@ -21,6 +21,7 @@ public final class FinalFantasy extends ExpansionSet { this.hasBasicLands = false; // temporary cards.add(new SetCardInfo("Cooking Campsite", 31, Rarity.UNCOMMON, mage.cards.c.CookingCampsite.class)); + cards.add(new SetCardInfo("Gladiolus Amicitia", 224, Rarity.UNCOMMON, mage.cards.g.GladiolusAmicitia.class)); cards.add(new SetCardInfo("Jumbo Cactuar", 191, Rarity.RARE, mage.cards.j.JumboCactuar.class)); cards.add(new SetCardInfo("Sazh's Chocobo", 200, Rarity.UNCOMMON, mage.cards.s.SazhsChocobo.class)); cards.add(new SetCardInfo("Sephiroth, Planet's Heir", 553, Rarity.MYTHIC, mage.cards.s.SephirothPlanetsHeir.class)); -- 2.47.2 From 6932a798d8dfbaffba745b24de9ac6394fe0c970 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 09:43:33 -0400 Subject: [PATCH 59/95] [FIN] Implement Cloud, Planet's Champion --- .../mage/cards/c/CloudPlanetsChampion.java | 53 +++++++++++++++ .../src/mage/cards/f/FerventChampion.java | 56 ++-------------- Mage.Sets/src/mage/sets/FinalFantasy.java | 1 + .../ReduceCostEquipTargetSourceEffect.java | 65 +++++++++++++++++++ 4 files changed, 124 insertions(+), 51 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/c/CloudPlanetsChampion.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/cost/ReduceCostEquipTargetSourceEffect.java diff --git a/Mage.Sets/src/mage/cards/c/CloudPlanetsChampion.java b/Mage.Sets/src/mage/cards/c/CloudPlanetsChampion.java new file mode 100644 index 00000000000..baa85bb3ff0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CloudPlanetsChampion.java @@ -0,0 +1,53 @@ +package mage.cards.c; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.EquippedSourceCondition; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.cost.ReduceCostEquipTargetSourceEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.f.CloudPlanetsChampion; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; + +/** + * + * @author TheElk801 + */ +public final class CloudPlanetsChampion extends CardImpl { + + private static final Condition condition=new CompoundCondition(EquippedSourceCondition.instance, MyTurnCondition.instance); + public CloudPlanetsChampion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.subtype.add(SubType.MERCENARY); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // During your turn, as long as Cloud is equipped, it has double strike and indestructible. + Ability ability=new SimpleStaticAbility(new ConditionalContinuousEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance()),condition,"during your turn, as long as {this} is equipped, it has double strike"));ability.addEffect(new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance()),condition,"and indestructible"));this.addAbility(ability); + + // Equip abilities you activate that target Cloud cost {2} less to activate. + this.addAbility(new SimpleStaticAbility(new ReduceCostEquipTargetSourceEffect(2))); + } + + private CloudPlanetsChampion(final CloudPlanetsChampion card) { + super(card); + } + + @Override + public CloudPlanetsChampion copy() { + return new CloudPlanetsChampion(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FerventChampion.java b/Mage.Sets/src/mage/cards/f/FerventChampion.java index 828b931c715..5f75340076f 100644 --- a/Mage.Sets/src/mage/cards/f/FerventChampion.java +++ b/Mage.Sets/src/mage/cards/f/FerventChampion.java @@ -5,23 +5,20 @@ import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.abilities.keyword.EquipAbility; +import mage.abilities.effects.common.cost.ReduceCostEquipTargetSourceEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.AttackingPredicate; -import mage.game.Game; -import mage.target.Target; import mage.target.TargetPermanent; -import mage.util.CardUtil; -import java.util.Collection; import java.util.UUID; /** @@ -59,7 +56,7 @@ public final class FerventChampion extends CardImpl { this.addAbility(ability); // Equip abilities you activate that target Fervent Champion cost {3} less to activate. - this.addAbility(new SimpleStaticAbility(new FerventChampionEffect())); + this.addAbility(new SimpleStaticAbility(new ReduceCostEquipTargetSourceEffect(3))); } private FerventChampion(final FerventChampion card) { @@ -71,46 +68,3 @@ public final class FerventChampion extends CardImpl { return new FerventChampion(this); } } - -class FerventChampionEffect extends CostModificationEffectImpl { - - FerventChampionEffect() { - super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); - staticText = "equip abilities you activate that target {this} cost {3} less to activate"; - } - - private FerventChampionEffect(final FerventChampionEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source, Ability abilityToModify) { - CardUtil.reduceCost(abilityToModify, 3); - return true; - } - - @Override - public boolean applies(Ability abilityToModify, Ability source, Game game) { - if (abilityToModify instanceof EquipAbility - && abilityToModify.isControlledBy(source.getControllerId())) { - if (game != null && game.inCheckPlayableState()) { - return !abilityToModify.getTargets().isEmpty() && - abilityToModify.getTargets().get(0).canTarget(source.getSourceId(), abilityToModify, game); - } else { - return abilityToModify - .getTargets() - .stream() - .map(Target::getTargets) - .flatMap(Collection::stream) - .anyMatch(source.getSourceId()::equals); - } - - } - return false; - } - - @Override - public FerventChampionEffect copy() { - return new FerventChampionEffect(this); - } -} diff --git a/Mage.Sets/src/mage/sets/FinalFantasy.java b/Mage.Sets/src/mage/sets/FinalFantasy.java index 6f11fd0d46e..e9fe16a888a 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasy.java +++ b/Mage.Sets/src/mage/sets/FinalFantasy.java @@ -20,6 +20,7 @@ public final class FinalFantasy extends ExpansionSet { this.blockName = "Final Fantasy"; // for sorting in GUI this.hasBasicLands = false; // temporary + cards.add(new SetCardInfo("Cloud, Planet's Champion", 552, Rarity.MYTHIC, mage.cards.c.CloudPlanetsChampion.class)); cards.add(new SetCardInfo("Cooking Campsite", 31, Rarity.UNCOMMON, mage.cards.c.CookingCampsite.class)); cards.add(new SetCardInfo("Gladiolus Amicitia", 224, Rarity.UNCOMMON, mage.cards.g.GladiolusAmicitia.class)); cards.add(new SetCardInfo("Jumbo Cactuar", 191, Rarity.RARE, mage.cards.j.JumboCactuar.class)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/ReduceCostEquipTargetSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/ReduceCostEquipTargetSourceEffect.java new file mode 100644 index 00000000000..5f1f35190f5 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/ReduceCostEquipTargetSourceEffect.java @@ -0,0 +1,65 @@ +package mage.abilities.effects.common.cost; + +import mage.abilities.Ability; +import mage.abilities.keyword.EquipAbility; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.target.Target; +import mage.util.CardUtil; + +import java.util.Collection; + +/** + * @author TheElk801 + */ +public class ReduceCostEquipTargetSourceEffect extends CostModificationEffectImpl { + + private final int amount; + + public ReduceCostEquipTargetSourceEffect(int amount) { + super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST); + this.amount = amount; + staticText = "equip abilities you activate that target {this} cost {" + amount + "} less to activate"; + } + + private ReduceCostEquipTargetSourceEffect(final ReduceCostEquipTargetSourceEffect effect) { + super(effect); + this.amount = effect.amount; + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.reduceCost(abilityToModify, amount); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + if (!(abilityToModify instanceof EquipAbility) + || !abilityToModify.isControlledBy(source.getControllerId())) { + return false; + } + if (game != null && game.inCheckPlayableState()) { + return !abilityToModify + .getTargets() + .isEmpty() + && abilityToModify + .getTargets() + .get(0) + .canTarget(source.getSourceId(), abilityToModify, game); + } + return abilityToModify + .getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .anyMatch(source.getSourceId()::equals); + } + + @Override + public ReduceCostEquipTargetSourceEffect copy() { + return new ReduceCostEquipTargetSourceEffect(this); + } +} -- 2.47.2 From 378d6c6c223178e33e8ddff5b8566f0757e9352e Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 10:02:30 -0400 Subject: [PATCH 60/95] fix error --- .../mage/cards/c/CloudPlanetsChampion.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CloudPlanetsChampion.java b/Mage.Sets/src/mage/cards/c/CloudPlanetsChampion.java index baa85bb3ff0..693e185e037 100644 --- a/Mage.Sets/src/mage/cards/c/CloudPlanetsChampion.java +++ b/Mage.Sets/src/mage/cards/c/CloudPlanetsChampion.java @@ -1,6 +1,5 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -13,21 +12,24 @@ import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.effects.common.cost.ReduceCostEquipTargetSourceEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.IndestructibleAbility; -import mage.cards.f.CloudPlanetsChampion; -import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class CloudPlanetsChampion extends CardImpl { - private static final Condition condition=new CompoundCondition(EquippedSourceCondition.instance, MyTurnCondition.instance); + private static final Condition condition = new CompoundCondition(EquippedSourceCondition.instance, MyTurnCondition.instance); + public CloudPlanetsChampion(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{W}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); @@ -36,7 +38,15 @@ public final class CloudPlanetsChampion extends CardImpl { this.toughness = new MageInt(4); // During your turn, as long as Cloud is equipped, it has double strike and indestructible. - Ability ability=new SimpleStaticAbility(new ConditionalContinuousEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance()),condition,"during your turn, as long as {this} is equipped, it has double strike"));ability.addEffect(new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance()),condition,"and indestructible"));this.addAbility(ability); + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance()), condition, + "during your turn, as long as {this} is equipped, it has double strike" + )); + ability.addEffect(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(IndestructibleAbility.getInstance()), + condition, "and indestructible" + )); + this.addAbility(ability); // Equip abilities you activate that target Cloud cost {2} less to activate. this.addAbility(new SimpleStaticAbility(new ReduceCostEquipTargetSourceEffect(2))); -- 2.47.2 From b61bd22c85fcee5afa15a67ea5f077e38317a0db Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 17 Apr 2025 19:59:43 +0400 Subject: [PATCH 61/95] server: fixed rare error on player reconnection with tournament; --- .../mage/server/tournament/TournamentSession.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Mage.Server/src/main/java/mage/server/tournament/TournamentSession.java b/Mage.Server/src/main/java/mage/server/tournament/TournamentSession.java index 650c80900b9..4168b869409 100644 --- a/Mage.Server/src/main/java/mage/server/tournament/TournamentSession.java +++ b/Mage.Server/src/main/java/mage/server/tournament/TournamentSession.java @@ -73,7 +73,20 @@ public class TournamentSession { setupTimeout(timeout); managerFactory.userManager().getUser(userId).ifPresent(user -> { int remaining = (int) futureTimeout.getDelay(TimeUnit.SECONDS); - user.ccConstruct(tournament.getPlayer(playerId).getDeck(), tableId, null, remaining); + + // can be called on reconnection, so make sure tournament still active + Deck lastDeck = tournament.getPlayer(playerId).getDeck(); + if (!tournament.isDoneConstructing() && lastDeck != null) { + user.ccConstruct(lastDeck, tableId, null, remaining); + } else { + logger.error("Found bad state on reconnection: player has tournament session, but don't have deck" + + ", deck " + lastDeck + + ", user " + user.getName() + + ", state " + tournament.getTournamentState() + + ", t type " + tournament.getTournamentType() + + ", t id " + tournament.getId() + ); + } }); } } -- 2.47.2 From 33fb660dc803c29f9979ad60afda976e5a21793c Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 17 Apr 2025 20:09:54 +0400 Subject: [PATCH 62/95] server: improved logs for can't create table errors (instead No message) --- .../main/java/mage/server/MageServerImpl.java | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index 7edd074a29e..bb69e76ff44 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -29,7 +29,6 @@ import mage.server.managers.ManagerFactory; import mage.server.services.impl.FeedbackServiceImpl; import mage.server.tournament.TournamentFactory; import mage.server.util.ServerMessagesUtil; -import mage.utils.SystemUtil; import mage.utils.*; import mage.view.*; import mage.view.ChatMessage.MessageColor; @@ -1300,29 +1299,30 @@ public class MageServerImpl implements MageServer { @Override public TableView execute() throws MageException { - Optional session = managerFactory.sessionManager().getSession(sessionId); - if (!session.isPresent()) { + Session session = managerFactory.sessionManager().getSession(sessionId).orElse(null); + if (session == null) { return null; } - UUID userId = session.get().getUserId(); - Optional _user = managerFactory.userManager().getUser(userId); - if (!_user.isPresent()) { - logger.error("User for session not found. session = " + sessionId); + UUID userId = session.getUserId(); + User user = managerFactory.userManager().getUser(userId).orElse(null); + if (user == null) { return null; } - User user = _user.get(); + // check if user can create another table int notStartedTables = user.getNumberOfNotStartedTables(); if (notStartedTables > 1) { user.showUserMessage("Create table", "You have already " + notStartedTables + " not started tables. You can't create another."); - throw new MageException("No message"); + throw new MageException("User " + user.getName() + " can't create table: too much started"); } + // check if the user itself satisfies the quitRatio requirement. int quitRatio = options.getQuitRatio(); if (quitRatio < user.getMatchQuitRatio()) { user.showUserMessage("Create table", "Your quit ratio " + user.getMatchQuitRatio() + "% is higher than the table requirement " + quitRatio + '%'); - throw new MageException("No message"); + throw new MageException("User " + user.getName() + " can't create table: incompatible quit ratio"); } + // check if the user satisfies the minimumRating requirement. int minimumRating = options.getMinimumRating(); int userRating; @@ -1334,20 +1334,15 @@ public class MageServerImpl implements MageServer { if (userRating < minimumRating) { String message = new StringBuilder("Your rating ").append(userRating).append(" is lower than the table requirement ").append(minimumRating).toString(); user.showUserMessage("Create table", message); - throw new MageException("No message"); + throw new MageException("User " + user.getName() + " can't create table: incompatible rating"); } - Optional room = managerFactory.gamesRoomManager().getRoom(roomId); - if (room.isPresent()) { - TableView table = room.get().createTable(userId, options); - if (logger.isDebugEnabled()) { - logger.debug("TABLE created - tableId: " + table.getTableId() + ' ' + table.getTableName()); - logger.debug("- " + user.getName() + " userId: " + user.getId()); - logger.debug("- chatId: " + managerFactory.tableManager().getChatId(table.getTableId())); - } - return table; - } else { + + GamesRoom room = managerFactory.gamesRoomManager().getRoom(roomId).orElse(null); + if (room == null) { return null; } + + return room.createTable(userId, options); } } -- 2.47.2 From 571605e22443e1884f0a2784d4dfa4ee3bf11717 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 17 Apr 2025 21:05:53 +0400 Subject: [PATCH 63/95] server: now it disable draft's clicks protection timeout in test mode; --- .../src/main/java/mage/client/draft/DraftPanel.java | 9 +++++++-- Mage.Server/src/main/java/mage/server/Main.java | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java index 5b3a3696898..20eb1aee65e 100644 --- a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java +++ b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java @@ -45,8 +45,9 @@ /** * ms delay between booster showing up and pick being allowed. + * Will be disabled in test mode */ - private static final int protectionTime = 1500; + private static final int PROTECTION_CLICKS_TIMEOUT_MS = 1500; /** * Timer starting at booster being displayed, to protect from early pick due to clicking * a little too much on the last pick. @@ -138,7 +139,11 @@ } ); - protectionTimer = new Timer(protectionTime, e -> protectionTimer.stop()); + int protectionTimeout = PROTECTION_CLICKS_TIMEOUT_MS; + if (SessionHandler.isTestMode()) { + protectionTimeout = 100; + } + protectionTimer = new Timer(protectionTimeout, e -> protectionTimer.stop()); } public void cleanUp() { diff --git a/Mage.Server/src/main/java/mage/server/Main.java b/Mage.Server/src/main/java/mage/server/Main.java index 8a6e06ac66b..b603e88069b 100644 --- a/Mage.Server/src/main/java/mage/server/Main.java +++ b/Mage.Server/src/main/java/mage/server/Main.java @@ -85,7 +85,8 @@ public final class Main { // - fast game buttons; // - cheat commands; // - no deck validation; - // - no connection validation by pings (no disconnects on IDE's debugger usage) + // - no draft's clicks protection timeout; + // - no connection validation by pings (no disconnects on IDE's debugger usage); // - load any deck in sideboarding; // - simplified registration and login (no password check); // - debug main menu for GUI and rendering testing (must use -debug arg for client app); -- 2.47.2 From 72f9d1b8cbbe252f16c0bcdeb1218e4286f1c804 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 17 Apr 2025 21:08:56 +0400 Subject: [PATCH 64/95] server: fixed miss cheat button on reconnection in server's test mode; --- .../src/main/java/mage/remote/SessionImpl.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Mage.Common/src/main/java/mage/remote/SessionImpl.java b/Mage.Common/src/main/java/mage/remote/SessionImpl.java index 963c12a4600..2dd6b539ebe 100644 --- a/Mage.Common/src/main/java/mage/remote/SessionImpl.java +++ b/Mage.Common/src/main/java/mage/remote/SessionImpl.java @@ -269,6 +269,7 @@ public class SessionImpl implements Session { } if (result) { + // server state used in client side to setup game panels and dialogs, e.g. test mode info or available game types serverState = server.getServerState(); if (serverState == null) { throw new MageVersionException(client.getVersion(), null); @@ -600,6 +601,16 @@ public class SessionImpl implements Session { @Override public void handleCallback(Callback callback) throws HandleCallbackException { try { + // on connection client will receive all waiting callbacks from a server, e.g. started table, draft pick, etc + // but it's require to get server settings first (server state), e.g. for test mode + // possible bugs: hidden cheat button or enabled clicks protection in draft + // so wait for server state some time + if (serverState == null) { + ThreadUtils.sleep(1000); + if (serverState == null) { + logger.error("Can't receive server state before other data (possible reason: unstable network)"); + } + } client.onCallback((ClientCallback) callback.getCallbackObject()); } catch (Exception ex) { logger.error("handleCallback error", ex); -- 2.47.2 From c3b57f197361b6c1b007ebff261a4c5da65fbea6 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 17 Apr 2025 22:23:39 +0400 Subject: [PATCH 65/95] connection: fixed really disabled proxy usage (related to #13549); --- .../src/main/java/mage/client/remote/XmageURLConnection.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mage.Client/src/main/java/mage/client/remote/XmageURLConnection.java b/Mage.Client/src/main/java/mage/client/remote/XmageURLConnection.java index fb4c0aeb44a..4ff6869ee0e 100644 --- a/Mage.Client/src/main/java/mage/client/remote/XmageURLConnection.java +++ b/Mage.Client/src/main/java/mage/client/remote/XmageURLConnection.java @@ -129,6 +129,10 @@ public class XmageURLConnection { } this.proxy = Proxy.NO_PROXY; + if (!PreferencesDialog.NETWORK_ENABLE_PROXY_SUPPORT) { + return; + } + if (type != Proxy.Type.DIRECT) { try { String address = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_ADDRESS, ""); -- 2.47.2 From 92c5a65124bcae00ce06126cf01684d7e2f5cba8 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 17 Apr 2025 22:24:59 +0400 Subject: [PATCH 66/95] images: improved error logs on wrong image download in some use cases; --- .../mage/plugins/card/images/DownloadPicturesService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java index 1bb365f6b96..650eedf5e98 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java @@ -940,14 +940,13 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements logger.warn(err); } } - } catch (AccessDeniedException e) { incErrorCount(); logger.error("Can't access to files: " + card.getName() + "(" + card.getSet() + "). Try rebooting your system to remove the file lock."); } catch (Exception e) { incErrorCount(); - logger.error("Unknown error: " + e.getMessage(), e); - } finally { + String sampleUrl = (urls == null ? "null" : urls.getDownloadList().stream().findFirst().orElse(null)); + logger.error("Unknown error: " + e.getMessage() + ", sample url: " + sampleUrl, e); } synchronized (sync) { -- 2.47.2 From 48ebe421380673d2dfff451b5d58d29c6c2f729b Mon Sep 17 00:00:00 2001 From: xenohedron <12538125+xenohedron@users.noreply.github.com> Date: Thu, 17 Apr 2025 17:22:54 -0400 Subject: [PATCH 67/95] fix #13550 (Elspeth, Storm Slayer) --- Mage.Sets/src/mage/cards/e/ElspethStormSlayer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/e/ElspethStormSlayer.java b/Mage.Sets/src/mage/cards/e/ElspethStormSlayer.java index cbf61ff0f3a..1dfd7c96b7f 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethStormSlayer.java +++ b/Mage.Sets/src/mage/cards/e/ElspethStormSlayer.java @@ -52,9 +52,9 @@ public final class ElspethStormSlayer extends CardImpl { CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE ), 0); ability.addEffect(new GainAbilityControlledEffect( - FlyingAbility.getInstance(), Duration.EndOfTurn, + FlyingAbility.getInstance(), Duration.UntilYourNextTurn, StaticFilters.FILTER_CONTROLLED_CREATURE - ).setText("those creatures gain flying until end of turn")); + ).setText("those creatures gain flying until your next turn")); this.addAbility(ability); // -3: Destroy target creature an opponent controls with mana value 3 or greater. -- 2.47.2 From 9f87efe784e01f25a946819cd7e83f4de5b53fdd Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 13:21:53 -0400 Subject: [PATCH 68/95] [FIN] Implement Garland, Knight of Cornelia --- .../src/mage/cards/c/ChaosTheEndless.java | 48 ++++++++++ .../mage/cards/g/GarlandKnightOfCornelia.java | 90 +++++++++++++++++++ Mage.Sets/src/mage/sets/FinalFantasy.java | 2 + 3 files changed, 140 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/ChaosTheEndless.java create mode 100644 Mage.Sets/src/mage/cards/g/GarlandKnightOfCornelia.java diff --git a/Mage.Sets/src/mage/cards/c/ChaosTheEndless.java b/Mage.Sets/src/mage/cards/c/ChaosTheEndless.java new file mode 100644 index 00000000000..cb222c37a81 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChaosTheEndless.java @@ -0,0 +1,48 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.PutOnLibrarySourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChaosTheEndless extends CardImpl { + + public ChaosTheEndless(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.DEMON); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + this.color.setBlack(true); + this.color.setRed(true); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Chaos dies, put it on the bottom of its owner's library. + this.addAbility(new DiesSourceTriggeredAbility(new PutOnLibrarySourceEffect( + false, "put it on the bottom of its owner's library" + ), false)); + } + + private ChaosTheEndless(final ChaosTheEndless card) { + super(card); + } + + @Override + public ChaosTheEndless copy() { + return new ChaosTheEndless(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GarlandKnightOfCornelia.java b/Mage.Sets/src/mage/cards/g/GarlandKnightOfCornelia.java new file mode 100644 index 00000000000..80580939181 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GarlandKnightOfCornelia.java @@ -0,0 +1,90 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GarlandKnightOfCornelia extends CardImpl { + + public GarlandKnightOfCornelia(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{R}"); + + this.secondSideCardClazz = mage.cards.c.ChaosTheEndless.class; + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever you cast a noncreature spell, surveil 1. + this.addAbility(new SpellCastControllerTriggeredAbility( + new SurveilEffect(1), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + )); + + // {3}{B}{B}{R}{R}: Return this card from your graveyard to the battlefield transformed. Activate only as a sorcery. + this.addAbility(new TransformAbility()); + this.addAbility(new ActivateAsSorceryActivatedAbility( + Zone.GRAVEYARD, new GarlandKnightOfCorneliaEffect(), new ManaCostsImpl<>("{3}{B}{B}{R}{R}") + )); + } + + private GarlandKnightOfCornelia(final GarlandKnightOfCornelia card) { + super(card); + } + + @Override + public GarlandKnightOfCornelia copy() { + return new GarlandKnightOfCornelia(this); + } +} + +class GarlandKnightOfCorneliaEffect extends OneShotEffect { + + GarlandKnightOfCorneliaEffect() { + super(Outcome.Benefit); + staticText = "return this card from your graveyard to the battlefield transformed"; + } + + private GarlandKnightOfCorneliaEffect(final GarlandKnightOfCorneliaEffect effect) { + super(effect); + } + + @Override + public GarlandKnightOfCorneliaEffect copy() { + return new GarlandKnightOfCorneliaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = Optional + .ofNullable(source.getSourceObjectIfItStillExists(game)) + .filter(Card.class::isInstance) + .map(Card.class::cast) + .orElse(null); + if (player == null || card == null) { + return false; + } + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId(), Boolean.TRUE); + return player.moveCards(card, Zone.BATTLEFIELD, source, game); + } +} diff --git a/Mage.Sets/src/mage/sets/FinalFantasy.java b/Mage.Sets/src/mage/sets/FinalFantasy.java index e9fe16a888a..4e01374f3d6 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasy.java +++ b/Mage.Sets/src/mage/sets/FinalFantasy.java @@ -20,8 +20,10 @@ public final class FinalFantasy extends ExpansionSet { this.blockName = "Final Fantasy"; // for sorting in GUI this.hasBasicLands = false; // temporary + cards.add(new SetCardInfo("Chaos, the Endless", 221, Rarity.UNCOMMON, mage.cards.c.ChaosTheEndless.class)); cards.add(new SetCardInfo("Cloud, Planet's Champion", 552, Rarity.MYTHIC, mage.cards.c.CloudPlanetsChampion.class)); cards.add(new SetCardInfo("Cooking Campsite", 31, Rarity.UNCOMMON, mage.cards.c.CookingCampsite.class)); + cards.add(new SetCardInfo("Garland, Knight of Cornelia", 221, Rarity.UNCOMMON, mage.cards.g.GarlandKnightOfCornelia.class)); cards.add(new SetCardInfo("Gladiolus Amicitia", 224, Rarity.UNCOMMON, mage.cards.g.GladiolusAmicitia.class)); cards.add(new SetCardInfo("Jumbo Cactuar", 191, Rarity.RARE, mage.cards.j.JumboCactuar.class)); cards.add(new SetCardInfo("Sazh's Chocobo", 200, Rarity.UNCOMMON, mage.cards.s.SazhsChocobo.class)); -- 2.47.2 From c4eaafc6b19ad94a0eb7e4f1d113b8fea870d9e4 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 18:02:30 -0400 Subject: [PATCH 69/95] [SPE] add set --- .../dl/sources/ScryfallImageSupportCards.java | 1 + .../mage/sets/MarvelsSpiderManEternal.java | 22 +++++++++++++++++++ Utils/known-sets.txt | 1 + Utils/mtg-cards-data.txt | 6 +++++ Utils/mtg-sets-data.txt | 1 + 5 files changed, 31 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java index f598d797970..c4065a9b0bc 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java @@ -583,6 +583,7 @@ public class ScryfallImageSupportCards { add("TDC"); // Tarkir: Dragonstorm Commander add("FIN"); // Final Fantasy add("FIC"); // Final Fantasy Commander + add("SPE"); // Marvel's Spider-Man Eternal // Custom sets using Scryfall images - must provide a direct link for each card in directDownloadLinks add("CALC"); // Custom Alchemized versions of existing cards diff --git a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java new file mode 100644 index 00000000000..3d25217df4d --- /dev/null +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java @@ -0,0 +1,22 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class MarvelsSpiderManEternal extends ExpansionSet { + + private static final MarvelsSpiderManEternal instance = new MarvelsSpiderManEternal(); + + public static MarvelsSpiderManEternal getInstance() { + return instance; + } + + private MarvelsSpiderManEternal() { + super("Marvel's Spider-Man Eternal", "SPE", ExpansionSet.buildDate(2025, 9, 26), SetType.EXPANSION); + this.blockName = "Marvel's Spider-Man"; // for sorting in GUI + this.hasBasicLands = false; // temporary + } +} diff --git a/Utils/known-sets.txt b/Utils/known-sets.txt index ffae098c575..1a0b1a4d250 100644 --- a/Utils/known-sets.txt +++ b/Utils/known-sets.txt @@ -173,6 +173,7 @@ Magic Origins|MagicOrigins| Magic Player Rewards|MagicPlayerRewards| Magic: The Gathering-Commander|Commander| Magic: The Gathering-Conspiracy|Conspiracy| +Marvel's Spider-Man Eternal|MarvelsSpiderManEternal| Masterpiece Series|MasterpieceSeries| Masterpiece Series Amonkhet|MasterpieceSeriesAmonkhet| Masters Edition|MastersEdition| diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 022eed2dba1..811447a7747 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -57861,3 +57861,9 @@ Vault of the Archangel|Tarkir: Dragonstorm Commander|410|R||Land|||{T}: Add {C}. Windbrisk Heights|Tarkir: Dragonstorm Commander|411|R||Land|||Hideaway 4$This land enters tapped.${T}: Add {W}.${W}, {T}: You may play the exiled card without paying its mana cost if you attacked with three or more creatures this turn.| Woodland Cemetery|Tarkir: Dragonstorm Commander|412|R||Land|||This land enters tapped unless you control a Swamp or a Forest.${T}: Add {B} or {G}.| Yavimaya Coast|Tarkir: Dragonstorm Commander|413|R||Land|||{T}: Add {C}.${T}: Add {G} or {U}. This land deals 1 damage to you.| +Grasping Tentacles|Marvel's Spider-Man Eternal|21|R|{1}{U}{B}|Sorcery|||Target opponent mills eight cards. You may put an artifact card from that player's graveyard onto the battlefield under your control.| +Venom, Deadly Devourer|Marvel's Spider-Man Eternal|22|R|{2}{B}{G}|Legendary Creature - Symbiote Villain|4|4|Vigilance, menace${3}: Exile target creature card from a graveyard. When you do, put X +1/+1 counters on target Symbiote, where X is the exiled card's toughness.| +Green Goblin, Nemesis|Marvel's Spider-Man Eternal|23|R|{2}{B}{R}|Legendary Creature - Goblin Human Villain|3|3|Flying$Whenever you discard a nonland card, put a +1/+1 counter on target Goblin you control.$Whenever you discard a land card, create a tapped Treasure token.| +Doc Ock, Evil Inventor|Marvel's Spider-Man Eternal|24|R|{5}{U}{B}|Legendary Creature - Human Scientist Villain|8|8|At the beginning of combat on your turn, target noncreature artifact you control becomes an 8/8 Robot Villain artifact creature in addition to its other types.| +Sensational Spider-Man|Marvel's Spider-Man Eternal|25|R|{1}{W}{U}|Legendary Creature - Spider Human Hero|3|3|Whenever Sensational Spider-Man attacks, tap target creature defending player controls and put a stun counter on it. Then you may remove up to three stun counters from among all permanents. Draw cards equal to the number of stun counters removed this way.| +Pumpkin Bombs|Marvel's Spider-Man Eternal|26|R|{1}{R}|Artifact|||{T}, Discard two cards: Draw three cards, then put a fuse counter on this artifact. It deals damage equal to the number of fuse counters on it to target opponent. They gain control of this artifact.| diff --git a/Utils/mtg-sets-data.txt b/Utils/mtg-sets-data.txt index 0aee6b40f99..af90b04b5b6 100644 --- a/Utils/mtg-sets-data.txt +++ b/Utils/mtg-sets-data.txt @@ -173,6 +173,7 @@ Masters 25|A25| Magic: The Gathering-Commander|CMD| Magic: The Gathering-Conspiracy|CNS| Media Inserts|MBP| +Marvel's Spider-Man Eternal|SPE| March of the Machine|MOM| March of the Machine Commander|MOC| March of the Machine: The Aftermath|MAT| -- 2.47.2 From c5275befb0bacb0b0caff99f76219d5e0ff01178 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 18:07:57 -0400 Subject: [PATCH 70/95] [SPE] Implement Grasping Tentacles --- .../src/mage/cards/g/GraspingTentacles.java | 76 +++++++++++++++++++ .../mage/sets/MarvelsSpiderManEternal.java | 3 + 2 files changed, 79 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/g/GraspingTentacles.java diff --git a/Mage.Sets/src/mage/cards/g/GraspingTentacles.java b/Mage.Sets/src/mage/cards/g/GraspingTentacles.java new file mode 100644 index 00000000000..fe1ffe6f2d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GraspingTentacles.java @@ -0,0 +1,76 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.MillCardsTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GraspingTentacles extends CardImpl { + + public GraspingTentacles(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}{B}"); + + // Target opponent mills eight cards. You may put an artifact card from that player's graveyard onto the battlefield under your control. + this.getSpellAbility().addEffect(new MillCardsTargetEffect(8)); + this.getSpellAbility().addEffect(new GraspingTentaclesEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + private GraspingTentacles(final GraspingTentacles card) { + super(card); + } + + @Override + public GraspingTentacles copy() { + return new GraspingTentacles(this); + } +} + +class GraspingTentaclesEffect extends OneShotEffect { + + GraspingTentaclesEffect() { + super(Outcome.Benefit); + staticText = "you may put an artifact card from that player's " + + "graveyard onto the battlefield under your control"; + } + + private GraspingTentaclesEffect(final GraspingTentaclesEffect effect) { + super(effect); + } + + @Override + public GraspingTentaclesEffect copy() { + return new GraspingTentaclesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (controller == null || opponent == null) { + return false; + } + TargetCard target = new TargetCardInGraveyard( + 0, 1, StaticFilters.FILTER_CARD_ARTIFACT, true + ); + controller.choose(Outcome.PutCardInPlay, opponent.getGraveyard(), target, source, game); + Card card = game.getCard(target.getFirstTarget()); + return card != null && controller.moveCards(card, Zone.BATTLEFIELD, source, game); + } +} diff --git a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java index 3d25217df4d..df0f9a2a0a1 100644 --- a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java @@ -1,6 +1,7 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.constants.Rarity; import mage.constants.SetType; /** @@ -18,5 +19,7 @@ public final class MarvelsSpiderManEternal extends ExpansionSet { super("Marvel's Spider-Man Eternal", "SPE", ExpansionSet.buildDate(2025, 9, 26), SetType.EXPANSION); this.blockName = "Marvel's Spider-Man"; // for sorting in GUI this.hasBasicLands = false; // temporary + + cards.add(new SetCardInfo("Grasping Tentacles", 21, Rarity.RARE, mage.cards.g.GraspingTentacles.class)); } } -- 2.47.2 From 93b08ef78deaf7c8041104972d2347c5d69b5007 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 18:14:07 -0400 Subject: [PATCH 71/95] [SPE] Implement Venom, Deadly Devourer --- .../src/mage/cards/v/VenomDeadlyDevourer.java | 97 +++++++++++++++++++ .../mage/sets/MarvelsSpiderManEternal.java | 1 + .../src/main/java/mage/constants/SubType.java | 1 + 3 files changed, 99 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/v/VenomDeadlyDevourer.java diff --git a/Mage.Sets/src/mage/cards/v/VenomDeadlyDevourer.java b/Mage.Sets/src/mage/cards/v/VenomDeadlyDevourer.java new file mode 100644 index 00000000000..f519b2083e4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VenomDeadlyDevourer.java @@ -0,0 +1,97 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VenomDeadlyDevourer extends CardImpl { + + public VenomDeadlyDevourer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SYMBIOTE); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Menace + this.addAbility(new MenaceAbility()); + + // {3}: Exile target creature card from a graveyard. When you do, put X +1/+1 counters on target Symbiote, where X is the exiled card's toughness. + Ability ability = new SimpleActivatedAbility(new VenomDeadlyDevourerEffect(), new GenericManaCost(3)); + ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE_A_GRAVEYARD)); + this.addAbility(ability); + } + + private VenomDeadlyDevourer(final VenomDeadlyDevourer card) { + super(card); + } + + @Override + public VenomDeadlyDevourer copy() { + return new VenomDeadlyDevourer(this); + } +} + +class VenomDeadlyDevourerEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent(SubType.SYMBIOTE, "Symbiote"); + + VenomDeadlyDevourerEffect() { + super(Outcome.Benefit); + staticText = "exile target creature card from a graveyard. When you do, " + + "put X +1/+1 counters on target Symbiote, where X is the exiled card's toughness"; + } + + private VenomDeadlyDevourerEffect(final VenomDeadlyDevourerEffect effect) { + super(effect); + } + + @Override + public VenomDeadlyDevourerEffect copy() { + return new VenomDeadlyDevourerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (player == null || card == null) { + return false; + } + int toughness = card.getToughness().getValue(); + player.moveCards(card, Zone.EXILED, source, game); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance(toughness)), false + ); + ability.addTarget(new TargetPermanent(filter)); + game.fireReflexiveTriggeredAbility(ability, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java index df0f9a2a0a1..f414e72ce46 100644 --- a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java @@ -21,5 +21,6 @@ public final class MarvelsSpiderManEternal extends ExpansionSet { this.hasBasicLands = false; // temporary cards.add(new SetCardInfo("Grasping Tentacles", 21, Rarity.RARE, mage.cards.g.GraspingTentacles.class)); + cards.add(new SetCardInfo("Venom, Deadly Devourer", 22, Rarity.RARE, mage.cards.v.VenomDeadlyDevourer.class)); } } diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index cbf9f006c19..727dc6e793b 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -394,6 +394,7 @@ public enum SubType { SULLUSTAN("Sullustan", SubTypeSet.CreatureType, true), // Star Wars SURRAKAR("Surrakar", SubTypeSet.CreatureType), SURVIVOR("Survivor", SubTypeSet.CreatureType), + SYMBIOTE("Symbiote", SubTypeSet.CreatureType), SYNTH("Synth", SubTypeSet.CreatureType), // T TENTACLE("Tentacle", SubTypeSet.CreatureType), -- 2.47.2 From 9e3ece290f75a857fa8a964f148bb84cebe4524e Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 18:22:34 -0400 Subject: [PATCH 72/95] [SPE] Implement Doc Ock, Evil Inventor --- .../src/mage/cards/d/DocOckEvilInventor.java | 63 +++++++++++++++++++ .../mage/sets/MarvelsSpiderManEternal.java | 1 + 2 files changed, 64 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DocOckEvilInventor.java diff --git a/Mage.Sets/src/mage/cards/d/DocOckEvilInventor.java b/Mage.Sets/src/mage/cards/d/DocOckEvilInventor.java new file mode 100644 index 00000000000..a1ddc150ec3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DocOckEvilInventor.java @@ -0,0 +1,63 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.Predicates; +import mage.game.permanent.token.custom.CreatureToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DocOckEvilInventor extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledArtifactPermanent("noncreature artifact you control"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + public DocOckEvilInventor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SCIENTIST); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // At the beginning of combat on your turn, target noncreature artifact you control becomes an 8/8 Robot Villain artifact creature in addition to its other types. + Ability ability = new BeginningOfCombatTriggeredAbility(new BecomesCreatureTargetEffect( + new CreatureToken( + 8, 8, + "8/8 Robot Villain artifact creature", + SubType.ROBOT, SubType.VILLAIN + ), false, false, Duration.Custom + )); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private DocOckEvilInventor(final DocOckEvilInventor card) { + super(card); + } + + @Override + public DocOckEvilInventor copy() { + return new DocOckEvilInventor(this); + } +} diff --git a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java index f414e72ce46..dc1f56e8146 100644 --- a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java @@ -20,6 +20,7 @@ public final class MarvelsSpiderManEternal extends ExpansionSet { this.blockName = "Marvel's Spider-Man"; // for sorting in GUI this.hasBasicLands = false; // temporary + cards.add(new SetCardInfo("Doc Ock, Evil Inventor", 24, Rarity.RARE, mage.cards.d.DocOckEvilInventor.class)); cards.add(new SetCardInfo("Grasping Tentacles", 21, Rarity.RARE, mage.cards.g.GraspingTentacles.class)); cards.add(new SetCardInfo("Venom, Deadly Devourer", 22, Rarity.RARE, mage.cards.v.VenomDeadlyDevourer.class)); } -- 2.47.2 From ad9373b630fd404c19a822b3232c4e727517c02a Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 18:30:34 -0400 Subject: [PATCH 73/95] [SPE] Implement Pumpkin Bombs --- Mage.Sets/src/mage/cards/p/PumpkinBombs.java | 52 +++++++++++++++++++ .../mage/sets/MarvelsSpiderManEternal.java | 1 + 2 files changed, 53 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/p/PumpkinBombs.java diff --git a/Mage.Sets/src/mage/cards/p/PumpkinBombs.java b/Mage.Sets/src/mage/cards/p/PumpkinBombs.java new file mode 100644 index 00000000000..fd500622f07 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PumpkinBombs.java @@ -0,0 +1,52 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.TargetPlayerGainControlSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PumpkinBombs extends CardImpl { + + private static final DynamicValue xValue = new CountersSourceCount(CounterType.FUSE); + + public PumpkinBombs(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{R}"); + + // {T}, Discard two cards: Draw three cards, then put a fuse counter on this artifact. It deals damage equal to the number of fuse counters on it to target opponent. They gain control of this artifact. + Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(3), new TapSourceCost()); + ability.addCost(new DiscardTargetCost(new TargetCardInHand(2, StaticFilters.FILTER_CARD_CARDS))); + ability.addEffect(new AddCountersSourceEffect(CounterType.FUSE.createInstance()).concatBy(", then")); + ability.addEffect(new DamageTargetEffect(xValue) + .setText("it deals damage equal to the number of fuse counters on it to target opponent")); + ability.addEffect(new TargetPlayerGainControlSourceEffect("they")); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private PumpkinBombs(final PumpkinBombs card) { + super(card); + } + + @Override + public PumpkinBombs copy() { + return new PumpkinBombs(this); + } +} diff --git a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java index dc1f56e8146..2676edd4e49 100644 --- a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java @@ -22,6 +22,7 @@ public final class MarvelsSpiderManEternal extends ExpansionSet { cards.add(new SetCardInfo("Doc Ock, Evil Inventor", 24, Rarity.RARE, mage.cards.d.DocOckEvilInventor.class)); cards.add(new SetCardInfo("Grasping Tentacles", 21, Rarity.RARE, mage.cards.g.GraspingTentacles.class)); + cards.add(new SetCardInfo("Pumpkin Bombs", 26, Rarity.RARE, mage.cards.p.PumpkinBombs.class)); cards.add(new SetCardInfo("Venom, Deadly Devourer", 22, Rarity.RARE, mage.cards.v.VenomDeadlyDevourer.class)); } } -- 2.47.2 From 31c1a13b190d7aac5a5e5640686bea7f062df956 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 18:35:40 -0400 Subject: [PATCH 74/95] [SPE] Implement Green Goblin, Nemesis --- .../src/mage/cards/g/GreenGoblinNemesis.java | 66 +++++++++++++++++++ .../mage/sets/MarvelsSpiderManEternal.java | 1 + 2 files changed, 67 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/g/GreenGoblinNemesis.java diff --git a/Mage.Sets/src/mage/cards/g/GreenGoblinNemesis.java b/Mage.Sets/src/mage/cards/g/GreenGoblinNemesis.java new file mode 100644 index 00000000000..9a7768da346 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GreenGoblinNemesis.java @@ -0,0 +1,66 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DiscardCardControllerTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.game.permanent.token.TreasureToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GreenGoblinNemesis extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.GOBLIN); + + public GreenGoblinNemesis(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever you discard a nonland card, put a +1/+1 counter on target Goblin you control. + Ability ability = new DiscardCardControllerTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), + false, StaticFilters.FILTER_CARD_A_NON_LAND + ); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + + // Whenever you discard a land card, create a tapped Treasure token. + this.addAbility(new DiscardCardControllerTriggeredAbility( + new CreateTokenEffect(new TreasureToken(), 1, true), + false, StaticFilters.FILTER_CARD_LAND_A + )); + } + + private GreenGoblinNemesis(final GreenGoblinNemesis card) { + super(card); + } + + @Override + public GreenGoblinNemesis copy() { + return new GreenGoblinNemesis(this); + } +} diff --git a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java index 2676edd4e49..144c6239e64 100644 --- a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java @@ -22,6 +22,7 @@ public final class MarvelsSpiderManEternal extends ExpansionSet { cards.add(new SetCardInfo("Doc Ock, Evil Inventor", 24, Rarity.RARE, mage.cards.d.DocOckEvilInventor.class)); cards.add(new SetCardInfo("Grasping Tentacles", 21, Rarity.RARE, mage.cards.g.GraspingTentacles.class)); + cards.add(new SetCardInfo("Green Goblin, Nemesis", 23, Rarity.RARE, mage.cards.g.GreenGoblinNemesis.class)); cards.add(new SetCardInfo("Pumpkin Bombs", 26, Rarity.RARE, mage.cards.p.PumpkinBombs.class)); cards.add(new SetCardInfo("Venom, Deadly Devourer", 22, Rarity.RARE, mage.cards.v.VenomDeadlyDevourer.class)); } -- 2.47.2 From f3c53b884a28fabc881a439b2453550f4a8dda3d Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 18:36:06 -0400 Subject: [PATCH 75/95] [SPE] fix legality --- Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java index 144c6239e64..3fd834c9315 100644 --- a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java @@ -16,7 +16,7 @@ public final class MarvelsSpiderManEternal extends ExpansionSet { } private MarvelsSpiderManEternal() { - super("Marvel's Spider-Man Eternal", "SPE", ExpansionSet.buildDate(2025, 9, 26), SetType.EXPANSION); + super("Marvel's Spider-Man Eternal", "SPE", ExpansionSet.buildDate(2025, 9, 26), SetType.SUPPLEMENTAL); this.blockName = "Marvel's Spider-Man"; // for sorting in GUI this.hasBasicLands = false; // temporary -- 2.47.2 From 5e27be4dfadfa757e97a8239167003af6a562d77 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Thu, 17 Apr 2025 20:52:49 -0400 Subject: [PATCH 76/95] [SPE] Implement Sensational Spider-Man --- .../mage/cards/s/SensationalSpiderMan.java | 114 ++++++++++++++++++ .../mage/sets/MarvelsSpiderManEternal.java | 1 + Mage/src/main/java/mage/cards/Card.java | 10 +- Mage/src/main/java/mage/cards/CardImpl.java | 15 ++- Mage/src/main/java/mage/game/stack/Spell.java | 8 +- 5 files changed, 132 insertions(+), 16 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/s/SensationalSpiderMan.java diff --git a/Mage.Sets/src/mage/cards/s/SensationalSpiderMan.java b/Mage.Sets/src/mage/cards/s/SensationalSpiderMan.java new file mode 100644 index 00000000000..0549474c5a1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SensationalSpiderMan.java @@ -0,0 +1,114 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.DefendingPlayerControlsSourceAttackingPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetPermanentAmount; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SensationalSpiderMan extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature defending player controls"); + + static { + filter.add(DefendingPlayerControlsSourceAttackingPredicate.instance); + } + + public SensationalSpiderMan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever Sensational Spider-Man attacks, tap target creature defending player controls and put a stun counter on it. Then you may remove up to three stun counters from among all permanents. Draw cards equal to the number of stun counters removed this way. + Ability ability = new AttacksTriggeredAbility(new TapTargetEffect()); + ability.addEffect(new AddCountersTargetEffect(CounterType.STUN.createInstance()).setText("and put a stun counter on it")); + ability.addEffect(new SensationalSpiderManEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private SensationalSpiderMan(final SensationalSpiderMan card) { + super(card); + } + + @Override + public SensationalSpiderMan copy() { + return new SensationalSpiderMan(this); + } +} + +class SensationalSpiderManEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent("permanents with stun counters on them"); + + static { + filter.add(CounterType.STUN.getPredicate()); + } + + SensationalSpiderManEffect() { + super(Outcome.Benefit); + staticText = "then you may remove up to three stun counters from among all permanents. " + + "Draw cards equal to the number of stun counters removed this way"; + } + + private SensationalSpiderManEffect(final SensationalSpiderManEffect effect) { + super(effect); + } + + @Override + public SensationalSpiderManEffect copy() { + return new SensationalSpiderManEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + // TODO: this ideally would be able to prevent the player from choosing a number greater than the number of stun counters available to remove + TargetPermanentAmount target = new TargetPermanentAmount(3, 0, filter); + target.withNotTarget(true); + player.choose(outcome, target, source, game); + int amountRemoved = target + .getTargets() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .mapToInt(permanent -> permanent.removeCounters( + CounterType.STUN.createInstance(target.getTargetAmount(permanent.getId())), source, game + )) + .sum(); + if (amountRemoved > 1) { + player.drawCards(amountRemoved, source, game); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java index 3fd834c9315..c715cc22e54 100644 --- a/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderManEternal.java @@ -24,6 +24,7 @@ public final class MarvelsSpiderManEternal extends ExpansionSet { cards.add(new SetCardInfo("Grasping Tentacles", 21, Rarity.RARE, mage.cards.g.GraspingTentacles.class)); cards.add(new SetCardInfo("Green Goblin, Nemesis", 23, Rarity.RARE, mage.cards.g.GreenGoblinNemesis.class)); cards.add(new SetCardInfo("Pumpkin Bombs", 26, Rarity.RARE, mage.cards.p.PumpkinBombs.class)); + cards.add(new SetCardInfo("Sensational Spider-Man", 25, Rarity.RARE, mage.cards.s.SensationalSpiderMan.class)); cards.add(new SetCardInfo("Venom, Deadly Devourer", 22, Rarity.RARE, mage.cards.v.VenomDeadlyDevourer.class)); } } diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java index b134c74e121..c8547e513a6 100644 --- a/Mage/src/main/java/mage/cards/Card.java +++ b/Mage/src/main/java/mage/cards/Card.java @@ -181,19 +181,21 @@ public interface Card extends MageObject, Ownerable { * Remove {@param amount} counters of the specified kind. * * @param isDamage if the counter removal is a result of being damaged (e.g. for Deification to work) + * @return amount of counters removed */ - void removeCounters(String counterName, int amount, Ability source, Game game, boolean isDamage); + int removeCounters(String counterName, int amount, Ability source, Game game, boolean isDamage); - default void removeCounters(Counter counter, Ability source, Game game) { - removeCounters(counter, source, game, false); + default int removeCounters(Counter counter, Ability source, Game game) { + return removeCounters(counter, source, game, false); } /** * Remove all counters of any kind. * * @param isDamage if the counter removal is a result of being damaged (e.g. for Deification to work) + * @return amount of counters removed */ - void removeCounters(Counter counter, Ability source, Game game, boolean isDamage); + int removeCounters(Counter counter, Ability source, Game game, boolean isDamage); /** * Remove all counters of any kind. diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index 2515d21c1fd..51bc39e6676 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -819,19 +819,19 @@ public abstract class CardImpl extends MageObjectImpl implements Card { } @Override - public void removeCounters(String counterName, int amount, Ability source, Game game, boolean isDamage) { + public int removeCounters(String counterName, int amount, Ability source, Game game, boolean isDamage) { if (amount <= 0) { - return; + return 0; } if (getCounters(game).getCount(counterName) <= 0) { - return; + return 0; } GameEvent removeCountersEvent = new RemoveCountersEvent(counterName, this, source, amount, isDamage); if (game.replaceEvent(removeCountersEvent)) { - return; + return 0; } int finalAmount = 0; @@ -854,13 +854,12 @@ public abstract class CardImpl extends MageObjectImpl implements Card { GameEvent event = new CountersRemovedEvent(counterName, this, source, finalAmount, isDamage); game.fireEvent(event); + return finalAmount; } @Override - public void removeCounters(Counter counter, Ability source, Game game, boolean isDamage) { - if (counter != null) { - removeCounters(counter.getName(), counter.getCount(), source, game, isDamage); - } + public int removeCounters(Counter counter, Ability source, Game game, boolean isDamage) { + return counter != null ? removeCounters(counter.getName(), counter.getCount(), source, game, isDamage) : 0; } @Override diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index 149d8a6e92b..a4939295c25 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -1093,13 +1093,13 @@ public class Spell extends StackObjectImpl implements Card { } @Override - public void removeCounters(String counterName, int amount, Ability source, Game game, boolean isDamage) { - card.removeCounters(counterName, amount, source, game, isDamage); + public int removeCounters(String counterName, int amount, Ability source, Game game, boolean isDamage) { + return card.removeCounters(counterName, amount, source, game, isDamage); } @Override - public void removeCounters(Counter counter, Ability source, Game game, boolean isDamage) { - card.removeCounters(counter, source, game, isDamage); + public int removeCounters(Counter counter, Ability source, Game game, boolean isDamage) { + return card.removeCounters(counter, source, game, isDamage); } @Override -- 2.47.2 From 30d44ce86908152c6749a102b54e31cd728903b2 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 18 Apr 2025 09:38:52 +0400 Subject: [PATCH 77/95] Improved server's reconnection and drafts stability: * draft: fixed miss or empty draft panels on reconnect; * draft: fixed tourney freezes for richman drafts on disconnects; * draft: fixed tourney freezes on rare use cases with bad connection; --- .../java/mage/client/draft/DraftPanel.java | 3 +- .../client/remote/CallbackClientImpl.java | 2 +- .../interfaces/callback/ClientCallback.java | 3 + .../main/java/mage/remote/SessionImpl.java | 49 ++++-- .../src/main/java/mage/server/Session.java | 10 +- .../src/main/java/mage/server/User.java | 10 ++ .../java/mage/server/draft/DraftSession.java | 5 + .../mage/server/util/ThreadExecutorImpl.java | 5 +- .../java/mage/game/draft/BoosterDraft.java | 1 + Mage/src/main/java/mage/game/draft/Draft.java | 1 + .../main/java/mage/game/draft/DraftImpl.java | 154 ++++++++++-------- .../java/mage/game/draft/DraftPlayer.java | 14 +- .../mage/game/draft/RandomBoosterDraft.java | 2 +- .../game/draft/ReshuffledBoosterDraft.java | 2 +- .../mage/game/draft/RichManBoosterDraft.java | 38 ++--- .../game/draft/RichManCubeBoosterDraft.java | 33 ++-- .../main/java/mage/game/match/MatchImpl.java | 2 +- 17 files changed, 201 insertions(+), 133 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java index 20eb1aee65e..1118801bf5f 100644 --- a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java +++ b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java @@ -401,7 +401,8 @@ } if (!draftBooster.isEmptyGrid()) { - SessionHandler.setBoosterLoaded(draftId); // confirm to the server that the booster has been successfully loaded, otherwise the server will re-send the booster + // confirm to the server that the booster has been successfully loaded, otherwise the server will re-send the booster + SessionHandler.setBoosterLoaded(draftId); if (pickNo != protectionPickNo && !protectionTimer.isRunning()) { // Restart the protection timer. diff --git a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java index ea3810ae27c..30e88a12e1e 100644 --- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -72,7 +72,7 @@ public class CallbackClientImpl implements CallbackClient { SwingUtilities.invokeLater(() -> { try { if (DebugUtil.NETWORK_SHOW_CLIENT_CALLBACK_MESSAGES_LOG) { - logger.info("message " + callback.getMessageId() + " - " + callback.getMethod().getType() + " - " + callback.getMethod()); + logger.info(callback.getInfo()); } // process bad connection (events can income in wrong order, so outdated data must be ignored) diff --git a/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallback.java b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallback.java index 1155facff78..e38b2fcecc5 100644 --- a/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallback.java +++ b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallback.java @@ -99,4 +99,7 @@ public class ClientCallback implements Serializable { return messageId; } + public String getInfo() { + return String.format("message %d - %s - %s", this.getMessageId(), this.getMethod().getType(), this.getMethod()); + } } diff --git a/Mage.Common/src/main/java/mage/remote/SessionImpl.java b/Mage.Common/src/main/java/mage/remote/SessionImpl.java index 2dd6b539ebe..aaf1959dd5a 100644 --- a/Mage.Common/src/main/java/mage/remote/SessionImpl.java +++ b/Mage.Common/src/main/java/mage/remote/SessionImpl.java @@ -13,13 +13,12 @@ import mage.interfaces.ServerState; import mage.interfaces.callback.ClientCallback; import mage.players.PlayerType; import mage.players.net.UserData; -import mage.utils.CompressUtil; import mage.util.ThreadUtils; +import mage.utils.CompressUtil; import mage.view.*; import org.apache.log4j.Logger; import org.jboss.remoting.*; import org.jboss.remoting.callback.Callback; -import org.jboss.remoting.callback.HandleCallbackException; import org.jboss.remoting.callback.InvokerCallbackHandler; import org.jboss.remoting.transport.bisocket.Bisocket; import org.jboss.remoting.transport.socket.SocketWrapper; @@ -31,6 +30,7 @@ import java.lang.reflect.UndeclaredThrowableException; import java.net.*; import java.util.*; import java.util.concurrent.CancellationException; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; /** @@ -47,6 +47,8 @@ public class SessionImpl implements Session { private static final int SESSION_VALIDATOR_PING_PERIOD_SECS = 4; private static final int SESSION_VALIDATOR_PING_TIMEOUT_SECS = 3; + private static final int CONNECT_WAIT_BEFORE_PROCESS_ANY_CALLBACKS_SECS = 3; + public static final String ADMIN_NAME = "Admin"; // if you change here then change in User too public static final String KEEP_MY_OLD_SESSION = "keep_my_old_session"; // for disconnects without active session lose (keep tables/games) @@ -598,24 +600,43 @@ public class SessionImpl implements Session { class CallbackHandler implements InvokerCallbackHandler { + final CopyOnWriteArrayList waitingCallbacks = new CopyOnWriteArrayList<>(); + @Override - public void handleCallback(Callback callback) throws HandleCallbackException { - try { - // on connection client will receive all waiting callbacks from a server, e.g. started table, draft pick, etc - // but it's require to get server settings first (server state), e.g. for test mode - // possible bugs: hidden cheat button or enabled clicks protection in draft - // so wait for server state some time + public void handleCallback(Callback callback) { + // keep callbacks + ClientCallback clientCallback = (ClientCallback) callback.getCallbackObject(); + waitingCallbacks.add(clientCallback); + + // wait for client ready + // on connection client will receive all waiting callbacks from a server, e.g. started table, draft pick, etc + // but it's require to get server settings first (server state), e.g. for test mode + // possible bugs: + // - hidden cheat button or enabled clicks protection in draft + // - miss dialogs like draft or game panels + // so wait for server state some time + if (serverState == null) { + ThreadUtils.sleep(CONNECT_WAIT_BEFORE_PROCESS_ANY_CALLBACKS_SECS * 1000); if (serverState == null) { - ThreadUtils.sleep(1000); - if (serverState == null) { - logger.error("Can't receive server state before other data (possible reason: unstable network)"); - } + logger.error("Can't receive server state before other data (possible reason: unstable network): " + + clientCallback.getInfo()); } - client.onCallback((ClientCallback) callback.getCallbackObject()); + } + + // execute waiting queue + // client.onCallback must process and ignore outdated data inside + List executingCallbacks; + synchronized (waitingCallbacks) { + executingCallbacks = new ArrayList<>(waitingCallbacks); + executingCallbacks.sort(Comparator.comparingInt(ClientCallback::getMessageId)); + waitingCallbacks.clear(); + } + + try { + executingCallbacks.forEach(client::onCallback); } catch (Exception ex) { logger.error("handleCallback error", ex); } - } } diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index a7dde925fdf..783c246d074 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -73,6 +73,7 @@ public class Session { private final ReentrantLock lock; private final ReentrantLock callBackLock; + private String lastCallbackInfo = ""; public Session(ManagerFactory managerFactory, String sessionId, InvokerCallbackHandler callbackHandler) { this.managerFactory = managerFactory; @@ -429,8 +430,10 @@ public class Session { */ public void fireCallback(final ClientCallback call) { boolean lockSet = false; // TODO: research about locks, why it here? 2023-12-06 + try { if (valid && callBackLock.tryLock(50, TimeUnit.MILLISECONDS)) { + lastCallbackInfo = call.getInfo(); call.setMessageId(messageId.incrementAndGet()); lockSet = true; Callback callback = new Callback(call); @@ -440,11 +443,14 @@ public class Session { } } catch (InterruptedException ex) { // already sending another command (connection problem?) + // TODO: un-support multiple games/drafts at the same time?!?!?!?! if (call.getMethod().equals(ClientCallbackMethod.GAME_INIT) || call.getMethod().equals(ClientCallbackMethod.START_GAME)) { - // it's ok use case, user has connection problem so can't send game init (see sendInfoAboutPlayersNotJoinedYetAndTryToFixIt) + // it's ok, possible use cases: + // - user has connection problem so can't send game init (see sendInfoAboutPlayersNotJoinedYetAndTryToFixIt) } else { - logger.warn("SESSION LOCK, possible connection problem - fireCallback - userId: " + userId + " messageId: " + call.getMessageId(), ex); + logger.warn("SESSION LOCK, possible connection problem - fireCallback - userId: " + + userId + ", prev call: " + lastCallbackInfo + ", current call: " + call.getInfo(), ex); } } catch (HandleCallbackException ex) { // general error diff --git a/Mage.Server/src/main/java/mage/server/User.java b/Mage.Server/src/main/java/mage/server/User.java index eb0a9d7faea..1898139760e 100644 --- a/Mage.Server/src/main/java/mage/server/User.java +++ b/Mage.Server/src/main/java/mage/server/User.java @@ -4,6 +4,7 @@ import mage.cards.decks.Deck; import mage.constants.ManaType; import mage.constants.TableState; import mage.game.Table; +import mage.game.draft.DraftPlayer; import mage.game.result.ResultProtos; import mage.game.tournament.TournamentPlayer; import mage.interfaces.callback.ClientCallback; @@ -441,6 +442,15 @@ public class User { ccDraftStarted(entry.getValue().getDraft().getTableId(), entry.getValue().getDraft().getId(), entry.getKey()); entry.getValue().init(); entry.getValue().update(); + + // on reconnect must resend booster data to new player + // TODO: there are possible rare race conditions and user can get draft panel without game info, miss tourney panel, etc + // TODO: client side - it must show active panel like current game or draft instead tourney panel + DraftPlayer draftPlayer = entry.getValue().getDraftPlayer(); + if (draftPlayer != null) { + draftPlayer.setBoosterNotLoaded(); + entry.getValue().getDraft().boosterSendingStart(); + } } // active constructing diff --git a/Mage.Server/src/main/java/mage/server/draft/DraftSession.java b/Mage.Server/src/main/java/mage/server/draft/DraftSession.java index bf659323eff..97dc4fe19cc 100644 --- a/Mage.Server/src/main/java/mage/server/draft/DraftSession.java +++ b/Mage.Server/src/main/java/mage/server/draft/DraftSession.java @@ -1,6 +1,7 @@ package mage.server.draft; import mage.game.draft.Draft; +import mage.game.draft.DraftPlayer; import mage.interfaces.callback.ClientCallback; import mage.interfaces.callback.ClientCallbackMethod; import mage.server.User; @@ -156,6 +157,10 @@ public class DraftSession { return new DraftPickView(draft.getPlayer(playerId), timeout); } + public DraftPlayer getDraftPlayer() { + return draft.getPlayer(playerId); + } + public Draft getDraft() { return draft; } diff --git a/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java b/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java index cd475219829..76d89cc41c7 100644 --- a/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java +++ b/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java @@ -38,20 +38,17 @@ public class ThreadExecutorImpl implements ThreadExecutor { */ public ThreadExecutorImpl(ConfigSettings config) { - //callExecutor = Executors.newCachedThreadPool(); callExecutor = new CachedThreadPoolWithException(); ((ThreadPoolExecutor) callExecutor).setKeepAliveTime(60, TimeUnit.SECONDS); ((ThreadPoolExecutor) callExecutor).allowCoreThreadTimeOut(true); ((ThreadPoolExecutor) callExecutor).setThreadFactory(new XmageThreadFactory(ThreadUtils.THREAD_PREFIX_CALL_REQUEST)); - //gameExecutor = Executors.newFixedThreadPool(config.getMaxGameThreads()); gameExecutor = new FixedThreadPoolWithException(config.getMaxGameThreads()); ((ThreadPoolExecutor) gameExecutor).setKeepAliveTime(60, TimeUnit.SECONDS); ((ThreadPoolExecutor) gameExecutor).allowCoreThreadTimeOut(true); ((ThreadPoolExecutor) gameExecutor).setThreadFactory(new XmageThreadFactory(ThreadUtils.THREAD_PREFIX_GAME)); - //tourney = Executors.newFixedThreadPool(config.getMaxGameThreads() / GAMES_PER_TOURNEY_RATIO); - tourneyExecutor = new FixedThreadPoolWithException(config.getMaxGameThreads() / GAMES_PER_TOURNEY_RATIO); + tourneyExecutor = new FixedThreadPoolWithException(Math.min(2, config.getMaxGameThreads() / GAMES_PER_TOURNEY_RATIO)); ((ThreadPoolExecutor) tourneyExecutor).setKeepAliveTime(60, TimeUnit.SECONDS); ((ThreadPoolExecutor) tourneyExecutor).allowCoreThreadTimeOut(true); ((ThreadPoolExecutor) tourneyExecutor).setThreadFactory(new XmageThreadFactory(ThreadUtils.THREAD_PREFIX_TOURNEY)); diff --git a/Mage/src/main/java/mage/game/draft/BoosterDraft.java b/Mage/src/main/java/mage/game/draft/BoosterDraft.java index a0063f8ae60..79587fc46d6 100644 --- a/Mage/src/main/java/mage/game/draft/BoosterDraft.java +++ b/Mage/src/main/java/mage/game/draft/BoosterDraft.java @@ -34,6 +34,7 @@ public class BoosterDraft extends DraftImpl { } boosterNum++; } + this.boosterSendingEnd(); this.fireEndDraftEvent(); } diff --git a/Mage/src/main/java/mage/game/draft/Draft.java b/Mage/src/main/java/mage/game/draft/Draft.java index 1fb235fea4e..1e5bb1de652 100644 --- a/Mage/src/main/java/mage/game/draft/Draft.java +++ b/Mage/src/main/java/mage/game/draft/Draft.java @@ -34,6 +34,7 @@ public interface Draft extends MageItem, Serializable { int getCardNum(); boolean addPick(UUID playerId, UUID cardId, Set hiddenCards); void setBoosterLoaded(UUID playerID); + void boosterSendingStart(); void start(); boolean isStarted(); void setStarted(); diff --git a/Mage/src/main/java/mage/game/draft/DraftImpl.java b/Mage/src/main/java/mage/game/draft/DraftImpl.java index c84917c2f3d..c0475e01802 100644 --- a/Mage/src/main/java/mage/game/draft/DraftImpl.java +++ b/Mage/src/main/java/mage/game/draft/DraftImpl.java @@ -18,7 +18,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; /** - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, JayDi85 */ public abstract class DraftImpl implements Draft { @@ -36,7 +36,7 @@ public abstract class DraftImpl implements Draft { protected int cardNum = 1; // starts with card number 1, increases by +1 after each picking protected TimingOption timing; protected int boosterLoadingCounter; // number of times the boosters have been sent to players until all are confirmed to have received them - protected final int BOOSTER_LOADING_INTERVAL = 2; // interval in seconds + protected final int BOOSTER_LOADING_INTERVAL_SECS = 2; // interval in seconds protected boolean abort = false; protected boolean started = false; @@ -44,8 +44,8 @@ public abstract class DraftImpl implements Draft { protected transient TableEventSource tableEventSource = new TableEventSource(); protected transient PlayerQueryEventSource playerQueryEventSource = new PlayerQueryEventSource(); - protected ScheduledFuture boosterLoadingHandle; - protected ScheduledExecutorService boosterLoadingExecutor = null; + protected ScheduledFuture boosterSendingWorker; + protected ScheduledExecutorService boosterSendingExecutor = null; public DraftImpl(DraftOptions options, List sets) { this.id = UUID.randomUUID(); @@ -83,7 +83,6 @@ public abstract class DraftImpl implements Draft { if (newPlayer != null) { DraftPlayer newDraftPlayer = new DraftPlayer(newPlayer); DraftPlayer oldDraftPlayer = players.get(oldPlayer.getId()); - newDraftPlayer.setBooster(oldDraftPlayer.getBooster()); Map newPlayers = new LinkedHashMap<>(); synchronized (players) { for (Map.Entry entry : players.entrySet()) { @@ -110,12 +109,14 @@ public abstract class DraftImpl implements Draft { table.setCurrent(currentId); } + + // boosters send to all players by timeout, so don't need to send it manually here + newDraftPlayer.setBoosterAndLoad(oldDraftPlayer.getBooster()); if (oldDraftPlayer.isPicking()) { - newDraftPlayer.setPicking(); - if (!newDraftPlayer.getBooster().isEmpty()) { - newDraftPlayer.getPlayer().pickCard(newDraftPlayer.getBooster(), newDraftPlayer.getDeck(), this); - } + newDraftPlayer.setPickingAndSending(); } + boosterSendingStart(); // if it's AI then make pick from it + return true; } return false; @@ -124,7 +125,7 @@ public abstract class DraftImpl implements Draft { @Override public Collection getPlayers() { synchronized (players) { - return players.values(); + return new ArrayList<>(players.values()); } } @@ -188,7 +189,7 @@ public abstract class DraftImpl implements Draft { List currentBooster = current.booster; while (true) { List nextBooster = next.booster; - next.setBooster(currentBooster); + next.setBoosterAndLoad(currentBooster); if (Objects.equals(nextId, startId)) { break; } @@ -209,7 +210,7 @@ public abstract class DraftImpl implements Draft { List currentBooster = current.booster; while (true) { List prevBooster = prev.booster; - prev.setBooster(currentBooster); + prev.setBoosterAndLoad(currentBooster); if (Objects.equals(prevId, startId)) { break; } @@ -221,70 +222,71 @@ public abstract class DraftImpl implements Draft { } protected void openBooster() { - if (boosterNum <= numberBoosters) { - for (DraftPlayer player : players.values()) { - if (draftCube != null) { - player.setBooster(draftCube.createBooster()); - } else { - player.setBooster(sets.get(boosterNum - 1).createBooster()); + synchronized (players) { + if (boosterNum <= numberBoosters) { + for (DraftPlayer player : players.values()) { + if (draftCube != null) { + player.setBoosterAndLoad(draftCube.createBooster()); + } else { + player.setBoosterAndLoad(sets.get(boosterNum - 1).createBooster()); + } } } } } protected boolean pickCards() { - for (DraftPlayer player : players.values()) { - if (player.getBooster().isEmpty()) { - return false; - } - player.setPicking(); - player.setBoosterNotLoaded(); - } - setupBoosterLoadingHandle(); - synchronized (this) { - while (!donePicking()) { - try { - this.wait(10000); // checked every 10s to make sure the draft moves on - } catch (InterruptedException ex) { + synchronized (players) { + for (DraftPlayer player : players.values()) { + if (player.getBooster().isEmpty()) { + return false; } + player.setPickingAndSending(); } } + + while (!donePicking()) { + boosterSendingStart(); + picksWait(); + } + cardNum++; return true; } - protected void setupBoosterLoadingHandle() { - cancelBoosterLoadingHandle(); - boosterLoadingCounter = 0; - - if (this.boosterLoadingExecutor == null) { - this.boosterLoadingExecutor = Executors.newSingleThreadScheduledExecutor( - new XmageThreadFactory(ThreadUtils.THREAD_PREFIX_TOURNEY_BOOSTERS_SEND) + public void boosterSendingStart() { + if (this.boosterSendingExecutor == null) { + this.boosterSendingExecutor = Executors.newSingleThreadScheduledExecutor( + new XmageThreadFactory(ThreadUtils.THREAD_PREFIX_TOURNEY_BOOSTERS_SEND + " " + this.getId()) ); } + boosterLoadingCounter = 0; - boosterLoadingHandle = boosterLoadingExecutor.scheduleAtFixedRate(() -> { - try { - if (loadBoosters()) { - cancelBoosterLoadingHandle(); - } else { - boosterLoadingCounter++; + if (boosterSendingWorker == null) { + boosterSendingWorker = boosterSendingExecutor.scheduleAtFixedRate(() -> { + try { + if (isAbort() || sendBoostersToPlayers()) { + boosterSendingEnd(); + } else { + boosterLoadingCounter++; + } + } catch (Exception ex) { + logger.fatal("Fatal boosterLoadingHandle error in draft " + id + " pack " + boosterNum + " pick " + cardNum, ex); } - } catch (Exception ex) { - logger.fatal("Fatal boosterLoadingHandle error in draft " + id + " pack " + boosterNum + " pick " + cardNum, ex); - } - }, 0, BOOSTER_LOADING_INTERVAL, TimeUnit.SECONDS); - } - - protected void cancelBoosterLoadingHandle() { - if (boosterLoadingHandle != null) { - boosterLoadingHandle.cancel(true); + }, 0, BOOSTER_LOADING_INTERVAL_SECS, TimeUnit.SECONDS); } } - protected boolean loadBoosters() { + protected void boosterSendingEnd() { + if (boosterSendingWorker != null) { + boosterSendingWorker.cancel(true); + boosterSendingWorker = null; + } + } + + protected boolean sendBoostersToPlayers() { boolean allBoostersLoaded = true; - for (DraftPlayer player : players.values()) { + for (DraftPlayer player : getPlayers()) { if (player.isPicking() && !player.isBoosterLoaded()) { allBoostersLoaded = false; player.getPlayer().pickCard(player.getBooster(), player.getDeck(), this); @@ -297,16 +299,20 @@ public abstract class DraftImpl implements Draft { if (isAbort()) { return true; } - return players.values() - .stream() - .noneMatch(DraftPlayer::isPicking); + synchronized (players) { + return players.values() + .stream() + .noneMatch(DraftPlayer::isPicking); + } } @Override public boolean allJoined() { - return players.values().stream() - .allMatch(DraftPlayer::isJoined); + synchronized (players) { + return players.values().stream() + .allMatch(DraftPlayer::isJoined); + } } @Override @@ -342,11 +348,32 @@ public abstract class DraftImpl implements Draft { // if the pack is re-sent to a player because they haven't been able to successfully load it, the pick time is reduced appropriately because of the elapsed time // the time is always at least 1 second unless it's set to 0, i.e. unlimited time if (time > 0) { - time = Math.max(1, time - boosterLoadingCounter * BOOSTER_LOADING_INTERVAL); + time = Math.max(1, time - boosterLoadingCounter * BOOSTER_LOADING_INTERVAL_SECS); } return time; } + public void picksCheckDone() { + // notify main thread about changes, can be called from user's thread + synchronized (this) { + this.notifyAll(); + } + } + + protected void picksWait() { + // main thread waiting any picks or changes + synchronized (this) { + try { + this.wait(10000); // checked every 10s to make sure the draft moves on + } catch (InterruptedException ignore) { + } + } + + if (donePicking()) { + boosterSendingEnd(); + } + } + @Override public boolean addPick(UUID playerId, UUID cardId, Set hiddenCards) { DraftPlayer player = players.get(playerId); @@ -354,13 +381,10 @@ public abstract class DraftImpl implements Draft { for (Card card : player.booster) { if (card.getId().equals(cardId)) { player.addPick(card, hiddenCards); - player.booster.remove(card); break; } } - synchronized (this) { - this.notifyAll(); - } + picksCheckDone(); } return !player.isPicking(); } diff --git a/Mage/src/main/java/mage/game/draft/DraftPlayer.java b/Mage/src/main/java/mage/game/draft/DraftPlayer.java index a333d143a3d..5f02a960f01 100644 --- a/Mage/src/main/java/mage/game/draft/DraftPlayer.java +++ b/Mage/src/main/java/mage/game/draft/DraftPlayer.java @@ -21,7 +21,7 @@ public class DraftPlayer { protected Deck deck; protected List booster; protected boolean picking; - protected boolean boosterLoaded; + protected boolean boosterLoaded; // client confirmed that it got a booster data (for computer must be always false) protected boolean joined = false; protected Set hiddenCards; @@ -64,14 +64,13 @@ public class DraftPlayer { if (hiddenCards != null) { this.hiddenCards = hiddenCards; } - synchronized (booster) { - booster.remove(card); - } + booster.remove(card); picking = false; } - public void setBooster(List booster) { + public void setBoosterAndLoad(List booster) { this.booster = booster; + this.boosterLoaded = false; // human will receive new pick, computer with choose new pick } public List getBooster() { @@ -83,8 +82,9 @@ public class DraftPlayer { } } - public void setPicking() { - picking = true; + public void setPickingAndSending() { + this.picking = true; + this.boosterLoaded = false; } public boolean isPicking() { diff --git a/Mage/src/main/java/mage/game/draft/RandomBoosterDraft.java b/Mage/src/main/java/mage/game/draft/RandomBoosterDraft.java index 629c13fd523..140dcabe39c 100644 --- a/Mage/src/main/java/mage/game/draft/RandomBoosterDraft.java +++ b/Mage/src/main/java/mage/game/draft/RandomBoosterDraft.java @@ -28,7 +28,7 @@ public class RandomBoosterDraft extends BoosterDraft { protected void openBooster() { if (boosterNum <= numberBoosters) { for (DraftPlayer player: players.values()) { - player.setBooster(getNextBooster().create15CardBooster()); + player.setBoosterAndLoad(getNextBooster().create15CardBooster()); } } } diff --git a/Mage/src/main/java/mage/game/draft/ReshuffledBoosterDraft.java b/Mage/src/main/java/mage/game/draft/ReshuffledBoosterDraft.java index 7fd747d449d..365b3cb4c60 100644 --- a/Mage/src/main/java/mage/game/draft/ReshuffledBoosterDraft.java +++ b/Mage/src/main/java/mage/game/draft/ReshuffledBoosterDraft.java @@ -20,7 +20,7 @@ public class ReshuffledBoosterDraft extends BoosterDraft { protected void openBooster() { if (boosterNum <= numberBoosters) { for (DraftPlayer player: players.values()) { - player.setBooster(reshuffledSet.createBooster()); + player.setBoosterAndLoad(reshuffledSet.createBooster()); } } } diff --git a/Mage/src/main/java/mage/game/draft/RichManBoosterDraft.java b/Mage/src/main/java/mage/game/draft/RichManBoosterDraft.java index 7ca3de815f0..a8c0b1cd851 100644 --- a/Mage/src/main/java/mage/game/draft/RichManBoosterDraft.java +++ b/Mage/src/main/java/mage/game/draft/RichManBoosterDraft.java @@ -1,15 +1,14 @@ - package mage.game.draft; -import java.util.List; -import java.util.Objects; -import java.util.UUID; import mage.cards.Card; import mage.cards.ExpansionSet; import org.apache.log4j.Logger; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + /** - * * @author spjspj */ public class RichManBoosterDraft extends DraftImpl { @@ -38,6 +37,7 @@ public class RichManBoosterDraft extends DraftImpl { } boosterNum++; } + this.boosterSendingEnd(); this.fireEndDraftEvent(); } @@ -50,7 +50,7 @@ public class RichManBoosterDraft extends DraftImpl { DraftPlayer next = players.get(nextId); while (true) { List nextBooster = sets.get((cardNum - 1) % sets.size()).createBooster(); - next.setBooster(nextBooster); + next.setBoosterAndLoad(nextBooster); if (Objects.equals(nextId, startId)) { break; } @@ -62,22 +62,21 @@ public class RichManBoosterDraft extends DraftImpl { @Override protected boolean pickCards() { - for (DraftPlayer player : players.values()) { - if (cardNum > 36) { - return false; - } - player.setPicking(); - player.getPlayer().pickCard(player.getBooster(), player.getDeck(), this); - } - cardNum++; - synchronized (this) { - while (!donePicking()) { - try { - this.wait(); - } catch (InterruptedException ex) { + synchronized (players) { + for (DraftPlayer player : players.values()) { + if (cardNum > 36) { + return false; } + player.setPickingAndSending(); } } + + while (!donePicking()) { + boosterSendingStart(); + picksWait(); + } + + cardNum++; return true; } @@ -85,6 +84,7 @@ public class RichManBoosterDraft extends DraftImpl { public void firePickCardEvent(UUID playerId) { DraftPlayer player = players.get(playerId); int cardNum = Math.min(36, this.cardNum); + // richman uses custom times int time = (int) Math.ceil(customProfiTimes[cardNum - 1] * timing.getCustomTimeoutFactor()); playerQueryEventSource.pickCard(playerId, "Pick card", player.getBooster(), time); diff --git a/Mage/src/main/java/mage/game/draft/RichManCubeBoosterDraft.java b/Mage/src/main/java/mage/game/draft/RichManCubeBoosterDraft.java index b930a0e80c8..c0d9ed9360e 100644 --- a/Mage/src/main/java/mage/game/draft/RichManCubeBoosterDraft.java +++ b/Mage/src/main/java/mage/game/draft/RichManCubeBoosterDraft.java @@ -1,13 +1,12 @@ package mage.game.draft; -import java.util.*; - import mage.cards.Card; import mage.cards.ExpansionSet; import mage.game.draft.DraftCube.CardIdentity; +import java.util.*; + /** - * * @author spjspj */ public class RichManCubeBoosterDraft extends DraftImpl { @@ -36,6 +35,7 @@ public class RichManCubeBoosterDraft extends DraftImpl { } boosterNum++; } + this.boosterSendingEnd(); this.fireEndDraftEvent(); } @@ -65,7 +65,7 @@ public class RichManCubeBoosterDraft extends DraftImpl { } List nextBooster = draftCube.createBooster(); - next.setBooster(nextBooster); + next.setBoosterAndLoad(nextBooster); if (Objects.equals(nextId, startId)) { break; } @@ -77,22 +77,21 @@ public class RichManCubeBoosterDraft extends DraftImpl { @Override protected boolean pickCards() { - for (DraftPlayer player : players.values()) { - if (cardNum > 36) { - return false; - } - player.setPicking(); - player.getPlayer().pickCard(player.getBooster(), player.getDeck(), this); - } - cardNum++; - synchronized (this) { - while (!donePicking()) { - try { - this.wait(); - } catch (InterruptedException ex) { + synchronized (players) { + for (DraftPlayer player : players.values()) { + if (cardNum > 36) { + return false; } + player.setPickingAndSending(); } } + + while (!donePicking()) { + boosterSendingStart(); + picksWait(); + } + + cardNum++; return true; } diff --git a/Mage/src/main/java/mage/game/match/MatchImpl.java b/Mage/src/main/java/mage/game/match/MatchImpl.java index 9d3b6463035..3fb5def5119 100644 --- a/Mage/src/main/java/mage/game/match/MatchImpl.java +++ b/Mage/src/main/java/mage/game/match/MatchImpl.java @@ -336,7 +336,7 @@ public abstract class MatchImpl implements Match { while (!isDoneSideboarding()) { try { this.wait(); - } catch (InterruptedException ex) { + } catch (InterruptedException ignore) { } } } -- 2.47.2 From c89f1c0d392561bca9fbb70e35fa3ed7023291ac Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 18 Apr 2025 10:00:25 +0400 Subject: [PATCH 78/95] merge fix --- .../src/main/java/mage/server/util/ThreadExecutorImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java b/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java index 76d89cc41c7..322e76ac6e9 100644 --- a/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java +++ b/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java @@ -48,7 +48,7 @@ public class ThreadExecutorImpl implements ThreadExecutor { ((ThreadPoolExecutor) gameExecutor).allowCoreThreadTimeOut(true); ((ThreadPoolExecutor) gameExecutor).setThreadFactory(new XmageThreadFactory(ThreadUtils.THREAD_PREFIX_GAME)); - tourneyExecutor = new FixedThreadPoolWithException(Math.min(2, config.getMaxGameThreads() / GAMES_PER_TOURNEY_RATIO)); + tourneyExecutor = new FixedThreadPoolWithException(Math.max(2, config.getMaxGameThreads() / GAMES_PER_TOURNEY_RATIO)); ((ThreadPoolExecutor) tourneyExecutor).setKeepAliveTime(60, TimeUnit.SECONDS); ((ThreadPoolExecutor) tourneyExecutor).allowCoreThreadTimeOut(true); ((ThreadPoolExecutor) tourneyExecutor).setThreadFactory(new XmageThreadFactory(ThreadUtils.THREAD_PREFIX_TOURNEY)); -- 2.47.2 From b87c22da6aee840bc18fe9d6ee5b742f99b07857 Mon Sep 17 00:00:00 2001 From: Callumvl <110387172+Callumvl@users.noreply.github.com> Date: Fri, 18 Apr 2025 16:23:17 +1000 Subject: [PATCH 79/95] =?UTF-8?q?Phyrexian=20Dreadnought=20-=20fixed=20tha?= =?UTF-8?q?t=20it=20can=E2=80=99t=20use=20itself=20for=20sacrifice=20cost?= =?UTF-8?q?=20(#13552)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java b/Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java index da44ececab5..e82404b0646 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java @@ -16,7 +16,6 @@ import mage.constants.SubType; import mage.constants.Outcome; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -56,10 +55,6 @@ class PhyrexianDreadnoughtSacrificeCost extends CostImpl { private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("any number of creatures with total power 12 or greater"); - static { - filter.add(AnotherPredicate.instance); - } - public PhyrexianDreadnoughtSacrificeCost() { this.addTarget(new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE, filter, true)); this.text = "sacrifice any number of creatures with total power 12 or greater"; @@ -89,9 +84,7 @@ class PhyrexianDreadnoughtSacrificeCost extends CostImpl { public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { int sumPower = 0; for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, controllerId, game)) { - if (!permanent.getId().equals(source.getSourceId())) { - sumPower += permanent.getPower().getValue(); - } + sumPower += permanent.getPower().getValue(); } return sumPower >= 12; } -- 2.47.2 From b915c6590b34d771645011bcd6d85b4c488409c1 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 19 Apr 2025 04:10:55 +0400 Subject: [PATCH 80/95] AI: fixed game freeze with yes/no combat triggers (related to #13290); --- .../Mage.Player.Human/src/mage/player/human/HumanPlayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 9f64a00117c..34e9baec448 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -426,7 +426,7 @@ public class HumanPlayer extends PlayerImpl { @Override public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) { - if (game.inCheckPlayableState()) { + if (!canCallFeedback(game)) { return true; } -- 2.47.2 From 3dc606501de83bcb6d50918246ea7aa0f7da2df6 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 19 Apr 2025 07:04:55 +0400 Subject: [PATCH 81/95] AI: improved stability and bug fixes (related to #13290): - bug's reason: wrong usage of canTarget, add/addTarget, getOpponents, etc; - fixed that it can target dead players in some use cases (close #13507); - fixed that it wrongly choose targets in bad/good effects in some use cases; - fixed that it can't find valid targets in some use cases; - fixed game freezes and errors with some cards; --- .../src/main/java/mage/utils/SystemUtil.java | 2 +- .../src/mage/player/ai/ComputerPlayer6.java | 4 +- .../mage/player/ai/GameStateEvaluator2.java | 3 +- .../src/mage/player/ai/SimulatedPlayer2.java | 8 +- .../java/mage/player/ai/ComputerPlayer.java | 392 +++++++++--------- .../player/ai/simulators/ActionSimulator.java | 2 +- .../player/ai/SelectAttackersNextAction.java | 2 +- .../mage/player/ai/SimulatedPlayerMCTS.java | 2 +- ...fAbleTargetRandomOpponentSourceEffect.java | 2 +- Mage/src/main/java/mage/target/Target.java | 6 + 10 files changed, 219 insertions(+), 204 deletions(-) diff --git a/Mage.Common/src/main/java/mage/utils/SystemUtil.java b/Mage.Common/src/main/java/mage/utils/SystemUtil.java index aff66539187..7d314cdf4ec 100644 --- a/Mage.Common/src/main/java/mage/utils/SystemUtil.java +++ b/Mage.Common/src/main/java/mage/utils/SystemUtil.java @@ -391,7 +391,7 @@ public final class SystemUtil { // 3. system commands if (runGroup.isSpecialCommand) { - Player opponent = game.getPlayer(game.getOpponents(feedbackPlayer.getId()).stream().findFirst().orElse(null)); + Player opponent = game.getPlayer(game.getOpponents(feedbackPlayer.getId(), true).stream().findFirst().orElse(null)); String info; switch (runGroup.name) { diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java index c0447fa59ca..6a2a2e1cd35 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java @@ -976,7 +976,7 @@ public class ComputerPlayer6 extends ComputerPlayer { Player attackingPlayer = game.getPlayer(activePlayerId); // check alpha strike first (all in attack to kill a player) - for (UUID defenderId : game.getOpponents(playerId)) { + for (UUID defenderId : game.getOpponents(playerId, true)) { Player defender = game.getPlayer(defenderId); if (!defender.isInGame()) { continue; @@ -999,7 +999,7 @@ public class ComputerPlayer6 extends ComputerPlayer { // TODO: add game simulations here to find best attackers/blockers combination // find safe attackers (can't be killed by blockers) - for (UUID defenderId : game.getOpponents(playerId)) { + for (UUID defenderId : game.getOpponents(playerId, true)) { Player defender = game.getPlayer(defenderId); if (!defender.isInGame()) { continue; diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/GameStateEvaluator2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/GameStateEvaluator2.java index 3b6fa38d229..f1d7b64205a 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/GameStateEvaluator2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/GameStateEvaluator2.java @@ -32,7 +32,8 @@ public final class GameStateEvaluator2 { public static PlayerEvaluateScore evaluate(UUID playerId, Game game, boolean useCombatPermanentScore) { // TODO: add multi opponents support, so AI can take better actions Player player = game.getPlayer(playerId); - Player opponent = game.getPlayer(game.getOpponents(playerId).stream().findFirst().orElse(null)); + // must find all leaved opponents too + Player opponent = game.getPlayer(game.getOpponents(playerId, false).stream().findFirst().orElse(null)); if (opponent == null) { return new PlayerEvaluateScore(playerId, WIN_GAME_SCORE); } diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java index 2ad894e1a8c..65ff643a73d 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java @@ -200,7 +200,7 @@ public final class SimulatedPlayer2 extends ComputerPlayer { Ability ability1 = iterator.next(); if (ability1.getTargets().size() == 1 && ability1.getTargets().get(0).getTargets().size() == 1) { Permanent permanent = game.getPermanent(ability1.getFirstTarget()); - if (permanent != null && !game.getOpponents(playerId).contains(permanent.getControllerId())) { + if (permanent != null && !game.getOpponents(playerId, true).contains(permanent.getControllerId())) { iterator.remove(); continue; } @@ -216,11 +216,11 @@ public final class SimulatedPlayer2 extends ComputerPlayer { Ability ability1 = iterator.next(); if (ability1.getTargets().size() == 1 && ability1.getTargets().get(0).getTargets().size() == 1) { Permanent permanent = game.getPermanent(ability1.getFirstTarget()); - if (permanent != null && game.getOpponents(playerId).contains(permanent.getControllerId())) { + if (permanent != null && game.getOpponents(playerId, true).contains(permanent.getControllerId())) { iterator.remove(); continue; } - if (game.getOpponents(playerId).contains(ability1.getFirstTarget())) { + if (game.getOpponents(playerId, true).contains(ability1.getFirstTarget())) { iterator.remove(); } } @@ -233,7 +233,7 @@ public final class SimulatedPlayer2 extends ComputerPlayer { public List addAttackers(Game game) { Map engagements = new HashMap<>(); //useful only for two player games - will only attack first opponent - UUID defenderId = game.getOpponents(playerId).iterator().next(); + UUID defenderId = game.getOpponents(playerId, true).iterator().next(); List attackersList = super.getAvailableAttackers(defenderId, game); //use binary digits to calculate powerset of attackers int powerElements = (int) Math.pow(2, attackersList.size()); diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 71ec32b1c21..517e175d149 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -59,6 +59,8 @@ import java.util.Map.Entry; /** * AI: basic server side bot with simple actions support (game, draft, construction/sideboarding) + *

+ * TODO: combine choose and chooseTarget to single logic to use shared code * * @author BetaSteward_at_googlemail.com, JayDi85 */ @@ -166,17 +168,10 @@ public class ComputerPlayer extends PlayerImpl { required = false; } - UUID randomOpponentId; - if (target.getTargetController() != null) { - randomOpponentId = getRandomOpponent(target.getTargetController(), game); - } else if (abilityControllerId != null) { - randomOpponentId = getRandomOpponent(abilityControllerId, game); - } else { - randomOpponentId = getRandomOpponent(playerId, game); - } + UUID randomOpponentId = getRandomOpponent(game); if (target.getOriginalTarget() instanceof TargetPlayer) { - return setTargetPlayer(outcome, target, null, abilityControllerId, randomOpponentId, game, required); + return selectPlayer(outcome, target, abilityControllerId, randomOpponentId, game, required); } if (target.getOriginalTarget() instanceof TargetDiscard) { @@ -184,7 +179,7 @@ public class ComputerPlayer extends PlayerImpl { // discard not playable first if (!unplayable.isEmpty()) { for (int i = unplayable.size() - 1; i >= 0; i--) { - if (target.canTarget(abilityControllerId, unplayable.values().toArray(new Card[0])[i].getId(), null, game)) { + if (target.canTarget(abilityControllerId, unplayable.values().toArray(new Card[0])[i].getId(), source, game)) { target.add(unplayable.values().toArray(new Card[0])[i].getId(), game); if (target.isChosen(game)) { return true; @@ -194,7 +189,7 @@ public class ComputerPlayer extends PlayerImpl { } if (!hand.isEmpty()) { for (int i = 0; i < hand.size(); i++) { - if (target.canTarget(abilityControllerId, hand.toArray(new UUID[0])[i], null, game)) { + if (target.canTarget(abilityControllerId, hand.toArray(new UUID[0])[i], source, game)) { target.add(hand.toArray(new UUID[0])[i], game); if (target.isChosen(game)) { return true; @@ -254,7 +249,7 @@ public class ComputerPlayer extends PlayerImpl { } for (Permanent permanent : targets) { - if (target.canTarget(abilityControllerId, permanent.getId(), null, game) && !target.getTargets().contains(permanent.getId())) { + if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) { // stop to add targets if not needed and outcome is no advantage for AI player if (target.getNumberOfTargets() == target.getTargets().size()) { if (outcome.isGood() && hasOpponent(permanent.getControllerId(), game)) { @@ -285,10 +280,10 @@ public class ComputerPlayer extends PlayerImpl { } while ((outcome.isGood() ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen(game)) && !cards.isEmpty()) { - Card pick = pickTarget(abilityControllerId, cards, outcome, target, null, game); - if (pick != null) { - target.addTarget(pick.getId(), null, game); - cards.remove(pick); + Card card = selectCard(abilityControllerId, cards, outcome, target, game); + if (card != null) { + target.add(card.getId(), game); + cards.remove(card); // selectCard don't remove cards (only on second+ tries) } } return target.isChosen(game); @@ -304,7 +299,7 @@ public class ComputerPlayer extends PlayerImpl { } for (Permanent permanent : targets) { List alreadyTargetted = target.getTargets(); - if (target.canTarget(abilityControllerId, permanent.getId(), null, game)) { + if (target.canTarget(abilityControllerId, permanent.getId(), source, game)) { if (alreadyTargetted != null && !alreadyTargetted.contains(permanent.getId())) { target.add(permanent.getId(), game); return true; @@ -312,11 +307,11 @@ public class ComputerPlayer extends PlayerImpl { } } if (outcome.isGood()) { - if (target.canTarget(abilityControllerId, abilityControllerId, null, game)) { - target.add(abilityControllerId, game); + if (target.canTarget(abilityControllerId, getId(), source, game)) { + target.add(getId(), game); return true; } - } else if (target.canTarget(abilityControllerId, randomOpponentId, null, game)) { + } else if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { target.add(randomOpponentId, game); return true; } @@ -337,7 +332,7 @@ public class ComputerPlayer extends PlayerImpl { } for (Permanent permanent : targets) { List alreadyTargeted = target.getTargets(); - if (target.canTarget(abilityControllerId, permanent.getId(), null, game)) { + if (target.canTarget(abilityControllerId, permanent.getId(), source, game)) { if (alreadyTargeted != null && !alreadyTargeted.contains(permanent.getId())) { target.add(permanent.getId(), game); return true; @@ -345,23 +340,23 @@ public class ComputerPlayer extends PlayerImpl { } } if (outcome.isGood()) { - if (target.canTarget(abilityControllerId, abilityControllerId, null, game)) { - target.add(abilityControllerId, game); + if (target.canTarget(abilityControllerId, getId(), source, game)) { + target.add(getId(), game); return true; } - } else if (target.canTarget(abilityControllerId, randomOpponentId, null, game)) { + } else if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { target.add(randomOpponentId, game); return true; } if (!target.isRequired(sourceId, game) || target.getNumberOfTargets() == 0) { return false; } - if (target.canTarget(abilityControllerId, randomOpponentId, null, game)) { + if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { target.add(randomOpponentId, game); return true; } - if (target.canTarget(abilityControllerId, abilityControllerId, null, game)) { - target.add(abilityControllerId, game); + if (target.canTarget(abilityControllerId, getId(), source, game)) { + target.add(getId(), game); return true; } if (outcome.isGood()) { // no other valid targets so use a permanent @@ -371,7 +366,7 @@ public class ComputerPlayer extends PlayerImpl { } for (Permanent permanent : targets) { List alreadyTargeted = target.getTargets(); - if (target.canTarget(abilityControllerId, permanent.getId(), null, game)) { + if (target.canTarget(abilityControllerId, permanent.getId(), source, game)) { if (alreadyTargeted != null && !alreadyTargeted.contains(permanent.getId())) { target.add(permanent.getId(), game); return true; @@ -385,7 +380,7 @@ public class ComputerPlayer extends PlayerImpl { List cards = new ArrayList<>(); for (Player player : game.getPlayers().values()) { for (Card card : player.getGraveyard().getCards(game)) { - if (target.canTarget(card.getId(), source, game)) { + if (target.canTarget(abilityControllerId, card.getId(), source, game)) { cards.add(card); } } @@ -395,10 +390,10 @@ public class ComputerPlayer extends PlayerImpl { boolean isRealGood = outcome.isGood() || outcome == Outcome.Exile; while ((isRealGood ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen(game)) && !cards.isEmpty()) { - Card pick = pickTarget(abilityControllerId, cards, outcome, target, null, game); - if (pick != null) { - target.addTarget(pick.getId(), null, game); - cards.remove(pick); + Card card = selectCard(abilityControllerId, cards, outcome, target, game); + if (card != null) { + target.add(card.getId(), game); + cards.remove(card); // selectCard don't remove cards (only on second+ tries) } else { break; } @@ -412,7 +407,7 @@ public class ComputerPlayer extends PlayerImpl { List cards = new ArrayList<>(); for (Player player : game.getPlayers().values()) { for (Card card : player.getGraveyard().getCards(game)) { - if (target.canTarget(abilityControllerId, card.getId(), null, game)) { + if (target.canTarget(abilityControllerId, card.getId(), source, game)) { cards.add(card); } } @@ -422,10 +417,10 @@ public class ComputerPlayer extends PlayerImpl { boolean isRealGood = outcome.isGood() || outcome == Outcome.Exile; while ((isRealGood ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen(game)) && !cards.isEmpty()) { - Card pick = pickTarget(abilityControllerId, cards, outcome, target, null, game); - if (pick != null) { - target.addTarget(pick.getId(), null, game); - cards.remove(pick); + Card card = selectCard(abilityControllerId, cards, outcome, target, game); + if (card != null) { + target.add(card.getId(), game); + cards.remove(card); // selectCard don't remove cards (only on second+ tries) } else { break; } @@ -440,7 +435,7 @@ public class ComputerPlayer extends PlayerImpl { TargetCard originalTarget = (TargetCard) target.getOriginalTarget(); List cards = new ArrayList<>(game.getPlayer(abilityControllerId).getGraveyard().getCards(originalTarget.getFilter(), game)); while (!cards.isEmpty()) { - Card card = pickTarget(abilityControllerId, cards, outcome, target, null, game); + Card card = selectCard(abilityControllerId, cards, outcome, target, game); if (card != null && alreadyTargeted != null && !alreadyTargeted.contains(card.getId())) { target.add(card.getId(), game); if (target.isChosen(game)) { @@ -465,7 +460,7 @@ public class ComputerPlayer extends PlayerImpl { List cards = game.getExile().getCards(filter, game); while (!cards.isEmpty()) { - Card card = pickTarget(abilityControllerId, cards, outcome, target, null, game); + Card card = selectCard(abilityControllerId, cards, outcome, target, game); if (card != null && alreadyTargeted != null && !alreadyTargeted.contains(card.getId())) { target.add(card.getId(), game); if (target.isChosen(game)) { @@ -483,7 +478,7 @@ public class ComputerPlayer extends PlayerImpl { MageObject targetObject = game.getObject(targetId); if (targetObject != null) { List alreadyTargeted = target.getTargets(); - if (target.canTarget(abilityControllerId, targetObject.getId(), null, game)) { + if (target.canTarget(abilityControllerId, targetObject.getId(), source, game)) { if (alreadyTargeted != null && !alreadyTargeted.contains(targetObject.getId())) { target.add(targetObject.getId(), game); return true; @@ -501,10 +496,10 @@ public class ComputerPlayer extends PlayerImpl { Cards cards = new CardsImpl(possibleTargets); List possibleCards = new ArrayList<>(cards.getCards(game)); while (!target.isChosen(game) && !possibleCards.isEmpty()) { - Card pick = pickTarget(abilityControllerId, possibleCards, outcome, target, null, game); - if (pick != null) { - target.addTarget(pick.getId(), null, game); - possibleCards.remove(pick); + Card card = selectCard(abilityControllerId, possibleCards, outcome, target, game); + if (card != null) { + target.add(card.getId(), game); + possibleCards.remove(card); // selectCard don't remove cards (only on second+ tries) } } return target.isChosen(game); @@ -515,15 +510,15 @@ public class ComputerPlayer extends PlayerImpl { List cardsInCommandZone = new ArrayList<>(); for (Player player : game.getPlayers().values()) { for (Card card : game.getCommanderCardsFromCommandZone(player, CommanderCardType.COMMANDER_OR_OATHBREAKER)) { - if (target.canTarget(abilityControllerId, card.getId(), null, game)) { + if (target.canTarget(abilityControllerId, card.getId(), source, game)) { cardsInCommandZone.add(card); } } } while (!target.isChosen(game) && !cardsInCommandZone.isEmpty()) { - Card pick = pickTarget(abilityControllerId, cardsInCommandZone, outcome, target, null, game); + Card pick = selectCard(abilityControllerId, cardsInCommandZone, outcome, target, game); if (pick != null) { - target.addTarget(pick.getId(), null, game); + target.add(pick.getId(), game); cardsInCommandZone.remove(pick); } } @@ -561,18 +556,10 @@ public class ComputerPlayer extends PlayerImpl { List badList = new ArrayList<>(); List allList = new ArrayList<>(); - // TODO: improve to process multiple opponents instead random - UUID randomOpponentId; - if (target.getTargetController() != null) { - randomOpponentId = getRandomOpponent(target.getTargetController(), game); - } else if (source != null && source.getControllerId() != null) { - randomOpponentId = getRandomOpponent(source.getControllerId(), game); - } else { - randomOpponentId = getRandomOpponent(playerId, game); - } + UUID randomOpponentId = getRandomOpponent(game); if (target.getOriginalTarget() instanceof TargetPlayer) { - return setTargetPlayer(outcome, target, source, abilityControllerId, randomOpponentId, game, required); + return selectPlayerTarget(outcome, target, source, abilityControllerId, randomOpponentId, game, required); } // Angel of Serenity trigger @@ -636,7 +623,7 @@ public class ComputerPlayer extends PlayerImpl { while (!target.isChosen(game) && !cardsInHand.isEmpty() && target.getMaxNumberOfTargets() > target.getTargets().size()) { - Card card = pickBestCard(cardsInHand, Collections.emptyList(), target, source, game); + Card card = selectBestCardTarget(cardsInHand, Collections.emptyList(), target, source, game); if (card != null) { if (target.canTarget(abilityControllerId, card.getId(), source, game)) { target.addTarget(card.getId(), source, game); @@ -746,8 +733,8 @@ public class ComputerPlayer extends PlayerImpl { if (targets.isEmpty()) { if (outcome.isGood()) { - if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { - return tryAddTarget(target, abilityControllerId, source, game); + if (target.canTarget(abilityControllerId, getId(), source, game)) { + return tryAddTarget(target, getId(), source, game); } } else if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { return tryAddTarget(target, randomOpponentId, source, game); @@ -767,8 +754,8 @@ public class ComputerPlayer extends PlayerImpl { } if (outcome.isGood()) { - if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { - return tryAddTarget(target, abilityControllerId, source, game); + if (target.canTarget(abilityControllerId, getId(), source, game)) { + return tryAddTarget(target, getId(), source, game); } } else if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { return tryAddTarget(target, randomOpponentId, source, game); @@ -789,8 +776,8 @@ public class ComputerPlayer extends PlayerImpl { if (targets.isEmpty()) { if (outcome.isGood()) { - if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { - return tryAddTarget(target, abilityControllerId, source, game); + if (target.canTarget(abilityControllerId, getId(), source, game)) { + return tryAddTarget(target, getId(), source, game); } } else if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { return tryAddTarget(target, randomOpponentId, source, game); @@ -828,8 +815,8 @@ public class ComputerPlayer extends PlayerImpl { // possible good/bad players if (targets.isEmpty()) { if (outcome.isGood()) { - if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { - return tryAddTarget(target, abilityControllerId, source, game); + if (target.canTarget(abilityControllerId, getId(), source, game)) { + return tryAddTarget(target, getId(), source, game); } } else if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { return tryAddTarget(target, randomOpponentId, source, game); @@ -853,21 +840,21 @@ public class ComputerPlayer extends PlayerImpl { // try target player as normal if (outcome.isGood()) { - if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { - return tryAddTarget(target, abilityControllerId, source, game); + if (target.canTarget(abilityControllerId, getId(), source, game)) { + return tryAddTarget(target, getId(), source, game); } } else if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { return tryAddTarget(target, randomOpponentId, source, game); } // try target player as bad (bad on itself, good on opponent) - for (UUID opponentId : game.getOpponents(abilityControllerId)) { + for (UUID opponentId : game.getOpponents(getId(), true)) { if (target.canTarget(abilityControllerId, opponentId, source, game)) { return tryAddTarget(target, opponentId, source, game); } } - if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { - return tryAddTarget(target, abilityControllerId, source, game); + if (target.canTarget(abilityControllerId, getId(), source, game)) { + return tryAddTarget(target, getId(), source, game); } return false; @@ -878,7 +865,7 @@ public class ComputerPlayer extends PlayerImpl { for (Player player : game.getPlayers().values()) { cards.addAll(player.getGraveyard().getCards(game)); } - Card card = pickTarget(abilityControllerId, cards, outcome, target, source, game); + Card card = selectCardTarget(abilityControllerId, cards, outcome, target, source, game); if (card != null) { return tryAddTarget(target, card.getId(), source, game); } @@ -888,7 +875,7 @@ public class ComputerPlayer extends PlayerImpl { if (target.getOriginalTarget() instanceof TargetCardInLibrary) { List cards = new ArrayList<>(game.getPlayer(abilityControllerId).getLibrary().getCards(game)); - Card card = pickTarget(abilityControllerId, cards, outcome, target, source, game); + Card card = selectCardTarget(abilityControllerId, cards, outcome, target, source, game); if (card != null) { return tryAddTarget(target, card.getId(), source, game); } @@ -898,10 +885,10 @@ public class ComputerPlayer extends PlayerImpl { if (target.getOriginalTarget() instanceof TargetCardInYourGraveyard) { List cards = new ArrayList<>(game.getPlayer(abilityControllerId).getGraveyard().getCards((FilterCard) target.getFilter(), game)); while (!target.isChosen(game) && !cards.isEmpty()) { - Card card = pickTarget(abilityControllerId, cards, outcome, target, source, game); + Card card = selectCardTarget(abilityControllerId, cards, outcome, target, source, game); if (card != null) { target.addTarget(card.getId(), source, game); - cards.remove(card); // pickTarget don't remove cards (only on second+ tries) + cards.remove(card); // selectCard don't remove cards (only on second+ tries) } } return target.isChosen(game); @@ -958,13 +945,13 @@ public class ComputerPlayer extends PlayerImpl { if (target.getOriginalTarget() instanceof TargetCardInOpponentsGraveyard) { List cards = new ArrayList<>(); - for (UUID uuid : game.getOpponents(abilityControllerId)) { + for (UUID uuid : game.getOpponents(getId(), true)) { Player player = game.getPlayer(uuid); if (player != null) { cards.addAll(player.getGraveyard().getCards(game)); } } - Card card = pickTarget(abilityControllerId, cards, outcome, target, source, game); + Card card = selectCardTarget(abilityControllerId, cards, outcome, target, source, game); if (card != null) { return tryAddTarget(target, card.getId(), source, game); } @@ -984,10 +971,10 @@ public class ComputerPlayer extends PlayerImpl { cards.addAll(player.getGraveyard().getCards(game)); } while (!target.isChosen(game) && !cards.isEmpty()) { - Card pick = pickTarget(abilityControllerId, cards, outcome, target, source, game); - if (pick != null) { - target.addTarget(pick.getId(), source, game); - cards.remove(pick); // pickTarget don't remove cards (only on second+ tries) + Card card = selectCardTarget(abilityControllerId, cards, outcome, target, source, game); + if (card != null) { + target.addTarget(card.getId(), source, game); + cards.remove(card); // selectCard don't remove cards (only on second+ tries) } } return target.isChosen(game); @@ -1012,10 +999,10 @@ public class ComputerPlayer extends PlayerImpl { } } while (!target.isChosen(game) && !cards.isEmpty()) { - Card pick = pickTarget(abilityControllerId, cards, outcome, target, source, game); - if (pick != null) { - target.addTarget(pick.getId(), source, game); - cards.remove(pick); // pickTarget don't remove cards (only on second+ tries) + Card card = selectCardTarget(abilityControllerId, cards, outcome, target, source, game); + if (card != null) { + target.addTarget(card.getId(), source, game); + cards.remove(card); // selectCard don't remove cards (only on second+ tries) } } return target.isChosen(game); @@ -1053,7 +1040,7 @@ public class ComputerPlayer extends PlayerImpl { cards.addAll(player.getGraveyard().getCards(game)); cards.addAll(game.getBattlefield().getAllActivePermanents(new FilterPermanent(), player.getId(), game)); } - Card card = pickTarget(abilityControllerId, cards, outcome, target, source, game); + Card card = selectCardTarget(abilityControllerId, cards, outcome, target, source, game); if (card != null) { return tryAddTarget(target, card.getId(), source, game); } @@ -1063,10 +1050,10 @@ public class ComputerPlayer extends PlayerImpl { Cards cards = new CardsImpl(possibleTargets); List possibleCards = new ArrayList<>(cards.getCards(game)); while (!target.isChosen(game) && !possibleCards.isEmpty()) { - Card pick = pickTarget(abilityControllerId, possibleCards, outcome, target, source, game); - if (pick != null) { - target.addTarget(pick.getId(), source, game); - possibleCards.remove(pick); + Card card = selectCardTarget(abilityControllerId, possibleCards, outcome, target, source, game); + if (card != null) { + target.addTarget(card.getId(), source, game); + possibleCards.remove(card); // selectCard don't remove cards (only on second+ tries) } } return target.isChosen(game); @@ -1075,18 +1062,28 @@ public class ComputerPlayer extends PlayerImpl { throw new IllegalStateException("Target wasn't handled in computer's chooseTarget method: " + target.getClass().getCanonicalName()); } //end of chooseTarget method - protected Card pickTarget(UUID abilityControllerId, List cards, Outcome outcome, Target target, Ability source, Game game) { + protected Card selectCard(UUID abilityControllerId, List cards, Outcome outcome, Target target, Game game) { + return selectCardInner(abilityControllerId, cards, outcome, target, null, game); + } + + protected Card selectCardTarget(UUID abilityControllerId, List cards, Outcome outcome, Target target, Ability targetingSource, Game game) { + return selectCardInner(abilityControllerId, cards, outcome, target, targetingSource, game); + } + + /** + * @param targetingSource null on non-target choice like choose and source on targeting choice like chooseTarget + */ + protected Card selectCardInner(UUID abilityControllerId, List cards, Outcome outcome, Target target, Ability targetingSource, Game game) { Card card; while (!cards.isEmpty()) { - if (outcome.isGood()) { - card = pickBestCard(cards, Collections.emptyList(), target, source, game); + card = selectBestCardInner(cards, Collections.emptyList(), target, targetingSource, game); } else { - card = pickWorstCard(cards, Collections.emptyList(), target, source, game); + card = selectWorstCardInner(cards, Collections.emptyList(), target, targetingSource, game); } if (!target.getTargets().contains(card.getId())) { - if (source != null) { - if (target.canTarget(abilityControllerId, card.getId(), source, game)) { + if (targetingSource != null) { + if (target.canTarget(abilityControllerId, card.getId(), targetingSource, game)) { return card; } } else { @@ -1107,15 +1104,15 @@ public class ComputerPlayer extends PlayerImpl { UUID sourceId = source != null ? source.getSourceId() : null; - // process multiple opponents by random - List opponents; - if (target.getTargetController() != null) { - opponents = new ArrayList<>(game.getOpponents(target.getTargetController())); - } else if (source != null && source.getControllerId() != null) { - opponents = new ArrayList<>(game.getOpponents(source.getControllerId())); - } else { - opponents = new ArrayList<>(game.getOpponents(getId())); + // sometimes a target selection can be made from a player that does not control the ability + UUID abilityControllerId = playerId; + if (target.getTargetController() != null + && target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); } + + // process multiple opponents by random + List opponents = new ArrayList<>(game.getOpponents(getId(), true)); Collections.shuffle(opponents); List targets; @@ -1126,7 +1123,7 @@ public class ComputerPlayer extends PlayerImpl { for (UUID opponentId : opponents) { Player opponent = game.getPlayer(opponentId); if (opponent != null - && target.canTarget(getId(), opponentId, source, game) + && target.canTarget(abilityControllerId, opponentId, source, game) && opponent.getLife() <= target.getAmountRemaining()) { return tryAddTarget(target, opponentId, opponent.getLife(), source, game); } @@ -1138,7 +1135,7 @@ public class ComputerPlayer extends PlayerImpl { // planeswalker kill for (Permanent permanent : targets) { - if (permanent.isPlaneswalker(game) && target.canTarget(getId(), permanent.getId(), source, game)) { + if (permanent.isPlaneswalker(game) && target.canTarget(abilityControllerId, permanent.getId(), source, game)) { int loy = permanent.getCounters(game).getCount(CounterType.LOYALTY); if (loy <= target.getAmountRemaining()) { return tryAddTarget(target, permanent.getId(), loy, source, game); @@ -1148,7 +1145,7 @@ public class ComputerPlayer extends PlayerImpl { // creature kill for (Permanent permanent : targets) { - if (permanent.isCreature(game) && target.canTarget(getId(), permanent.getId(), source, game)) { + if (permanent.isCreature(game) && target.canTarget(abilityControllerId, permanent.getId(), source, game)) { if (permanent.getToughness().getValue() <= target.getAmountRemaining()) { return tryAddTarget(target, permanent.getId(), permanent.getToughness().getValue(), source, game); } @@ -1168,22 +1165,22 @@ public class ComputerPlayer extends PlayerImpl { // planeswalkers for (Permanent permanent : targets) { - if (permanent.isPlaneswalker(game) && target.canTarget(getId(), permanent.getId(), source, game)) { + if (permanent.isPlaneswalker(game) && target.canTarget(abilityControllerId, permanent.getId(), source, game)) { return tryAddTarget(target, permanent.getId(), target.getAmountRemaining(), source, game); } } // players - if (outcome.isGood() && target.canTarget(getId(), getId(), source, game)) { + if (outcome.isGood() && target.canTarget(abilityControllerId, getId(), source, game)) { return tryAddTarget(target, getId(), target.getAmountRemaining(), source, game); } - if (!outcome.isGood() && target.canTarget(getId(), opponentId, source, game)) { + if (!outcome.isGood() && target.canTarget(abilityControllerId, opponentId, source, game)) { return tryAddTarget(target, opponentId, target.getAmountRemaining(), source, game); } // creature for (Permanent permanent : targets) { - if (permanent.isCreature(game) && target.canTarget(getId(), permanent.getId(), source, game)) { + if (permanent.isCreature(game) && target.canTarget(abilityControllerId, permanent.getId(), source, game)) { return tryAddTarget(target, permanent.getId(), target.getAmountRemaining(), source, game); } } @@ -1196,7 +1193,7 @@ public class ComputerPlayer extends PlayerImpl { } for (UUID opponentId : opponents) { if (!outcome.isGood()) { - // bad on yourself, uses weakest targets + // bad on yourself, uses the weakest targets targets = threats(getId(), source, StaticFilters.FILTER_PERMANENT, game, target.getTargets(), false); } else { targets = threats(opponentId, source, StaticFilters.FILTER_PERMANENT, game, target.getTargets(), false); @@ -1204,7 +1201,7 @@ public class ComputerPlayer extends PlayerImpl { // creatures - non killable (TODO: add extra skill checks like undestructeable) for (Permanent permanent : targets) { - if (permanent.isCreature(game) && target.canTarget(getId(), permanent.getId(), source, game)) { + if (permanent.isCreature(game) && target.canTarget(abilityControllerId, permanent.getId(), source, game)) { int safeDamage = Math.min(permanent.getToughness().getValue() - 1, target.getAmountRemaining()); if (safeDamage > 0) { return tryAddTarget(target, permanent.getId(), safeDamage, source, game); @@ -1214,23 +1211,25 @@ public class ComputerPlayer extends PlayerImpl { // creatures - all for (Permanent permanent : targets) { - if (permanent.isCreature(game) && target.canTarget(getId(), permanent.getId(), source, game)) { + if (permanent.isCreature(game) && target.canTarget(abilityControllerId, permanent.getId(), source, game)) { return tryAddTarget(target, permanent.getId(), target.getAmountRemaining(), source, game); } } // planeswalkers for (Permanent permanent : targets) { - if (permanent.isPlaneswalker(game) && target.canTarget(getId(), permanent.getId(), source, game)) { + if (permanent.isPlaneswalker(game) && target.canTarget(abilityControllerId, permanent.getId(), source, game)) { return tryAddTarget(target, permanent.getId(), target.getAmountRemaining(), source, game); } } } // players for (UUID opponentId : opponents) { - if (target.canTarget(getId(), getId(), source, game)) { + if (target.canTarget(abilityControllerId, getId(), source, game)) { + // on itself return tryAddTarget(target, getId(), target.getAmountRemaining(), source, game); - } else if (target.canTarget(getId(), opponentId, source, game)) { + } else if (target.canTarget(abilityControllerId, opponentId, source, game)) { + // on opponent return tryAddTarget(target, opponentId, target.getAmountRemaining(), source, game); } } @@ -1249,7 +1248,7 @@ public class ComputerPlayer extends PlayerImpl { } private boolean priorityPlay(Game game) { - UUID opponentId = game.getOpponents(playerId).iterator().next(); + UUID opponentId = getRandomOpponent(game); if (game.isActivePlayer(playerId)) { if (game.isMainPhase() && game.getStack().isEmpty()) { playLand(game); @@ -1978,7 +1977,7 @@ public class ComputerPlayer extends PlayerImpl { if (outcome == Outcome.Detriment) { // choose a creature type of opponent on battlefield or graveyard for (Permanent permanent : game.getBattlefield().getActivePermanents(this.getId(), game)) { - if (game.getOpponents(this.getId()).contains(permanent.getControllerId()) + if (game.getOpponents(getId(), true).contains(permanent.getControllerId()) && permanent.getCardType(game).contains(CardType.CREATURE) && !permanent.getSubtype(game).isEmpty()) { if (choice.getChoices().contains(permanent.getSubtype(game).get(0).toString())) { @@ -1989,7 +1988,7 @@ public class ComputerPlayer extends PlayerImpl { } // or in opponent graveyard if (!choice.isChosen()) { - for (UUID opponentId : game.getOpponents(this.getId())) { + for (UUID opponentId : game.getOpponents(getId(), true)) { Player opponent = game.getPlayer(opponentId); for (Card card : opponent.getGraveyard().getCards(game)) { if (card != null && card.getCardType(game).contains(CardType.CREATURE) && !card.getSubtype(game).isEmpty()) { @@ -2046,7 +2045,7 @@ public class ComputerPlayer extends PlayerImpl { // we still use playerId when getting cards even if they don't control the search List cardChoices = new ArrayList<>(cards.getCards(target.getFilter(), playerId, source, game)); while (!target.doneChoosing(game)) { - Card card = pickTarget(abilityControllerId, cardChoices, outcome, target, source, game); + Card card = selectCardTarget(abilityControllerId, cardChoices, outcome, target, source, game); if (card != null) { target.addTarget(card.getId(), source, game); cardChoices.remove(card); @@ -2077,10 +2076,10 @@ public class ComputerPlayer extends PlayerImpl { List cardChoices = new ArrayList<>(cards.getCards(target.getFilter(), abilityControllerId, source, game)); while (!target.doneChoosing(game)) { - Card card = pickTarget(abilityControllerId, cardChoices, outcome, target, source, game); + Card card = selectCard(abilityControllerId, cardChoices, outcome, target, game); if (card != null) { target.add(card.getId(), game); - cardChoices.remove(card); + cardChoices.remove(card); // selectCard don't remove cards (only on second+ tries) } else { // We don't have any valid target to choose so stop choosing return target.getTargets().size() >= target.getNumberOfTargets(); @@ -2385,26 +2384,28 @@ public class ComputerPlayer extends PlayerImpl { tournament.submitDeck(playerId, deck); } - public Card pickBestCard(List cards, List chosenColors) { - if (cards.isEmpty()) { - return null; - } - Card bestCard = null; - int maxScore = 0; - for (Card card : cards) { - int score = RateCard.rateCard(card, chosenColors); - if (bestCard == null || score > maxScore) { - maxScore = score; - bestCard = card; - } - } - return bestCard; + public Card selectBestCard(List cards, List chosenColors) { + return selectBestCardInner(cards, chosenColors, null, null, null); } - public Card pickBestCard(List cards, List chosenColors, Target target, Ability source, Game game) { + public Card selectBestCardTarget(List cards, List chosenColors, Target target, Ability targetingSource, Game game) { + return selectBestCardInner(cards, chosenColors, target, targetingSource, game); + } + + /** + * @param targetingSource null on non-target choice like choose and source on targeting choice like chooseTarget + */ + public Card selectBestCardInner(List cards, List chosenColors, Target target, Ability targetingSource, Game game) { if (cards.isEmpty()) { return null; } + + // sometimes a target selection can be made from a player that does not control the ability + UUID abilityControllerId = playerId; + if (target != null && target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); + } + Card bestCard = null; int maxScore = 0; for (Card card : cards) { @@ -2413,9 +2414,9 @@ public class ComputerPlayer extends PlayerImpl { if (bestCard == null) { // we need any card to prevent NPE in callers betterCard = true; } else if (score > maxScore) { // we need better card - if (target != null && source != null && game != null) { + if (target != null && targetingSource != null && game != null) { // but also check it can be targeted - betterCard = target.canTarget(getId(), card.getId(), source, game); + betterCard = target.canTarget(abilityControllerId, card.getId(), targetingSource, game); } else { // target object wasn't provided, so accepting it anyway betterCard = true; @@ -2430,10 +2431,28 @@ public class ComputerPlayer extends PlayerImpl { return bestCard; } - public Card pickWorstCard(List cards, List chosenColors, Target target, Ability source, Game game) { + public Card selectWorstCard(List cards, List chosenColors) { + return selectWorstCardInner(cards, chosenColors, null, null, null); + } + + public Card selectWorstCardTarget(List cards, List chosenColors, Target target, Ability targetingSource, Game game) { + return selectWorstCardInner(cards, chosenColors, target, targetingSource, game); + } + + /** + * @param targetingSource null on non-target choice like choose and source on targeting choice like chooseTarget + */ + public Card selectWorstCardInner(List cards, List chosenColors, Target target, Ability targetingSource, Game game) { if (cards.isEmpty()) { return null; } + + // sometimes a target selection can be made from a player that does not control the ability + UUID abilityControllerId = playerId; + if (target != null && target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); + } + Card worstCard = null; int minScore = Integer.MAX_VALUE; for (Card card : cards) { @@ -2442,9 +2461,9 @@ public class ComputerPlayer extends PlayerImpl { if (worstCard == null) { // we need any card to prevent NPE in callers worseCard = true; } else if (score < minScore) { // we need worse card - if (target != null && source != null && game != null) { + if (target != null && targetingSource != null && game != null) { // but also check it can be targeted - worseCard = target.canTarget(getId(), card.getId(), source, game); + worseCard = target.canTarget(abilityControllerId, card.getId(), targetingSource, game); } else { // target object wasn't provided, so accepting it anyway worseCard = true; @@ -2459,36 +2478,20 @@ public class ComputerPlayer extends PlayerImpl { return worstCard; } - public Card pickWorstCard(List cards, List chosenColors) { - if (cards.isEmpty()) { - return null; - } - Card worstCard = null; - int minScore = Integer.MAX_VALUE; - for (Card card : cards) { - int score = RateCard.rateCard(card, chosenColors); - if (worstCard == null || score < minScore) { - minScore = score; - worstCard = card; - } - } - return worstCard; - } - @Override public void pickCard(List cards, Deck deck, Draft draft) { if (cards.isEmpty()) { throw new IllegalArgumentException("No cards to pick from."); } try { - Card bestCard = pickBestCard(cards, chosenColors); + Card bestCard = selectBestCard(cards, chosenColors); int maxScore = RateCard.rateCard(bestCard, chosenColors); int pickedCardRate = RateCard.getBaseCardScore(bestCard); if (pickedCardRate <= 30) { // if card is bad // try to counter pick without any color restriction - Card counterPick = pickBestCard(cards, Collections.emptyList()); + Card counterPick = selectBestCard(cards, Collections.emptyList()); int counterPickScore = RateCard.getBaseCardScore(counterPick); // card is really good // take it! @@ -2901,12 +2904,20 @@ public class ComputerPlayer extends PlayerImpl { return before != after; } + private boolean selectPlayer(Outcome outcome, Target target, UUID abilityControllerId, UUID randomOpponentId, Game game, boolean required) { + return selectPlayerInner(outcome, target, null, abilityControllerId, randomOpponentId, game, required); + } + + private boolean selectPlayerTarget(Outcome outcome, Target target, Ability targetingSource, UUID abilityControllerId, UUID randomOpponentId, Game game, boolean required) { + return selectPlayerInner(outcome, target, targetingSource, abilityControllerId, randomOpponentId, game, required); + } + /** * Sets a possible target player. Depends on bad/good outcome * - * @param source null on choose and non-null on chooseTarget + * @param targetingSource null on non-target choice like choose and source on targeting choice like chooseTarget */ - private boolean setTargetPlayer(Outcome outcome, Target target, Ability source, UUID abilityControllerId, UUID randomOpponentId, Game game, boolean required) { + private boolean selectPlayerInner(Outcome outcome, Target target, Ability targetingSource, UUID abilityControllerId, UUID randomOpponentId, Game game, boolean required) { Outcome affectedOutcome; if (abilityControllerId == this.playerId) { // selects for itself @@ -2917,36 +2928,36 @@ public class ComputerPlayer extends PlayerImpl { } if (target.getOriginalTarget() instanceof TargetOpponent) { - if (source == null) { + if (targetingSource == null) { if (target.canTarget(randomOpponentId, game)) { target.add(randomOpponentId, game); return true; } - } else if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { - target.addTarget(randomOpponentId, source, game); + } else if (target.canTarget(abilityControllerId, randomOpponentId, targetingSource, game)) { + target.addTarget(randomOpponentId, targetingSource, game); return true; } - for (UUID possibleOpponentId : game.getOpponents(abilityControllerId)) { - if (source == null) { + for (UUID possibleOpponentId : game.getOpponents(getId(), true)) { + if (targetingSource == null) { if (target.canTarget(possibleOpponentId, game)) { target.add(possibleOpponentId, game); return true; } - } else if (target.canTarget(abilityControllerId, possibleOpponentId, source, game)) { - target.addTarget(possibleOpponentId, source, game); + } else if (target.canTarget(abilityControllerId, possibleOpponentId, targetingSource, game)) { + target.addTarget(possibleOpponentId, targetingSource, game); return true; } } return false; } - UUID sourceId = source != null ? source.getSourceId() : null; + UUID sourceId = targetingSource != null ? targetingSource.getSourceId() : null; if (target.getOriginalTarget() instanceof TargetPlayer) { if (affectedOutcome.isGood()) { - if (source == null) { + if (targetingSource == null) { // good - if (target.canTarget(abilityControllerId, game)) { - target.add(abilityControllerId, game); + if (target.canTarget(getId(), game)) { + target.add(getId(), game); return true; } if (target.isRequired(sourceId, game)) { @@ -2957,38 +2968,38 @@ public class ComputerPlayer extends PlayerImpl { } } else { // good - if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { - target.addTarget(abilityControllerId, source, game); + if (target.canTarget(abilityControllerId, getId(), targetingSource, game)) { + target.addTarget(getId(), targetingSource, game); return true; } if (target.isRequired(sourceId, game)) { - if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { - target.addTarget(randomOpponentId, source, game); + if (target.canTarget(abilityControllerId, randomOpponentId, targetingSource, game)) { + target.addTarget(randomOpponentId, targetingSource, game); return true; } } } - } else if (source == null) { + } else if (targetingSource == null) { // bad if (target.canTarget(randomOpponentId, game)) { target.add(randomOpponentId, game); return true; } if (target.isRequired(sourceId, game)) { - if (target.canTarget(abilityControllerId, game)) { - target.add(abilityControllerId, game); + if (target.canTarget(getId(), game)) { + target.add(getId(), game); return true; } } } else { // bad - if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { - target.addTarget(randomOpponentId, source, game); + if (target.canTarget(abilityControllerId, randomOpponentId, targetingSource, game)) { + target.addTarget(randomOpponentId, targetingSource, game); return true; } if (required) { - if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { - target.addTarget(abilityControllerId, source, game); + if (target.canTarget(abilityControllerId, getId(), targetingSource, game)) { + target.addTarget(getId(), targetingSource, game); return true; } } @@ -3001,13 +3012,10 @@ public class ComputerPlayer extends PlayerImpl { /** * Returns an opponent by random - * - * @param abilityControllerId - * @param game - * @return */ - private UUID getRandomOpponent(UUID abilityControllerId, Game game) { - return RandomUtil.randomFromCollection(game.getOpponents(abilityControllerId)); + @Deprecated // TODO: rework all usages and replace to all possible opponents instead single + private UUID getRandomOpponent(Game game) { + return RandomUtil.randomFromCollection(game.getOpponents(getId(), true)); } @Override diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/ActionSimulator.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/ActionSimulator.java index 1f3c00d3ab2..6f112e25441 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/ActionSimulator.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/ActionSimulator.java @@ -34,7 +34,7 @@ public class ActionSimulator { } public int evaluateState() { - Player opponent = game.getPlayer(game.getOpponents(player.getId()).stream().findFirst().orElse(null)); + Player opponent = game.getPlayer(game.getOpponents(player.getId(), true).stream().findFirst().orElse(null)); if (opponent == null) { return Integer.MAX_VALUE; } diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SelectAttackersNextAction.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SelectAttackersNextAction.java index 3a3919c9489..1fa0038522b 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SelectAttackersNextAction.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SelectAttackersNextAction.java @@ -17,7 +17,7 @@ public class SelectAttackersNextAction implements MCTSNodeNextAction{ attacks = player.getAttacks(game); else attacks = getAttacks(player, fullStateValue, game); - UUID defenderId = game.getOpponents(player.getId()).iterator().next(); + UUID defenderId = game.getOpponents(player.getId(), true).iterator().next(); for (List attack: attacks) { Game sim = game.createSimulationForAI(); MCTSPlayer simPlayer = (MCTSPlayer) sim.getPlayer(player.getId()); diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java index 5fd1b4ea3b2..a7ac9164be1 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java @@ -141,7 +141,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer { @Override public void selectAttackers(Game game, UUID attackingPlayerId) { //useful only for two player games - will only attack first opponent - UUID defenderId = game.getOpponents(playerId).iterator().next(); + UUID defenderId = game.getOpponents(playerId, true).iterator().next(); List attackersList = super.getAvailableAttackers(defenderId, game); //use binary digits to calculate powerset of attackers int powerElements = (int) Math.pow(2, attackersList.size()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/AttackIfAbleTargetRandomOpponentSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/AttackIfAbleTargetRandomOpponentSourceEffect.java index 0042d1708fa..59843b03a09 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/AttackIfAbleTargetRandomOpponentSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/AttackIfAbleTargetRandomOpponentSourceEffect.java @@ -36,7 +36,7 @@ public class AttackIfAbleTargetRandomOpponentSourceEffect extends OneShotEffect if (controller == null) { return false; } - List opponents = new ArrayList<>(game.getOpponents(controller.getId())); + List opponents = new ArrayList<>(game.getOpponents(controller.getId(), true)); Player opponent = game.getPlayer(opponents.get(RandomUtil.nextInt(opponents.size()))); if (opponent != null) { game.informPlayers(opponent.getLogName() + " was chosen at random."); diff --git a/Mage/src/main/java/mage/target/Target.java b/Mage/src/main/java/mage/target/Target.java index 9664a0a10ca..92fd3dbea7c 100644 --- a/Mage/src/main/java/mage/target/Target.java +++ b/Mage/src/main/java/mage/target/Target.java @@ -55,6 +55,9 @@ public interface Target extends Serializable { boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game); + /** + * Add target from targeting methods like chooseTarget (will check and generate target events and effects) + */ void addTarget(UUID id, Ability source, Game game); void addTarget(UUID id, int amount, Ability source, Game game); @@ -90,6 +93,9 @@ public interface Target extends Serializable { boolean choose(Outcome outcome, UUID playerId, UUID sourceId, Ability source, Game game); + /** + * Add target from non targeting methods like choose + */ void add(UUID id, Game game); void remove(UUID targetId); -- 2.47.2 From 3b421de2e30af45455f5c1970cb399cfdece2b27 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 19 Apr 2025 10:37:44 +0400 Subject: [PATCH 82/95] Veteran Survivor - fixed game error in some use cases --- Mage.Sets/src/mage/cards/v/VeteranSurvivor.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/cards/v/VeteranSurvivor.java b/Mage.Sets/src/mage/cards/v/VeteranSurvivor.java index b890eb823a4..e141aaeac14 100644 --- a/Mage.Sets/src/mage/cards/v/VeteranSurvivor.java +++ b/Mage.Sets/src/mage/cards/v/VeteranSurvivor.java @@ -15,6 +15,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; +import mage.game.ExileZone; import mage.game.Game; import mage.target.common.TargetCardInGraveyard; import mage.util.CardUtil; @@ -67,10 +68,7 @@ enum VeteranSurvivorCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - return game - .getExile() - .getExileZone(CardUtil.getExileZoneId(game, source)) - .getCards(game) - .size() >= 3; + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + return exileZone != null && exileZone.getCards(game).size() >= 3; } } -- 2.47.2 From f83c2bea4b4444baaf265133c0a32520686c345b Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 19 Apr 2025 17:48:27 +0400 Subject: [PATCH 83/95] merge fix --- .../src/mage/player/ai/GameStateEvaluator2.java | 2 +- .../main/java/mage/player/ai/simulators/ActionSimulator.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/GameStateEvaluator2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/GameStateEvaluator2.java index f1d7b64205a..ebc0dbcdade 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/GameStateEvaluator2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/GameStateEvaluator2.java @@ -32,7 +32,7 @@ public final class GameStateEvaluator2 { public static PlayerEvaluateScore evaluate(UUID playerId, Game game, boolean useCombatPermanentScore) { // TODO: add multi opponents support, so AI can take better actions Player player = game.getPlayer(playerId); - // must find all leaved opponents too + // must find all leaved opponents Player opponent = game.getPlayer(game.getOpponents(playerId, false).stream().findFirst().orElse(null)); if (opponent == null) { return new PlayerEvaluateScore(playerId, WIN_GAME_SCORE); diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/ActionSimulator.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/ActionSimulator.java index 6f112e25441..e67d6383710 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/ActionSimulator.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/simulators/ActionSimulator.java @@ -34,7 +34,8 @@ public class ActionSimulator { } public int evaluateState() { - Player opponent = game.getPlayer(game.getOpponents(player.getId(), true).stream().findFirst().orElse(null)); + // must find all leaved opponents + Player opponent = game.getPlayer(game.getOpponents(player.getId(), false).stream().findFirst().orElse(null)); if (opponent == null) { return Integer.MAX_VALUE; } -- 2.47.2 From fd50341b99f0707d20511a4515c17d2f76dd9f01 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 19 Apr 2025 17:55:33 +0400 Subject: [PATCH 84/95] bump version --- Mage.Common/src/main/java/mage/utils/MageVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java index 07ba1547c1a..7aef255e537 100644 --- a/Mage.Common/src/main/java/mage/utils/MageVersion.java +++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java @@ -18,7 +18,7 @@ public class MageVersion implements Serializable, Comparable { public static final int MAGE_VERSION_MAJOR = 1; public static final int MAGE_VERSION_MINOR = 4; public static final int MAGE_VERSION_RELEASE = 57; - public static final String MAGE_VERSION_RELEASE_INFO = "V1"; // V1, V1a, V1b for releases; V1-beta3, V1-beta4 for betas + public static final String MAGE_VERSION_RELEASE_INFO = "V2"; // V1, V1a, V1b for releases; V1-beta3, V1-beta4 for betas // strict mode // Each update requires a strict version -- 2.47.2 From 9487455feb502b02f52d00250278e1422bcf4b6f Mon Sep 17 00:00:00 2001 From: PurpleCrowbar <26198472+PurpleCrowbar@users.noreply.github.com> Date: Sun, 20 Apr 2025 16:07:32 +0100 Subject: [PATCH 85/95] Fix Psychic Frog exiling cards to unused source exile zone --- Mage.Sets/src/mage/cards/p/PsychicFrog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/p/PsychicFrog.java b/Mage.Sets/src/mage/cards/p/PsychicFrog.java index 9c020c12f65..26aad092681 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicFrog.java +++ b/Mage.Sets/src/mage/cards/p/PsychicFrog.java @@ -44,7 +44,7 @@ public final class PsychicFrog extends CardImpl { // Exile three cards from your graveyard: Psychic Frog gains flying until end of turn. this.addAbility(new SimpleActivatedAbility( new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), - new ExileFromGraveCost(new TargetCardInYourGraveyard(3)) + new ExileFromGraveCost(new TargetCardInYourGraveyard(3)).withSourceExileZone(false) )); } -- 2.47.2 From 4691c40451d01c6d452276f1a6b20a4addb00991 Mon Sep 17 00:00:00 2001 From: Steven Knipe Date: Sun, 20 Apr 2025 13:44:23 -0700 Subject: [PATCH 86/95] Fix Spectral Denial missing target --- Mage.Sets/src/mage/cards/s/SpectralDenial.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/cards/s/SpectralDenial.java b/Mage.Sets/src/mage/cards/s/SpectralDenial.java index 78487a657cb..4dbc47f11f2 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralDenial.java +++ b/Mage.Sets/src/mage/cards/s/SpectralDenial.java @@ -16,6 +16,7 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; +import mage.target.TargetSpell; import java.util.UUID; @@ -43,6 +44,7 @@ public final class SpectralDenial extends CardImpl { // Counter target spell unless its controller pays {X}. this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(GetXValue.instance)); + this.getSpellAbility().addTarget(new TargetSpell()); } private SpectralDenial(final SpectralDenial card) { -- 2.47.2 From 7981d4a9ca07f1b6291b21783461dc52f038918c Mon Sep 17 00:00:00 2001 From: xenohedron <12538125+xenohedron@users.noreply.github.com> Date: Sun, 20 Apr 2025 17:18:55 -0400 Subject: [PATCH 87/95] fix #13558 (Will of the Mardu) --- Mage.Sets/src/mage/cards/w/WillOfTheMardu.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/w/WillOfTheMardu.java b/Mage.Sets/src/mage/cards/w/WillOfTheMardu.java index 946f4a4c935..b3ce72955dd 100644 --- a/Mage.Sets/src/mage/cards/w/WillOfTheMardu.java +++ b/Mage.Sets/src/mage/cards/w/WillOfTheMardu.java @@ -16,6 +16,7 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.token.RedWarriorToken; import mage.players.Player; +import mage.target.TargetPlayer; import mage.target.common.TargetCreaturePermanent; import java.util.Objects; @@ -38,7 +39,8 @@ public final class WillOfTheMardu extends CardImpl { // * Create a number of 1/1 red Warrior creature tokens equal to the number of creatures target player controls. this.getSpellAbility().addEffect(new CreateTokenEffect(new RedWarriorToken(), WillOfTheMarduValue.instance) - .setText("reate a number of 1/1 red Warrior creature tokens equal to the number of creatures target player controls")); + .setText("Create a number of 1/1 red Warrior creature tokens equal to the number of creatures target player controls")); + this.getSpellAbility().addTarget(new TargetPlayer()); // * Will of the Mardu deals damage to target creature equal to the number of creatures you control. this.getSpellAbility().addMode(new Mode(new DamageTargetEffect(CreaturesYouControlCount.instance) -- 2.47.2 From b20f89c1f99c8825920cf6ad46f8684ef5f6c9b5 Mon Sep 17 00:00:00 2001 From: PurpleCrowbar <26198472+PurpleCrowbar@users.noreply.github.com> Date: Tue, 22 Apr 2025 17:16:30 +0100 Subject: [PATCH 88/95] Struggle for Project Purity: remove unused filter, rules text fix for Brotherhood mode --- Mage.Sets/src/mage/cards/s/StruggleForProjectPurity.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/StruggleForProjectPurity.java b/Mage.Sets/src/mage/cards/s/StruggleForProjectPurity.java index 5a46b36aeb4..b2bfb094120 100644 --- a/Mage.Sets/src/mage/cards/s/StruggleForProjectPurity.java +++ b/Mage.Sets/src/mage/cards/s/StruggleForProjectPurity.java @@ -18,8 +18,6 @@ import mage.constants.ModeChoice; import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; @@ -66,7 +64,7 @@ class StruggleForProjectDrawEffect extends OneShotEffect { StruggleForProjectDrawEffect() { super(Outcome.DrawCard); - this.staticText = "Each opponent draws a card. You draw a card for each card drawn this way."; + this.staticText = "each opponent draws a card. You draw a card for each card drawn this way."; } private StruggleForProjectDrawEffect(final StruggleForProjectDrawEffect effect) { @@ -136,8 +134,6 @@ class StruggleForProjectRadCountersTriggeredAbility extends TriggeredAbilityImpl } this.getEffects().clear(); - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new ControllerIdPredicate(event.getPlayerId())); Effect effect = new AddCountersTargetEffect( CounterType.RAD.createInstance(), StaticValue.get(attackersOnYou.size() * 2) -- 2.47.2 From 36196742ad9173da24be4e584f90ac24f25954da Mon Sep 17 00:00:00 2001 From: PurpleCrowbar <26198472+PurpleCrowbar@users.noreply.github.com> Date: Tue, 22 Apr 2025 19:47:11 +0100 Subject: [PATCH 89/95] Update EDH bans --- .../Mage.Deck.Constructed/src/mage/deck/Commander.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java index 3724522e671..ae3a24b7ebd 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Commander.java @@ -17,15 +17,12 @@ public class Commander extends AbstractCommander { banned.add("Balance"); banned.add("Biorhythm"); banned.add("Black Lotus"); - banned.add("Braids, Cabal Minion"); banned.add("Channel"); - banned.add("Coalition Victory"); banned.add("Dockside Extortionist"); banned.add("Emrakul, the Aeons Torn"); banned.add("Erayo, Soratami Ascendant"); banned.add("Fastbond"); banned.add("Flash"); - banned.add("Gifts Ungiven"); banned.add("Golos, Tireless Pilgrim"); banned.add("Griselbrand"); banned.add("Hullbreacher"); @@ -44,14 +41,12 @@ public class Commander extends AbstractCommander { banned.add("Mox Ruby"); banned.add("Mox Sapphire"); banned.add("Nadu, Winged Wisdom"); - banned.add("Panoptic Mirror"); banned.add("Paradox Engine"); banned.add("Primeval Titan"); banned.add("Prophet of Kruphix"); banned.add("Recurring Nightmare"); banned.add("Rofellos, Llanowar Emissary"); banned.add("Sundering Titan"); - banned.add("Sway of the Stars"); banned.add("Sylvan Primordial"); banned.add("Time Vault"); banned.add("Time Walk"); -- 2.47.2 From e34ebe740ed85e4f4a0f901296fc037864fa6705 Mon Sep 17 00:00:00 2001 From: Jmlundeen <98545818+Jmlundeen@users.noreply.github.com> Date: Tue, 22 Apr 2025 17:37:57 -0500 Subject: [PATCH 90/95] Feature: Retro Border Renderer (#13563) * Add Retro Card Renderer * Updated old sets with retro frames Adds sets: * 30th Anniversary Play Promos * 30th Anniversary Misc Promos * Eternal Weekend * MagicFest 2025 * Modern Horizon 2 Timeshifts --- .../mage/client/dialog/PreferencesDialog.java | 12 + .../mage/card/arcane/CardRendererFactory.java | 4 + .../mage/card/arcane/ModernCardRenderer.java | 3 +- .../mage/card/arcane/RetroCardRenderer.java | 1287 +++++++++++++++++ .../dl/sources/ScryfallImageSupportCards.java | 4 + .../background_texture_artifact_retro.png | Bin 0 -> 128050 bytes .../background_texture_black_retro.png | Bin 0 -> 416423 bytes .../background_texture_blue_retro.png | Bin 0 -> 398469 bytes .../background_texture_colorless_retro.png | Bin 0 -> 249306 bytes .../background_texture_gold_retro.png | Bin 0 -> 182458 bytes .../background_texture_green_retro.png | Bin 0 -> 396743 bytes .../background_texture_land_retro.png | Bin 0 -> 412703 bytes .../background_texture_red_retro.png | Bin 0 -> 432124 bytes .../background_texture_white_retro.png | Bin 0 -> 226256 bytes .../src/main/java/mage/view/CardView.java | 5 + Mage.Sets/src/mage/sets/Alliances.java | 384 ++--- Mage.Sets/src/mage/sets/Anthologies.java | 170 +-- Mage.Sets/src/mage/sets/Antiquities.java | 201 +-- Mage.Sets/src/mage/sets/Apocalypse.java | 286 ++-- Mage.Sets/src/mage/sets/ArabianNights.java | 180 +-- Mage.Sets/src/mage/sets/ArenaLeague1996.java | 14 +- Mage.Sets/src/mage/sets/ArenaLeague1999.java | 20 +- Mage.Sets/src/mage/sets/ArenaLeague2000.java | 22 +- Mage.Sets/src/mage/sets/ArenaLeague2001.java | 24 +- Mage.Sets/src/mage/sets/ArenaLeague2002.java | 10 +- Mage.Sets/src/mage/sets/ArenaLeague2003.java | 14 +- .../mage/sets/ArenaNewPlayerExperience.java | 70 +- .../src/mage/sets/AsiaPacificLandProgram.java | 30 +- .../src/mage/sets/BattleRoyaleBoxSet.java | 274 ++-- Mage.Sets/src/mage/sets/BeatdownBoxSet.java | 182 +-- Mage.Sets/src/mage/sets/Chronicles.java | 248 ++-- .../src/mage/sets/ClassicSixthEdition.java | 701 ++++----- Mage.Sets/src/mage/sets/CommanderMasters.java | 114 +- .../src/mage/sets/DCILegendMembership.java | 4 +- Mage.Sets/src/mage/sets/Deckmasters.java | 114 +- .../src/mage/sets/DominariaRemastered.java | 348 ++--- Mage.Sets/src/mage/sets/DragonCon.java | 2 +- Mage.Sets/src/mage/sets/EternalWeekend.java | 31 + .../src/mage/sets/EuropeanLandProgram.java | 30 +- Mage.Sets/src/mage/sets/Exodus.java | 287 ++-- Mage.Sets/src/mage/sets/FallenEmpires.java | 374 ++--- Mage.Sets/src/mage/sets/FifthEdition.java | 904 ++++++------ Mage.Sets/src/mage/sets/FourthEdition.java | 748 +++++----- .../src/mage/sets/FridayNightMagic2000.java | 22 +- .../src/mage/sets/FridayNightMagic2001.java | 14 +- .../src/mage/sets/FridayNightMagic2002.java | 24 +- .../src/mage/sets/FridayNightMagic2003.java | 16 +- Mage.Sets/src/mage/sets/Guru.java | 10 +- .../src/mage/sets/HarperPrismBookPromos.java | 10 +- Mage.Sets/src/mage/sets/Homelands.java | 278 ++-- Mage.Sets/src/mage/sets/IceAge.java | 734 +++++----- .../src/mage/sets/InnistradRemastered.java | 380 ++--- .../mage/sets/IntroductoryTwoPlayerSet.java | 136 +- Mage.Sets/src/mage/sets/Invasion.java | 703 ++++----- .../src/mage/sets/JudgeGiftCards1998.java | 6 +- .../src/mage/sets/JudgeGiftCards1999.java | 2 +- .../src/mage/sets/JudgeGiftCards2000.java | 4 +- .../src/mage/sets/JudgeGiftCards2001.java | 4 +- .../src/mage/sets/JudgeGiftCards2002.java | 4 +- .../src/mage/sets/JudgeGiftCards2003.java | 6 +- .../src/mage/sets/JudgeGiftCards2011.java | 4 +- .../src/mage/sets/JudgeGiftCards2012.java | 4 +- .../src/mage/sets/JudgeGiftCards2013.java | 4 +- .../src/mage/sets/JudgeGiftCards2014.java | 2 +- .../src/mage/sets/JudgeGiftCards2022.java | 2 +- .../src/mage/sets/JudgeGiftCards2023.java | 10 +- Mage.Sets/src/mage/sets/Judgment.java | 284 ++-- .../src/mage/sets/JuniorSuperSeries.java | 14 +- Mage.Sets/src/mage/sets/Legends.java | 605 ++++---- Mage.Sets/src/mage/sets/Legions.java | 291 ++-- .../src/mage/sets/LimitedEditionAlpha.java | 561 +++---- .../src/mage/sets/LimitedEditionBeta.java | 573 ++++---- Mage.Sets/src/mage/sets/LoveYourLGS2021.java | 19 +- Mage.Sets/src/mage/sets/MagicFest2025.java | 30 + .../src/mage/sets/MagicOnlinePromos.java | 572 ++++---- .../src/mage/sets/MagicOnlineThemeDecks.java | 190 ++- .../src/mage/sets/MagicPlayerRewards2001.java | 2 +- .../src/mage/sets/MagicPlayerRewards2003.java | 2 +- .../sets/MarchOfTheMachineTheAftermath.java | 192 +-- Mage.Sets/src/mage/sets/MastersEdition.java | 390 ++--- Mage.Sets/src/mage/sets/MastersEditionII.java | 481 +++--- .../src/mage/sets/MastersEditionIII.java | 460 +++--- Mage.Sets/src/mage/sets/MastersEditionIV.java | 537 +++---- .../sets/MediaAndCollaborationPromos.java | 47 +- Mage.Sets/src/mage/sets/MercadianMasques.java | 701 ++++----- Mage.Sets/src/mage/sets/Mirage.java | 698 ++++----- .../mage/sets/ModernHorizons1Timeshifts.java | 80 +- Mage.Sets/src/mage/sets/ModernHorizons2.java | 176 +-- .../mage/sets/ModernHorizons2Timeshifts.java | 40 + Mage.Sets/src/mage/sets/ModernHorizons3.java | 180 +-- .../src/mage/sets/MultiverseGiftBox.java | 20 +- Mage.Sets/src/mage/sets/Nemesis.java | 287 ++-- Mage.Sets/src/mage/sets/Odyssey.java | 705 ++++----- Mage.Sets/src/mage/sets/Onslaught.java | 700 ++++----- Mage.Sets/src/mage/sets/Planeshift.java | 292 ++-- Mage.Sets/src/mage/sets/Portal.java | 506 +++---- Mage.Sets/src/mage/sets/PortalSecondAge.java | 329 ++--- .../src/mage/sets/PortalThreeKingdoms.java | 361 ++--- .../src/mage/sets/ProTourCollectorSet.java | 614 ++++---- Mage.Sets/src/mage/sets/Prophecy.java | 288 ++-- .../src/mage/sets/RavnicaClueEdition.java | 2 +- .../src/mage/sets/RavnicaRemastered.java | 350 +++-- Mage.Sets/src/mage/sets/RevisedEdition.java | 583 ++++---- .../src/mage/sets/RivalsQuickStartSet.java | 130 +- Mage.Sets/src/mage/sets/Scourge.java | 287 ++-- ...SecretLair30thAnniversaryCountdownKit.java | 6 +- Mage.Sets/src/mage/sets/SecretLairDrop.java | 155 +- .../src/mage/sets/SegaDreamcastCards.java | 4 +- Mage.Sets/src/mage/sets/SeventhEdition.java | 704 ++++----- Mage.Sets/src/mage/sets/Starter1999.java | 345 ++--- Mage.Sets/src/mage/sets/Starter2000.java | 41 +- Mage.Sets/src/mage/sets/Stronghold.java | 286 ++-- Mage.Sets/src/mage/sets/SummerMagicEdgar.java | 611 ++++---- .../src/mage/sets/TarkirDragonstorm.java | 2 +- Mage.Sets/src/mage/sets/Tempest.java | 693 ++++----- .../mage/sets/The30thAnniversaryEdition.java | 598 ++++---- .../sets/The30thAnniversaryMiscPromos.java | 30 + .../sets/The30thAnniversaryPlayPromos.java | 56 + Mage.Sets/src/mage/sets/TheBrothersWar.java | 4 +- .../mage/sets/TheBrothersWarCommander.java | 348 ++--- .../sets/TheBrothersWarRetroArtifacts.java | 315 ++-- Mage.Sets/src/mage/sets/TheDark.java | 238 +-- .../src/mage/sets/TimeSpiralRemastered.java | 246 ++-- .../src/mage/sets/TimeSpiralTimeshifted.java | 241 +-- Mage.Sets/src/mage/sets/Torment.java | 284 ++-- Mage.Sets/src/mage/sets/Unglued.java | 78 +- Mage.Sets/src/mage/sets/Unhinged.java | 8 +- Mage.Sets/src/mage/sets/UnlimitedEdition.java | 573 ++++---- Mage.Sets/src/mage/sets/Unsanctioned.java | 12 +- Mage.Sets/src/mage/sets/UrzasDestiny.java | 287 ++-- Mage.Sets/src/mage/sets/UrzasLegacy.java | 287 ++-- Mage.Sets/src/mage/sets/UrzasSaga.java | 708 ++++----- Mage.Sets/src/mage/sets/Visions.java | 329 +++-- Mage.Sets/src/mage/sets/Weatherlight.java | 335 ++--- .../sets/WizardsOfTheCoastOnlineStore.java | 2 +- .../src/mage/sets/WizardsPlayNetwork2021.java | 2 +- .../src/mage/sets/WizardsPlayNetwork2023.java | 10 +- .../src/mage/sets/WizardsPlayNetwork2024.java | 16 +- .../src/mage/sets/WizardsPlayNetwork2025.java | 4 +- .../mage/sets/WorldChampionshipDecks1997.java | 242 ++-- .../mage/sets/WorldChampionshipDecks1998.java | 214 +-- .../mage/sets/WorldChampionshipDecks1999.java | 200 +-- .../mage/sets/WorldChampionshipDecks2000.java | 214 +-- .../mage/sets/WorldChampionshipDecks2001.java | 252 ++-- .../mage/sets/WorldChampionshipDecks2002.java | 280 ++-- .../mage/sets/WorldChampionshipPromos.java | 3 +- .../java/mage/verify/mtgjson/MtgJsonCard.java | 1 + .../java/mage/verify/VerifyCardDataTest.java | 46 +- Mage/src/main/java/mage/cards/ArtRect.java | 1 + .../main/java/mage/cards/ExpansionSet.java | 9 + Mage/src/main/java/mage/cards/FrameStyle.java | 6 +- 151 files changed, 16549 insertions(+), 14836 deletions(-) create mode 100644 Mage.Client/src/main/java/org/mage/card/arcane/RetroCardRenderer.java create mode 100644 Mage.Client/src/main/resources/cardrender/background_texture_artifact_retro.png create mode 100644 Mage.Client/src/main/resources/cardrender/background_texture_black_retro.png create mode 100644 Mage.Client/src/main/resources/cardrender/background_texture_blue_retro.png create mode 100644 Mage.Client/src/main/resources/cardrender/background_texture_colorless_retro.png create mode 100644 Mage.Client/src/main/resources/cardrender/background_texture_gold_retro.png create mode 100644 Mage.Client/src/main/resources/cardrender/background_texture_green_retro.png create mode 100644 Mage.Client/src/main/resources/cardrender/background_texture_land_retro.png create mode 100644 Mage.Client/src/main/resources/cardrender/background_texture_red_retro.png create mode 100644 Mage.Client/src/main/resources/cardrender/background_texture_white_retro.png create mode 100644 Mage.Sets/src/mage/sets/EternalWeekend.java create mode 100644 Mage.Sets/src/mage/sets/MagicFest2025.java create mode 100644 Mage.Sets/src/mage/sets/ModernHorizons2Timeshifts.java create mode 100644 Mage.Sets/src/mage/sets/The30thAnniversaryMiscPromos.java create mode 100644 Mage.Sets/src/mage/sets/The30thAnniversaryPlayPromos.java diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 1a0b396cc53..4e8581be71f 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -90,6 +90,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_CARD_IMAGES_PREF_LANGUAGE = "cardImagesPreferredImageLaguage"; public static final String KEY_CARD_RENDERING_IMAGE_MODE = "cardRenderingFallback"; + public static final String KEY_CARD_RENDERING_ENABLE_RETRO_FRAMES = "cardRenderingRetroFrames"; public static final String KEY_CARD_RENDERING_ICONS_FOR_ABILITIES = "cardRenderingIconsForAbilities"; public static final String KEY_CARD_RENDERING_ICONS_FOR_PLAYABLE = "cardRenderingIconsForPlayable"; public static final String KEY_CARD_RENDERING_REMINDER_TEXT = "cardRenderingReminderText"; @@ -962,6 +963,7 @@ public class PreferencesDialog extends javax.swing.JDialog { labelPreferredImageLanguage = new javax.swing.JLabel(); panelCardStyles = new javax.swing.JPanel(); cbCardRenderImageFallback = new javax.swing.JCheckBox(); + cbCardRenderRetroFrames = new javax.swing.JCheckBox(); cbCardRenderIconsForAbilities = new javax.swing.JCheckBox(); cbCardRenderIconsForPlayable = new javax.swing.JCheckBox(); jSeparator1 = new javax.swing.JSeparator(); @@ -2328,6 +2330,9 @@ public class PreferencesDialog extends javax.swing.JDialog { cbCardRenderImageFallback.setText("Render mode: MTGO style (off) or IMAGE style (on)"); panelCardStyles.add(cbCardRenderImageFallback); + cbCardRenderRetroFrames.setText("Force retro frames (MTGO render mode will use old border for all cards)"); + panelCardStyles.add(cbCardRenderRetroFrames); + cbCardRenderIconsForAbilities.setText("Enable card icons for abilities (example: flying, deathtouch)"); panelCardStyles.add(cbCardRenderIconsForAbilities); @@ -3034,6 +3039,7 @@ public class PreferencesDialog extends javax.swing.JDialog { // rendering save(prefs, dialog.cbCardRenderImageFallback, KEY_CARD_RENDERING_IMAGE_MODE, "true", "false"); + save(prefs, dialog.cbCardRenderRetroFrames, KEY_CARD_RENDERING_ENABLE_RETRO_FRAMES, "true", "false"); save(prefs, dialog.cbCardRenderIconsForAbilities, KEY_CARD_RENDERING_ICONS_FOR_ABILITIES, "true", "false"); save(prefs, dialog.cbCardRenderIconsForPlayable, KEY_CARD_RENDERING_ICONS_FOR_PLAYABLE, "true", "false"); save(prefs, dialog.cbCardRenderHideSetSymbol, KEY_CARD_RENDERING_SET_SYMBOL, "true", "false"); @@ -3485,6 +3491,7 @@ public class PreferencesDialog extends javax.swing.JDialog { // rendering settings load(prefs, dialog.cbCardRenderImageFallback, KEY_CARD_RENDERING_IMAGE_MODE, "true", "false"); + load(prefs, dialog.cbCardRenderRetroFrames, KEY_CARD_RENDERING_ENABLE_RETRO_FRAMES, "true", "false"); load(prefs, dialog.cbCardRenderIconsForAbilities, KEY_CARD_RENDERING_ICONS_FOR_ABILITIES, "true", "true"); load(prefs, dialog.cbCardRenderIconsForPlayable, KEY_CARD_RENDERING_ICONS_FOR_PLAYABLE, "true", "true"); load(prefs, dialog.cbCardRenderHideSetSymbol, KEY_CARD_RENDERING_SET_SYMBOL, "true"); @@ -3815,6 +3822,10 @@ public class PreferencesDialog extends javax.swing.JDialog { } } + public static boolean getRenderRetroFrames() { + return (getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_ENABLE_RETRO_FRAMES, "true").endsWith("true")); + } + public static boolean getRenderIconsForAbilities() { return (getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_ICONS_FOR_ABILITIES, "true").equals("true")); } @@ -4043,6 +4054,7 @@ public class PreferencesDialog extends javax.swing.JDialog { private javax.swing.JCheckBox cbCardRenderIconsForAbilities; private javax.swing.JCheckBox cbCardRenderIconsForPlayable; private javax.swing.JCheckBox cbCardRenderImageFallback; + private javax.swing.JCheckBox cbCardRenderRetroFrames; private javax.swing.JCheckBox cbCardRenderShowAbilityTextOverlay; private javax.swing.JCheckBox cbCardRenderShowReminderText; private javax.swing.JCheckBox cbConfirmEmptyManaPool; diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java index 3d3f9c2e811..4be35b5e155 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java @@ -1,5 +1,7 @@ package org.mage.card.arcane; +import mage.cards.FrameStyle; +import mage.client.dialog.PreferencesDialog; import mage.view.CardView; /** @@ -13,6 +15,8 @@ public class CardRendererFactory { public CardRenderer create(CardView card) { if (card.isSplitCard()) { return new ModernSplitCardRenderer(card); + } else if (card.getFrameStyle().equals(FrameStyle.RETRO) || card.getFrameStyle().equals(FrameStyle.LEA_ORIGINAL_DUAL_LAND_ART_BASIC) || PreferencesDialog.getRenderRetroFrames()) { + return new RetroCardRenderer(card); } else { return new ModernCardRenderer(card); } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index f188a9fbd61..f42230814c0 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -318,7 +318,8 @@ public class ModernCardRenderer extends CardRenderer { } else if (isUnstableFullArtLand()) { rect = new Rectangle2D.Float(.0f, .0f, 1.0f, 1.0f); } else if (cardView.getArtRect() == ArtRect.FULL_LENGTH_LEFT || - cardView.getArtRect() == ArtRect.FULL_LENGTH_RIGHT) { + cardView.getArtRect() == ArtRect.FULL_LENGTH_RIGHT || + cardView.getArtRect() == ArtRect.RETRO) { rect = cardView.getArtRect().rect; } else if (cardView.getFrameStyle().isFullArt() || (cardView.isToken())) { rect = new Rectangle2D.Float(.079f, .11f, .84f, .63f); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/RetroCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/RetroCardRenderer.java new file mode 100644 index 00000000000..961b6a43ce7 --- /dev/null +++ b/Mage.Client/src/main/java/org/mage/card/arcane/RetroCardRenderer.java @@ -0,0 +1,1287 @@ +package org.mage.card.arcane; + +import mage.MageInt; +import mage.ObjectColor; +import mage.cards.ArtRect; +import mage.cards.FrameStyle; +import mage.client.dialog.PreferencesDialog; +import mage.constants.CardType; +import mage.constants.MageObjectType; +import mage.constants.SubType; +import mage.view.CardView; +import mage.view.PermanentView; + +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.awt.image.BufferedImage; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.text.CharacterIterator; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.mage.card.arcane.ModernCardResourceLoader.*; + +/** + * @author stravant@gmail.com, JayDi85, Jmlundeen + *

+ * Base rendering class for old border cards + */ +public class RetroCardRenderer extends CardRenderer { + + public static final Color MANA_ICONS_TEXT_COLOR = Color.DARK_GRAY; // text color of missing mana icons in IMAGE render mode + + public static final BufferedImage BG_IMG_WHITE = loadBackgroundImage("white_retro"); + public static final BufferedImage BG_IMG_BLUE = loadBackgroundImage("blue_retro"); + public static final BufferedImage BG_IMG_BLACK = loadBackgroundImage("black_retro"); + public static final BufferedImage BG_IMG_RED = loadBackgroundImage("red_retro"); + public static final BufferedImage BG_IMG_GREEN = loadBackgroundImage("green_retro"); + public static final BufferedImage BG_IMG_GOLD = loadBackgroundImage("gold_retro"); + public static final BufferedImage BG_IMG_ARTIFACT = loadBackgroundImage("artifact_retro"); + public static final BufferedImage BG_IMG_LAND = loadBackgroundImage("land_retro"); + public static final BufferedImage BG_IMG_COLORLESS = loadBackgroundImage("colorless_retro"); + + + public static final Color BORDER_WHITE = new Color(168, 159, 156); + public static final Color BORDER_BLUE = new Color(29, 107, 124); + public static final Color BORDER_BLACK = new Color(77, 71, 73); + public static final Color BORDER_RED = new Color(200, 71, 58); + public static final Color BORDER_GREEN = new Color(91, 136, 62, 255); + public static final Color BORDER_GOLD = new Color(244, 172, 65); + public static final Color BORDER_COLORLESS = new Color(208, 212, 212); + public static final Color BORDER_LAND = new Color(190, 173, 115); + + public static final Color BOX_WHITE = new Color(248, 247, 245); + public static final Color BOX_BLUE = new Color(200, 226, 235); + public static final Color BOX_BLACK = new Color(230, 191, 134); + public static final Color BOX_RED = new Color(203, 141, 117); + public static final Color BOX_GREEN = new Color(234, 187, 134); + public static final Color BOX_GOLD = new Color(154, 142, 145); + public static final Color BOX_COLORLESS = new Color(190, 183, 178); + public static final Color BOX_LAND = new Color(220, 215, 213); + public static final Color BOX_INVENTION = new Color(209, 97, 33); + public static final Color BOX_VEHICLE = new Color(155, 105, 60); + + public static final Color LAND_TEXTBOX_WHITE = new Color(248, 232, 188, 234); + public static final Color LAND_TEXTBOX_BLUE = new Color(189, 212, 236, 234); + public static final Color LAND_TEXTBOX_BLACK = new Color(174, 164, 162, 234); + public static final Color LAND_TEXTBOX_RED = new Color(242, 168, 133, 234); + public static final Color LAND_TEXTBOX_GREEN = new Color(198, 220, 198, 234); + public static final Color LAND_TEXTBOX_GOLD = new Color(236, 229, 207, 234); + + public static final Color LAND_SPIRAL_TEXTBOX_WHITE = new Color(248, 232, 188, 220); + public static final Color LAND_SPIRAL_TEXTBOX_BLUE = new Color(189, 212, 236, 220); + public static final Color LAND_SPIRAL_TEXTBOX_BLACK = new Color(174, 164, 162, 220); + public static final Color LAND_SPIRAL_TEXTBOX_RED = new Color(242, 168, 133, 220); + public static final Color LAND_SPIRAL_TEXTBOX_GREEN = new Color(198, 220, 198, 220); + + public static final Color TEXTBOX_WHITE = new Color(248, 247, 245); + public static final Color TEXTBOX_BLUE = new Color(200, 226, 235); + public static final Color TEXTBOX_BLACK = new Color(230, 191, 134); + public static final Color TEXTBOX_RED = new Color(203, 141, 117); + public static final Color TEXTBOX_GREEN = new Color(234, 187, 134); + public static final Color TEXTBOX_GOLD = new Color(154, 142, 145); + public static final Color TEXTBOX_COLORLESS = new Color(190, 183, 178); + public static final Color TEXTBOX_LAND = new Color(211, 151, 92, 255); + + public static final Color ERROR_COLOR = new Color(255, 0, 255); + + /////////////////////////////////////////////////////////////////////////// + // Layout metrics for modern border cards + // How far the main box, art, and name / type line are inset from the + // card border. That is, the width of background texture that shows around + // the edge of the card. + protected int contentInset; + + // Helper: The total inset from card edge to rules box etc. + // = borderWidth + contentInset + protected int totalContentInset; + + protected int frameInset; + + // Width of the content region of the card + // = cardWidth - 2 x totalContentInset + protected int contentWidth; + + // Width of art / text box + protected int innerContentWidth; + // X position of inside content + private int innerContentStart; + + + // How tall the name / type lines and P/T box are + protected static final float BOX_HEIGHT_FRAC = 0.065f; // x cardHeight + protected static final int BOX_HEIGHT_MIN = 16; + protected int boxHeight; + + // How far down the card is the type line placed? + protected static final float TYPE_LINE_Y_FRAC = 0.52f; // x cardHeight + protected int typeLineY; + + // Possible sizes of rules text font + protected static final int[] RULES_TEXT_FONT_SIZES = {24, 18, 15, 12, 9}; + + // How large is the box text, and how far is it down the boxes + protected int boxTextHeight; + protected int boxTextOffset; + protected Font boxTextFont; + protected Font boxTextFontNarrow; + + // How large is the P/T text, and how far is it down the boxes + protected int ptTextHeight; + protected int ptTextOffset; + protected Font ptTextFont; + + // Processed mana cost string + protected String manaCostString; + + // Inset frame colors + protected Color frameTopRightColor; + protected Color frameBottomLeftColor; + + public RetroCardRenderer(CardView card) { + // Pass off to parent + super(card); + + // Mana cost string + manaCostString = ManaSymbols.getClearManaCost(cardView.getManaCostStr()); + } + + @Override + protected void layout(int cardWidth, int cardHeight) { + // Pass to parent + super.layout(cardWidth, cardHeight); + + borderWidth = (int) Math.max( + BORDER_WIDTH_MIN, + 0.042 * cardWidth); + + frameInset = (int) Math.max( + BORDER_WIDTH_MIN, + 0.012 * cardWidth); + + // Content inset, just equal to border width + contentInset = borderWidth - frameInset; + + // Total content inset helper + totalContentInset = borderWidth + contentInset; + + // Content width + contentWidth = cardWidth - 2 * totalContentInset; + + // Box height + boxHeight = (int) Math.max( + BOX_HEIGHT_MIN, + BOX_HEIGHT_FRAC * cardHeight); + + // Art / text box size + innerContentWidth = (int) (cardWidth * 0.81f); + innerContentStart = (int) (cardWidth * 0.095f); + + // Type line at + typeLineY = (int) (TYPE_LINE_Y_FRAC * cardHeight); + + // Box text height + boxTextHeight = getTextHeightForBoxHeight(boxHeight); + boxTextOffset = (boxHeight - boxTextHeight) / 2; + boxTextFont = new Font("Arial", Font.PLAIN, boxTextHeight); + boxTextFontNarrow = new Font("Arial Narrow", Font.PLAIN, boxTextHeight); + + // Box text height + ptTextHeight = getPTTextHeightForLineHeight(boxHeight); + ptTextOffset = (boxHeight - ptTextHeight) / 2; + ptTextFont = new Font("Arial", Font.BOLD, ptTextHeight); + + // Inset Frame Colors + frameTopRightColor = getFrameColor(true); + frameBottomLeftColor = getFrameColor(false); + } + + @Override + protected void drawBorder(Graphics2D g) { + // Selection Borders + Color borderColor; + if (isSelected) { + borderColor = Color.green; + } else if (isChoosable) { + borderColor = new Color(250, 250, 0, 230); + } else if (cardView.isPlayable()) { + borderColor = new Color(153, 102, 204, 200); + } else if (cardView.isCanAttack()) { + borderColor = new Color(255, 50, 50, 230); + } else if (cardView.isCanBlock()) { + borderColor = new Color(255, 50, 50, 230); + } else { + borderColor = Color.BLACK; + } + + // Draw border as one rounded rectangle + g.setColor(borderColor); + g.fillRoundRect(0, 0, cardWidth, cardHeight, cornerRadius, cornerRadius); + } + + @Override + protected void drawBackground(Graphics2D g) { + if (cardView.isFaceDown()) { + // just draw a brown rectangle + drawCardBackTexture(g); + } else { + BufferedImage bg = getBackgroundTexture(cardView.getColor(), cardView.getCardTypes()); + if (bg == null) { + return; + } + int bgw = bg.getWidth(); + int bgh = bg.getHeight(); + + Rectangle bgRect = new Rectangle(borderWidth , borderWidth, + cardWidth - borderWidth * 2, cardHeight - borderWidth * 2); + Area area = new Area(bgRect); + g.setClip(area); + g.drawImage(bg, 0, 0, cardWidth, cardHeight, 0, 0, bgw, bgh, BOX_BLUE, null); + } + } + + private boolean isOriginalDualLand() { + return cardView.getFrameStyle() == FrameStyle.LEA_ORIGINAL_DUAL_LAND_ART_BASIC; + } + + @Override + protected void drawArt(Graphics2D g) { + if (artImage != null) { + + boolean shouldPreserveAspect = false; + Rectangle2D sourceRect = ArtRect.RETRO.rect; + if (cardView.getFrameStyle() != FrameStyle.RETRO) { + sourceRect = new Rectangle2D.Double(sourceRect.getX(), sourceRect.getY() + .01, sourceRect.getWidth(), sourceRect.getHeight()); + } + + if (cardView.getMageObjectType() == MageObjectType.SPELL) { + ArtRect rect = cardView.getArtRect(); + if (rect != ArtRect.NORMAL) { + sourceRect = rect.rect; + } + } + + // Normal drawing of art from a source part of the card frame into the rect + drawArtIntoRect(g, + innerContentStart + frameInset, innerContentStart + frameInset * 2, + innerContentWidth - frameInset * 2, typeLineY - borderWidth * 2 - frameInset, + sourceRect, shouldPreserveAspect); + + } + } + + @Override + protected void drawFrame(Graphics2D g, CardPanelAttributes attribs, BufferedImage image, boolean lessOpaqueRulesTextBox) { + // Get the card colors to base the frame on + ObjectColor frameColors = getFrameObjectColor(); + + // Get the border paint + Color boxColor = getBoxColor(frameColors, cardView.getCardTypes()); + Paint textboxPaint = getTextboxPaint(frameColors, cardView.getCardTypes(), cardWidth, lessOpaqueRulesTextBox); + Paint borderPaint = getBorderPaint(frameColors, cardView.getCardTypes(), cardWidth); + + // Special colors + if (cardView.getFrameStyle() == FrameStyle.KLD_INVENTION) { + boxColor = BOX_INVENTION; + } + + // Draw the textbox fill + drawTextboxBackground(g, textboxPaint, frameColors, borderPaint, isOriginalDualLand()); + + drawInsetFrame(g, innerContentStart, innerContentStart + frameInset, + innerContentWidth, typeLineY - borderWidth * 2 + frameInset, borderPaint, cardView.getCardTypes().contains(CardType.LAND)); + + drawTypeLine(g, attribs, getCardTypeLine(), + innerContentStart, typeLineY + frameInset, + innerContentWidth, boxHeight + frameInset); + + // Draw the transform circle + int nameOffset = drawTransformationCircle(g, attribs, borderPaint); + + // Draw the name line + drawNameLine(g, attribs, cardView.getDisplayName(), manaCostString, + innerContentStart + nameOffset, totalContentInset / 2 - frameInset, + contentWidth - nameOffset - borderWidth); + + // Draw the textbox rules + drawRulesText(g, textboxKeywords, textboxRules, + innerContentStart + 2, typeLineY + boxHeight + 2, + innerContentWidth - 4, (int) ((cardHeight - borderWidth * 2) * 0.32f)); + + // Draw the bottom right stuff + drawBottomRight(g, borderPaint, boxColor); + } + + private void drawInsetFrame(Graphics2D g2, int x, int y, int width, int height, Paint borderPaint, boolean isLand) { + + // Outer and inner bounds + int x1 = x + width; + int y1 = y + height; + + int xi0 = x + frameInset; + int yi0 = y + frameInset; + int xi1 = x + width - frameInset; + int yi1 = y + height - frameInset; + + // Colors for visual effect (you can customize this) + Color topColor = frameTopRightColor; + Color leftColor = frameBottomLeftColor; + + // Top trapezoid + Path2D top = new Path2D.Double(); + top.moveTo(x, y); + top.lineTo(x1, y); + top.lineTo(xi1, yi0); + top.lineTo(xi0, yi0); + top.closePath(); + + // Left trapezoid + Path2D left = new Path2D.Double(); + left.moveTo(x, y); + left.lineTo(xi0, yi0); + left.lineTo(xi0, yi1); + left.lineTo(x, y1); + left.closePath(); + + // Right trapezoid + Path2D right = new Path2D.Double(); + right.moveTo(x1, y); + right.lineTo(x1, y1); + right.lineTo(xi1, yi1); + right.lineTo(xi1, yi0); + right.closePath(); + + // Bottom trapezoid + Path2D bottom = new Path2D.Double(); + bottom.moveTo(x, y1); + bottom.lineTo(x1, y1); + bottom.lineTo(xi1, yi1); + bottom.lineTo(xi0, yi1); + bottom.closePath(); + + if (isLand) { + g2.draw(top); + g2.draw(left); + g2.draw(right); + g2.draw(bottom); + if (cardView.getFrameColor().getColorCount() > 1) { + g2.setColor(BORDER_LAND); + } else { + g2.setPaint(borderPaint); + } + } else { + g2.setColor(topColor); + g2.fill(top); + g2.fill(right); + g2.setColor(leftColor); + g2.fill(left); + g2.fill(bottom); + g2.setColor(CardRendererUtils.abitdarker(topColor)); + } + x = x - 1; + y = y - 1; + g2.setStroke(new BasicStroke(1.5f)); + g2.drawRect(x, y, x1 - x, y1 - y); + g2.setStroke(new BasicStroke(1)); + } + + private void drawTextboxBackground(Graphics2D g, Paint textboxPaint, ObjectColor frameColors, Paint borderPaint, boolean isOriginalDual) { + g.setPaint(textboxPaint); + int x = innerContentStart; + int backgroundHeight = (int) ((cardHeight - borderWidth * 2) * 0.33f); + if (cardView.getCardTypes().contains(CardType.LAND)) { + + // Analysis of LEA Duals (Scrubland) gives 16.5 height of unit of 'spirals' in the text area + int height_of_spiral = (int) ((backgroundHeight - frameInset * 2) * 0.06); + + List twoColors = frameColors.getColors(); + g.setPaint(borderPaint); + g.fillRect(x, typeLineY + boxHeight + 1, innerContentWidth, backgroundHeight); + g.setColor(Color.black); + g.drawRect(x, typeLineY + boxHeight + 1, innerContentWidth, backgroundHeight); + g.setPaint(textboxPaint); + + if (twoColors.size() <= 2) { + if (isOriginalDual && twoColors.size() == 2) { + g.setPaint(getSpiralLandTextboxColor(twoColors.get(0), twoColors.get(1), false)); + } + g.fillRect(x + frameInset, typeLineY + boxHeight + 1 + frameInset, innerContentWidth - frameInset * 2, backgroundHeight - frameInset * 2); + g.setColor(Color.black); + g.drawRect(x + frameInset, typeLineY + boxHeight + 1 + frameInset, innerContentWidth - frameInset * 2, backgroundHeight - frameInset * 2); + + } + if (frameColors.getColorCount() >= 3) { + g.fillRect(x, typeLineY + boxHeight + 1, innerContentWidth - 4, backgroundHeight); + } + if (frameColors.getColorCount() == 2) { + if (isOriginalDual) { + for (int i = 0; i < 8; i++) { + int offset = height_of_spiral * i; + int inset = frameInset + offset; + int width = innerContentWidth - frameInset * 2 - height_of_spiral * i * 2; + int height = backgroundHeight - frameInset * 2 - height_of_spiral * i * 2; + + boolean useFirstColor = (i % 2 == 0); + g.setPaint(getSpiralLandTextboxColor(twoColors.get(0), twoColors.get(1), useFirstColor)); + g.fillRect( + x + inset, + typeLineY + boxHeight + 1 + inset, + width, + height + ); + } + } + } + } else { + g.fillRect( + x, typeLineY + boxHeight, + innerContentWidth, backgroundHeight); + g.setColor(Color.black); + g.drawRect(x, typeLineY + boxHeight, + innerContentWidth, backgroundHeight); + } + } + + // Draw the name line + protected void drawNameLine(Graphics2D g, CardPanelAttributes attribs, String baseName, String manaCost, int x, int y, int w) { + // Width of the mana symbols + int manaCostWidth; + if (cardView.isAbility()) { + manaCostWidth = 0; + } else { + manaCostWidth = CardRendererUtils.getManaCostWidth(manaCost, boxTextHeight); + } + + // Available width for name. Add a little bit of slop so that one character + // can partially go underneath the mana cost + int availableWidth = w - manaCostWidth + 2; + + // Draw the name + if (!baseName.isEmpty()) { + AttributedString str = new AttributedString(baseName); + str.addAttribute(TextAttribute.FONT, boxTextFont); + TextMeasurer measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext()); + int breakIndex = measure.getLineBreakIndex(0, availableWidth); + if (breakIndex < baseName.length()) { + str = new AttributedString(baseName); + str.addAttribute(TextAttribute.FONT, boxTextFontNarrow); + measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext()); + breakIndex = measure.getLineBreakIndex(0, availableWidth); + } + if (breakIndex > 0) { + TextLayout layout = measure.getLayout(0, breakIndex); + int drawY = y + boxTextOffset + boxTextHeight - 1; + + // Draw main text + g.setColor(getBoxTextColor(attribs)); + layout.draw(g, x, drawY); + } + } + + // Draw the mana symbols + if (!cardView.isAbility() && !cardView.isFaceDown()) { + ManaSymbols.draw(g, manaCost, x + w - manaCostWidth, y + boxTextOffset, boxTextHeight, RetroCardRenderer.MANA_ICONS_TEXT_COLOR, 2); + } + } + + // Draw the type line (color indicator, types, and expansion symbol) + protected void drawTypeLine(Graphics2D g, CardPanelAttributes attribs, String baseTypeLine, int x, int y, int w, int h) { + // Draw expansion symbol + int expansionSymbolWidth = 0; + if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_SET_SYMBOL, "false").equals("false")) { + expansionSymbolWidth = drawExpansionSymbol(g, x, y, w, h); + } + + // Draw type line text + int availableWidth = w - expansionSymbolWidth + 1; + String types = baseTypeLine; + g.setFont(boxTextFont); + + // Replace "Legendary" in type line if there's not enough space + if (g.getFontMetrics().stringWidth(types) > availableWidth) { + types = types.replace("Token", "T."); + types = types.replace("Legendary", "L."); + } + + if (!types.isEmpty()) { + AttributedString str = new AttributedString(types); + str.addAttribute(TextAttribute.FONT, boxTextFont); + TextMeasurer measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext()); + int breakIndex = measure.getLineBreakIndex(0, availableWidth); + if (breakIndex < types.length()) { + str = new AttributedString(types); + str.addAttribute(TextAttribute.FONT, boxTextFontNarrow); + measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext()); + breakIndex = measure.getLineBreakIndex(0, availableWidth); + } + if (breakIndex > 0) { + TextLayout layout = measure.getLayout(0, breakIndex); + g.setColor(getBoxTextColor(attribs)); + layout.draw(g, x, y + (float) (h - boxTextHeight) / 2 + boxTextHeight - 1); + } + } + } + + // Draw the P/T and/or Loyalty boxes + protected void drawBottomRight(Graphics2D g, Paint borderPaint, Color fill) { + // No bottom right for abilities + if (cardView.isAbility()) { + return; + } + + // Where to start drawing the things + int curY = cardHeight - (int) (0.03f * cardHeight); + + // Width of the boxes + int partBoxWidth = (int) Math.max(30, 0.20f * cardWidth); + + // Is it a creature? + boolean isVehicle = cardView.getSubTypes().contains(SubType.VEHICLE); + if (cardView.showPT()) { + + // draws p/t by parts + int ptDeviderSpace = 1; // Arial font is too narrow for devider (2/2) and needs extra space + String ptText1 = cardView.getPower(); + String ptText2 = "/"; + String ptText3 = CardRendererUtils.getCardLifeWithDamage(cardView); + int ptTextWidth1 = g.getFontMetrics(ptTextFont).stringWidth(ptText1); + int ptTextWidth2 = g.getFontMetrics(ptTextFont).stringWidth(ptText2) + 2 * ptDeviderSpace; + int ptTextWidth3 = g.getFontMetrics(ptTextFont).stringWidth(ptText3); + + // PT max size + int ptContentWidth = contentInset + ptTextWidth1 + ptDeviderSpace + ptTextWidth2 + ptDeviderSpace + ptTextWidth3 + contentInset; + partBoxWidth = Math.max(ptContentWidth, partBoxWidth); + + int x = cardWidth - borderWidth - partBoxWidth; + + // Draw PT box + CardRendererUtils.drawRoundedBox(g, + x, curY - boxHeight, + partBoxWidth, boxHeight, + contentInset, + borderPaint, + isVehicle ? BOX_VEHICLE : fill); + + // Draw text + Color defaultTextColor = Color.black; + boolean defaultTextLight = true; + g.setFont(ptTextFont); + + // real PT info + MageInt currentPower = cardView.getOriginalPower(); + MageInt currentToughness = cardView.getOriginalToughness(); + + // draws + int ptEmptySpace = (partBoxWidth - ptContentWidth) / 2; + int ptPosStart1 = x + contentInset + ptEmptySpace; + int ptPosStart2 = ptPosStart1 + ptTextWidth1 + ptDeviderSpace; + int ptPosStart3 = ptPosStart2 + ptTextWidth2 + ptDeviderSpace; + // p + g.setColor(CardRendererUtils.getCardTextColor(currentPower, false, defaultTextColor, defaultTextLight)); + g.drawString(ptText1, ptPosStart1, curY - ptTextOffset - 1); // left + // / + g.setColor(defaultTextColor); + g.drawString(ptText2, ptPosStart2, curY - ptTextOffset - 1); // center + // t + g.setColor(CardRendererUtils.getCardTextColor(currentToughness, CardRendererUtils.isCardWithDamage(cardView), defaultTextColor, defaultTextLight)); + g.drawString(ptText3, ptPosStart3, curY - ptTextOffset - 1); // right + // + g.setColor(defaultTextColor); + + // Advance + curY -= boxHeight; + } + + // Is it a walker? (But don't draw the box if it's a non-permanent view + // of a walker without a starting loyalty (EG: Arlin Kord's flipped side). + if (cardView.isPlaneswalker() + && (cardView instanceof PermanentView || !cardView.getStartingLoyalty().equals("0"))) { + // Draw the PW loyalty box + int w = partBoxWidth; + int h = partBoxWidth / 2; + int x = cardWidth - partBoxWidth - borderWidth; + int y = curY - h; + + Polygon symbol = new Polygon(); + symbol.addPoint(x + w / 2, y + h); + symbol.addPoint((int) (x + w * 0.9), (int) (y + 0.8 * h)); + symbol.addPoint(x + w, y); + symbol.addPoint((int) (x + w * 0.6), (int) (y - 0.2 * h)); + symbol.addPoint(x + w / 2, y); + symbol.addPoint((int) (x + w * 0.4), (int) (y - 0.2 * h)); + symbol.addPoint(x, y); + symbol.addPoint((int) (x + w * 0.1), (int) (y + 0.8 * h)); + + // Draw + stroke + g.setColor(Color.black); + g.fillPolygon(symbol); + g.setColor(new Color(200, 200, 200)); + g.setStroke(new BasicStroke(2)); + g.drawPolygon(symbol); + g.setStroke(new BasicStroke(1)); + + // Loyalty number + String loyalty; + if (cardView instanceof PermanentView) { + loyalty = cardView.getLoyalty(); + } else { + loyalty = cardView.getStartingLoyalty(); + } + + g.setFont(ptTextFont); + g.setColor(Color.white); + int loyaltyWidth = g.getFontMetrics().stringWidth(loyalty); + g.drawString(loyalty, x + (w - loyaltyWidth) / 2, y + ptTextHeight + (h - ptTextHeight) / 2); + + // Advance + curY -= (int) (1.2 * y); + } + + // Is it a battle? + if (cardView.isBattle() + && (cardView instanceof PermanentView || !cardView.getStartingDefense().equals("0"))) { + // Draw the PW loyalty box + int w = 3 * partBoxWidth / 4; + int h = 3 * partBoxWidth / 4; + int x = cardWidth - w - borderWidth; + int y = curY - h; + + Polygon symbol = new Polygon(); + symbol.addPoint(x + (0 * w) / 80, y + (2 * h) / 80); + symbol.addPoint(x + (12 * w) / 80, y + (30 * h) / 80); + symbol.addPoint(x + (3 * w) / 80, y + (40 * h) / 80); + symbol.addPoint(x + (12 * w) / 80, y + (50 * h) / 80); + symbol.addPoint(x + (0 * w) / 80, y + (78 * h) / 80); + symbol.addPoint(x + (30 * w) / 80, y + (71 * h) / 80); + symbol.addPoint(x + (40 * w) / 80, y + (80 * h) / 80); + symbol.addPoint(x + (50 * w) / 80, y + (71 * h) / 80); + symbol.addPoint(x + (80 * w) / 80, y + (78 * h) / 80); + symbol.addPoint(x + (68 * w) / 80, y + (50 * h) / 80); + symbol.addPoint(x + (77 * w) / 80, y + (40 * h) / 80); + symbol.addPoint(x + (68 * w) / 80, y + (30 * h) / 80); + symbol.addPoint(x + (80 * w) / 80, y + (2 * h) / 80); + symbol.addPoint(x + (48 * w) / 80, y + (9 * h) / 80); + symbol.addPoint(x + (40 * w) / 80, y + (0 * h) / 80); + symbol.addPoint(x + (32 * w) / 80, y + (9 * h) / 80); + + + // Draw + stroke + g.setColor(Color.black); + g.fillPolygon(symbol); + g.setColor(new Color(200, 200, 200)); + g.setStroke(new BasicStroke(2)); + g.drawPolygon(symbol); + g.setStroke(new BasicStroke(1)); + + // Loyalty number + String defense; + if (cardView instanceof PermanentView) { + defense = cardView.getDefense(); + } else { + defense = cardView.getStartingDefense(); + } + + g.setFont(ptTextFont); + g.setColor(Color.white); + int defenseWidth = g.getFontMetrics().stringWidth(defense); + g.drawString(defense, x + 1 + (w - defenseWidth) / 2, y - 1 + ptTextHeight + (h - ptTextHeight) / 2); + + // Advance + curY -= (int) (1.2 * y); + } + + // does it have damage on it? + if ((cardView instanceof PermanentView) && ((PermanentView) cardView).getDamage() > 0) { + int x = cardWidth - partBoxWidth - borderWidth; + int y = curY - boxHeight; + String damage = String.valueOf(((PermanentView) cardView).getDamage()); + g.setFont(ptTextFont); + int txWidth = g.getFontMetrics().stringWidth(damage); + g.setColor(Color.red); + g.fillRect(x, y, partBoxWidth, boxHeight); + g.setColor(Color.white); + g.drawRect(x, y, partBoxWidth, boxHeight); + g.drawString(damage, x + (partBoxWidth - txWidth) / 2, curY - 1); + } + } + + // Draw the card's textbox in a given rect + protected boolean loyaltyAbilityColorToggle = false; + + private static class RuleLayout { + + public List attributedRules; + public int remainingHeight; + public boolean fits; + public Font font; + public Font fontItalic; + } + + /** + * Figure out if a given text size will work for laying out the rules in a + * card textbox + */ + protected RuleLayout layoutRules(Graphics2D g, List rules, int w, int h, int fontSize) { + // The fonts to try + Font font = new Font("Arial", Font.PLAIN, fontSize); + Font fontItalic = new Font("Arial", Font.ITALIC, fontSize); + + // Get the total height of the rules + List attributedRules = new ArrayList<>(); + boolean fits = true; + int remaining = h; + for (TextboxRule rule : rules) { + AttributedString attributed = rule.generateAttributedString(font, fontItalic); + attributedRules.add(attributed); + remaining -= drawSingleRule(g, attributed, rule, 0, 0, w, remaining, /*doDraw=*/ false); + if (remaining < 0) { + fits = false; + break; + } + } + + // Return the information + RuleLayout layout = new RuleLayout(); + layout.attributedRules = attributedRules; + layout.remainingHeight = remaining; + layout.fits = fits; + layout.font = font; + layout.fontItalic = fontItalic; + return layout; + } + + protected void drawRulesText(Graphics2D g, List keywords, List rules, int x, int y, int w, int h) { + // Gather all rules to render + List allRules = new ArrayList<>(rules); + + // Lands have an inset frame and need to adjust their x and y + if (cardView.getCardTypes().contains(CardType.LAND)) { + x = x + frameInset; + y = y + frameInset; + h = h - frameInset; + w = w - frameInset; + } + + // Add the keyword rule if there are any keywords + if (!keywords.isEmpty()) { + String keywordRulesString = getKeywordRulesString(keywords); + TextboxRule keywordsRule = new TextboxRule(keywordRulesString, new ArrayList<>()); + allRules.add(0, keywordsRule); + } + + // Basic mana draw mana symbol in textbox (for basic lands) + if ((allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand())) { + drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); + return; + } + + // Go through possible font sizes in descending order to find the best fit + RuleLayout bestLayout = null; + for (int fontSize : RULES_TEXT_FONT_SIZES) { + bestLayout = layoutRules(g, allRules, w, h, fontSize); + + // Stop, we found a good fit + if (bestLayout.fits) { + break; + } + } + + // Nothing to draw + if (bestLayout == null) { + return; + } + + // Do we have room for additional padding between the parts of text? + // If so, calculate the padding based on how much space was left over + int padding; + if (bestLayout.fits) { + padding = (int) (((float) bestLayout.remainingHeight) / (1 + allRules.size())); + } else { + // When the text doesn't fit to begin with there's no room for padding + padding = 0; + } + + // Do the actual draw + loyaltyAbilityColorToggle = false; + g.setColor(Color.black); + int curY = y + padding; + for (int i = 0; i < bestLayout.attributedRules.size(); ++i) { + AttributedString attributedRule = bestLayout.attributedRules.get(i); + TextboxRule rule = allRules.get(i); + int adv = drawSingleRule(g, attributedRule, rule, x, curY, w, h, true); + curY += adv + padding; + h -= adv; + if (h < 0) { + break; + } + } + } + + // Draw a basic mana symbol + private void drawBasicManaTextbox(Graphics2D g, int x, int y, int w, int h, String symbol) { + String symbs = symbol; + int symbHeight = (int) (0.8 * h); + int manaCostWidth = CardRendererUtils.getManaCostWidth(symbs, symbHeight); + ManaSymbols.draw(g, symbs, x + (w - manaCostWidth) / 2, y + (h - symbHeight) / 2, symbHeight, RetroCardRenderer.MANA_ICONS_TEXT_COLOR, 2); + } + + // Get the first line of the textbox, the keyword string + private static String getKeywordRulesString(List keywords) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < keywords.size(); ++i) { + builder.append(keywords.get(i).text); + if (i != keywords.size() - 1) { + builder.append(", "); + } + } + return builder.toString(); + } + + // Draw a single rule and returns the amount vertically advanced by, but + // only if doDraw is true. If doDraw is false, just returns the vertical + // advance if the rule were to be drawn. + private int drawSingleRule(Graphics2D g, AttributedString text, TextboxRule rule, int x, int y, int w, int h, boolean doDraw) { + // Inset, in case we are a leveler or loyalty ability + int inset = 0; + if (rule != null && rule.type == TextboxRuleType.LOYALTY) { + inset = cardWidth / 12; + } + int availWidth = w - inset; + if (availWidth < 0) { + return 0; + } + + FontRenderContext frc = g.getFontRenderContext(); + AttributedCharacterIterator textIter = text.getIterator(); + LineBreakMeasurer measure = new LineBreakMeasurer(textIter, frc); + float yPos = y; + float remain = h; + AttributedCharacterIterator newLineCheck = text.getIterator(); + while (measure.getPosition() < textIter.getEndIndex()) { + // Advance iterator to next line break + newLineCheck.setIndex(measure.getPosition()); + char ch; + while ((ch = newLineCheck.next()) != CharacterIterator.DONE) { + if (ch == '\n') { + break; + } + } + + // Get the text layout + TextLayout layout = measure.nextLayout(availWidth, newLineCheck.getIndex(), false); + float ascent = layout.getAscent(); + yPos += ascent; + remain -= ascent; + if (remain < 0) { + break; + } + if (doDraw) { + g.setColor(Color.black); + layout.draw(g, x + inset, yPos); + } + yPos += layout.getDescent() + layout.getLeading() - 2; + } + + // Advance + int advance = ((int) Math.ceil(yPos)) - y; + + // Is it a loyalty ability? + if (rule != null && rule.type == TextboxRuleType.LOYALTY) { + TextboxLoyaltyRule loyaltyRule = (TextboxLoyaltyRule) rule; + Polygon symbol; + int symbolWidth = (x + inset) - borderWidth - 4; + int symbolHeight = (int) (0.7f * symbolWidth); + if (symbolHeight > advance) { + advance = symbolHeight; + } + int symbolX = x - borderWidth - frameInset; + int symbolY = y + (advance - symbolHeight) / 2; + if (doDraw) { + if (loyaltyRule.loyaltyChange < 0 || loyaltyRule.loyaltyChange == TextboxLoyaltyRule.MINUS_X) { + symbol = new Polygon( + new int[]{ + symbolX, + symbolX + symbolWidth, + symbolX + symbolWidth, + symbolX + symbolWidth / 2, + symbolX,}, + new int[]{ + symbolY, + symbolY, + symbolY + symbolHeight - 3, + symbolY + symbolHeight + 3, + symbolY + symbolHeight - 3,}, + 5); + } else if (loyaltyRule.loyaltyChange > 0) { + symbol = new Polygon( + new int[]{ + symbolX, + symbolX + symbolWidth / 2, + symbolX + symbolWidth, + symbolX + symbolWidth, + symbolX,}, + new int[]{ + symbolY + 3, + symbolY - 3, + symbolY + 3, + symbolY + symbolHeight, + symbolY + symbolHeight,}, + 5); + } else { + symbol = new Polygon( + new int[]{ + symbolX, + symbolX + symbolWidth, + symbolX + symbolWidth, + symbolX,}, + new int[]{ + symbolY, + symbolY, + symbolY + symbolHeight, + symbolY + symbolHeight,}, + 4); + } + g.setColor(new Color(0, 0, 0, 128)); + if (y + advance + 1 <= yPos) { + g.fillRect(x + 2, y + advance + 1, w - 2, 1); + } + g.setColor(Color.black); + g.fillPolygon(symbol); + g.setColor(new Color(200, 200, 200)); + g.setStroke(new BasicStroke(2)); + g.drawPolygon(symbol); + g.setStroke(new BasicStroke(1)); + g.setColor(Color.white); + g.setFont(boxTextFont); + String loyaltyString = loyaltyRule.getChangeString(); + int textWidth = g.getFontMetrics().stringWidth(loyaltyString); + g.drawString(loyaltyString, + symbolX + (symbolWidth - textWidth) / 2, + symbolY + symbolHeight - (symbolHeight - boxTextHeight) / 2); + + advance += 3; + loyaltyAbilityColorToggle = !loyaltyAbilityColorToggle; + } + } + + return advance; + } + + protected boolean isTransformCard(CardPanelAttributes attribs) { + return cardView.canTransform() || attribs.isTransformed; + } + + protected int drawTransformationCircle(Graphics2D g, CardPanelAttributes attribs, Paint borderPaint) { + int transformCircleOffset = 0; + int y = totalContentInset / 2 + frameInset; + int height = (boxHeight - boxTextHeight) / 2 + boxTextHeight - 1; + if (isTransformCard(attribs)) { + transformCircleOffset = height - contentInset * 2; + g.setPaint(borderPaint); + g.drawOval(borderWidth, y, height - 1, height - 1); + g.setColor(Color.black); + g.fillOval(borderWidth + 1, y + 1, height - 2, height - 2); + g.setColor(Color.white); + if (attribs.isTransformed) { + g.fillArc(borderWidth + 3, y + 3, height - 6, height - 6, 90, 270); + g.setColor(Color.black); + g.fillArc(borderWidth + 3 + 3, y + 3, height - 6 - 3, height - 6, 90, 270); + } else { + g.fillOval(borderWidth + 3, y + 3, height - 6, height - 6); + } + } + return transformCircleOffset; + } + + // Get the text height for a given box height + protected static int getTextHeightForBoxHeight(int h) { + if (h < 15) { + return h - 3; + } else { + return (int) Math.ceil(.6 * h); + } + } + + protected static int getPTTextHeightForLineHeight(int h) { + return h - 4; + } + + protected Color getFrameColor(boolean isTop) { + ObjectColor color = cardView.getColor(); + if (color.isMulticolored()) { + return isTop ? new Color(118, 98, 43) : new Color(228, 221, 182); + } else if (color.isBlack()) { + return isTop ? new Color(34, 34, 34) : new Color(122, 121, 113); + } else if (color.isWhite()) { + return isTop ? new Color(187, 171, 144) : new Color(253, 247, 226); + } else if (color.isBlue()) { + return isTop ? new Color(40, 112, 110) : new Color(146, 202, 219); + } else if (color.isGreen()) { + return isTop ? new Color(59, 79, 46) : new Color(122, 154, 106); + } else if (color.isRed()) { + return isTop ? new Color(117, 57, 25) : new Color(245, 146, 107); + } else if (cardView.getCardTypes().contains(CardType.ARTIFACT)) { + return isTop ? new Color(85, 68, 32) : new Color(152, 124, 107); + } else if (cardView.getCardTypes().contains(CardType.LAND)) { + return isTop ? new Color(73, 55, 30) : new Color(140, 107, 52); + } else { + return isTop ? new Color(139, 130, 130) : new Color(165, 165, 169); + } + } + + // Determine the color of the name / type line text + protected Color getBoxTextColor(CardPanelAttributes attribs) { + if (!cardView.getColor().isMulticolored() && cardView.getColor().isWhite()) { + return Color.black; + } else if (attribs.isTransformed) { + return Color.white; + } else if (cardView.isAbility()) { + return Color.white; + } else { + return Color.white; + } + } + + // Determine the colors to base the frame on + protected ObjectColor getFrameObjectColor() { + // TODO: Take into account devoid, land frame colors, etc + return cardView.getColor().union(cardView.getFrameColor()); + } + + // Determine which background image to use from a set of colors + // and the current card. + protected static BufferedImage getBackgroundTexture(ObjectColor colors, Collection types) { + if (types.contains(CardType.LAND)) { + return BG_IMG_LAND; + } else if (types.contains(CardType.ARTIFACT)) { + return BG_IMG_ARTIFACT; + } else if (colors.isMulticolored()) { + return BG_IMG_GOLD; + } else if (colors.isWhite()) { + return BG_IMG_WHITE; + } else if (colors.isBlue()) { + return BG_IMG_BLUE; + } else if (colors.isBlack()) { + return BG_IMG_BLACK; + } else if (colors.isRed()) { + return BG_IMG_RED; + } else if (colors.isGreen()) { + return BG_IMG_GREEN; + } else { + // Colorless + return BG_IMG_COLORLESS; + } + } + // Get the box color for the given colors + protected Color getBoxColor(ObjectColor colors, Collection types) { + if (cardView.isAbility()) { + return Color.BLACK; + } else if (colors.getColorCount() == 2 && types.contains(CardType.LAND)) { + // Special case for two color lands. Boxes should be normal land colored + // rather than multicolor. Three or greater color lands use a multi-color + // box as normal. + return BOX_LAND; + } else if (colors.isMulticolored()) { + return BOX_GOLD; + } else if (colors.isColorless()) { + if (types.contains(CardType.LAND)) { + return BOX_LAND; + } else { + return BOX_COLORLESS; + } + } else if (colors.isWhite()) { + return BOX_WHITE; + } else if (colors.isBlue()) { + return BOX_BLUE; + } else if (colors.isBlack()) { + return BOX_BLACK; + } else if (colors.isRed()) { + return BOX_RED; + } else if (colors.isGreen()) { + return BOX_GREEN; + } else { + return ERROR_COLOR; + } + } + + // Get the border color for a single color + protected static Color getBorderColor(ObjectColor color) { + if (color.isWhite()) { + return BORDER_WHITE; + } else if (color.isBlue()) { + return BORDER_BLUE; + } else if (color.isBlack()) { + return BORDER_BLACK; + } else if (color.isRed()) { + return BORDER_RED; + } else if (color.isGreen()) { + return BORDER_GREEN; + } else { + return ERROR_COLOR; + } + } + + // Determine the border paint to use, based on an ObjectColors + protected static Paint getBorderPaint(ObjectColor colors, Collection types, int width) { + if (colors.isMulticolored()) { + if (colors.getColorCount() == 2) { + List twoColors = colors.getColors(); + + // Two-color frames look better if we use a whiter white + // than the normal white frame color for them, as the normal + // white border color is very close to the gold background + // color. + Color color1, color2; + if (twoColors.get(0).isWhite()) { + color1 = new Color(240, 240, 240); + } else { + color1 = getBorderColor(twoColors.get(0)); + } + if (twoColors.get(1).isWhite()) { + color2 = new Color(240, 240, 240); + } else { + color2 = getBorderColor(twoColors.get(1)); + } + + // Special case for two colors, gradient paint + return new LinearGradientPaint( + 0, 0, width, 0, + new float[]{0.4f, 0.6f}, + new Color[]{color1, color2}); + } else { + return BORDER_GOLD; + } + } else if (colors.isColorless()) { + if (types.contains(CardType.LAND)) { + return BORDER_LAND; + } else { + return BORDER_COLORLESS; + } + } else { + return getBorderColor(colors); + } + } + + // Determine the textbox color for a single color + protected static Color getTextboxColor(ObjectColor color) { + if (color.isWhite()) { + return TEXTBOX_WHITE; + } else if (color.isBlue()) { + return TEXTBOX_BLUE; + } else if (color.isBlack()) { + return TEXTBOX_BLACK; + } else if (color.isRed()) { + return TEXTBOX_RED; + } else if (color.isGreen()) { + return TEXTBOX_GREEN; + } else { + return ERROR_COLOR; + } + } + + // Determine the land textbox color for the spiral colours + protected static Color getSpiralLandTextboxColor(ObjectColor color, ObjectColor secondColor, boolean firstOne) { + // Absolutely mental, but the coloring for the spirals is as follows (reading from biggest box in): + // WG WU BW RW UG BU BG RB RG RU + boolean white = color.isWhite() || secondColor.isWhite(); + boolean blue = color.isBlue() || secondColor.isBlue(); + boolean black = color.isBlack() || secondColor.isBlack(); + boolean red = color.isRed() || secondColor.isRed(); + boolean green = color.isGreen() || secondColor.isGreen(); + + if (white && green) { + return firstOne ? LAND_SPIRAL_TEXTBOX_WHITE : LAND_SPIRAL_TEXTBOX_GREEN; + } + if (white && blue) { + return firstOne ? LAND_SPIRAL_TEXTBOX_WHITE : LAND_SPIRAL_TEXTBOX_BLUE; + } + if (black && white) { + return firstOne ? LAND_SPIRAL_TEXTBOX_BLACK : LAND_SPIRAL_TEXTBOX_WHITE; + } + if (red && white) { + return firstOne ? LAND_SPIRAL_TEXTBOX_RED : LAND_SPIRAL_TEXTBOX_WHITE; + } + if (blue && green) { + return firstOne ? LAND_SPIRAL_TEXTBOX_BLUE : LAND_SPIRAL_TEXTBOX_GREEN; + } + if (black && blue) { + return firstOne ? LAND_SPIRAL_TEXTBOX_BLACK : LAND_SPIRAL_TEXTBOX_BLUE; + } + if (black && green) { + return firstOne ? LAND_SPIRAL_TEXTBOX_BLACK : LAND_SPIRAL_TEXTBOX_GREEN; + } + if (red && black) { + return firstOne ? LAND_SPIRAL_TEXTBOX_RED : LAND_SPIRAL_TEXTBOX_BLACK; + } + if (red && green) { + return firstOne ? LAND_SPIRAL_TEXTBOX_RED : LAND_SPIRAL_TEXTBOX_GREEN; + } + if (red && blue) { + return firstOne ? LAND_SPIRAL_TEXTBOX_RED : LAND_SPIRAL_TEXTBOX_BLUE; + } + + return getLandTextboxColor(color); + } + + // Determine the land textbox color for a single color. Uses the same colors as the + // type / name line. + protected static Color getLandTextboxColor(ObjectColor color) { + if (color.isWhite()) { + return LAND_TEXTBOX_WHITE; + } else if (color.isBlue()) { + return LAND_TEXTBOX_BLUE; + } else if (color.isBlack()) { + return LAND_TEXTBOX_BLACK; + } else if (color.isRed()) { + return LAND_TEXTBOX_RED; + } else if (color.isGreen()) { + return LAND_TEXTBOX_GREEN; + } else { + return ERROR_COLOR; + } + } + + private static Color getLessOpaqueColor(Color color, boolean lessOpaqueRulesTextBox) { + if (lessOpaqueRulesTextBox) { + Color lessOpaque = new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() - 50); + return lessOpaque; + } + return color; + } + + // Determine the border paint to use, based on an ObjectColors + protected static Paint getTextboxPaint(ObjectColor colors, Collection types, int width, boolean lessOpaqueRulesTextBox) { + if (colors.isMulticolored()) { + if (colors.getColorCount() == 2 && types.contains(CardType.LAND)) { + List twoColors = colors.getColors(); + Color[] translatedColors; + translatedColors = new Color[]{ + getLessOpaqueColor(getLandTextboxColor(twoColors.get(0)), lessOpaqueRulesTextBox), + getLessOpaqueColor(getLandTextboxColor(twoColors.get(1)), lessOpaqueRulesTextBox) + }; + // Special case for two colors, gradient paint + return new LinearGradientPaint( + 0, 0, width, 0, + new float[]{0.4f, 0.6f}, + translatedColors); + } else if (types.contains(CardType.LAND)) { + return getLessOpaqueColor(LAND_TEXTBOX_GOLD, lessOpaqueRulesTextBox); + } else { + return getLessOpaqueColor(TEXTBOX_GOLD, lessOpaqueRulesTextBox); + } + } else if (colors.isColorless()) { + if (types.contains(CardType.LAND)) { + return getLessOpaqueColor(TEXTBOX_LAND, lessOpaqueRulesTextBox); + } else { + return getLessOpaqueColor(TEXTBOX_COLORLESS, lessOpaqueRulesTextBox); + } + } else if (types.contains(CardType.LAND)) { + return getLessOpaqueColor(getLandTextboxColor(colors), lessOpaqueRulesTextBox); + } else { + return getLessOpaqueColor(getTextboxColor(colors), lessOpaqueRulesTextBox); + } + } +} diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java index c4065a9b0bc..9c1ae4f2f3f 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java @@ -543,6 +543,9 @@ public class ScryfallImageSupportCards { add("MAT"); // March of the Machine: The Aftermath add("MUL"); // Multiverse Legends add("30A"); // 30th Anniversary Edition + add("P30A"); // 30th Anniversary Play Promos + add("P30M"); // 30th Anniversary Misc Promos + add("PEWK"); // Eternal Weekend add("LTR"); // The Lord of the Rings: Tales of Middle-Earth add("LTC"); // Tales of Middle-Earth Commander add("CMM"); // Commander Masters @@ -577,6 +580,7 @@ public class ScryfallImageSupportCards { add("PIO"); // Pioneer Masters add("PW25"); // Wizards Play Network 2025 add("INR"); // Innistrad Remastered + add("PF25"); // MagicFest 2025 add("DFT"); // Aetherdrift add("DRC"); // Aetherdrift Commander add("TDM"); // Tarkir: Dragonstorm diff --git a/Mage.Client/src/main/resources/cardrender/background_texture_artifact_retro.png b/Mage.Client/src/main/resources/cardrender/background_texture_artifact_retro.png new file mode 100644 index 0000000000000000000000000000000000000000..16f4585dba0a6a1196f4fcdf1c8f0c0d6faf6e3a GIT binary patch literal 128050 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

RX_E+a&do@VkN4&DR`I4j1-G zUc+0Y{~36^P1C>u`S9Yb=i;Qq56^AC0FqZ>{NbCkTLXHMWJU+kCjRj}$v`xa=BDZt2HE!H#grmniaIj-J0y zG4Ol|9>|GM{+(MHL2Rl%=L$E1rOi0Z`~4a4!}HhBYFQbnM!j|C6rXFBdAsw*{!Of7!Rp&Cmj!^(*eQ?>gaC=Gmf+qN}z}3FqHeROiO+ zUd9Z4{iq3jadjMWf#U3aeIxc(*paeOsz??T0%A0Qcy*nDhc70bmYBC~D#CACX|Z*} zQQ}2ATxjrDnDBEDIjiLmJbJ><9XaS(@W4H&r-z{_g{g8Osy{dz@+Hw$@BJ{by;?B@ ztpCpYPmpKrH@5Spw&3w5ngeyU*4*^3#1wgEllJ(;hc-OQ_&e{}&(xY&g^0DCIR92>9Ocq3JXGU344teV-l>r7vB#%KhDrPjV@qA}W9y3?8 zDDUo_h4(M;?GVS{QHQCwe8jByJUoD%Vrqg%YcE@mEAUvsXHA?9ejp3WIjDW-4|01Yj zBzd@K4(!L6&BbxewMGq`Z*Yz(`X*!?oGGh- zh)JKHfXArNNFv#p9Z^E!FFnh+=3??x7>pf6!Hi??)&qFe7`Qq|;pCJl+7IsGEpo7>{^ zCHg(QWSiE?`pBI7ecI|3`0IJWnhKPII^a9H(Tq4VD1U&Yopb8929Mkc5U+m^IL15V zzb^NB+xKY>x21Ft0pjD2%Y|5HOhc6;wC{8l*;7u_wr_wVk`^SN1(8D3d|&WXK?%du ze@iG%sa8M)9m+@DP}WCrc1%(svp=#)ryzpuR=<;zNmbBW`MW#Q1M|8v_v`-Cj$z~w zJnXwx?7=h$vQcB5#`JF=cZM{7=uvwbvrRgxtXpIZi@bx19FsZKT=LoH<~z}p&}Ea} z0u)L%HVjGFFBk*-jZf9bHe4azx5Q1WvXc5)f2-klP-1GvbT(;RJ*v(poZ!oscKMd{ zBLEWnPKq`CfsGm*O?-DNP|5Lpmia)8a5bh(apv^Uv4!}r(wujC9%iLy#{qaw9k2KM z8$>y95XInO9sE}1;HMdfl1cd&Daci9)W- zXZy8Plf$@nPJu)+2J<@11HOPNAo;T>e8&aZfs*l-@*}m@!;g|eq}2mNc$zwDdceg0 znnVyK2Pyz0v#iMztN2(#SCKxTw8KPHKYNbQ(r7>k%Yg5|BkSL)dV5FjRrGN%G@)2- z6H){$j_3@N@OZN_voUg~{@FajBpwZV+&CtJv(Pq+NVR3&5ZqPmt0wcxnS&^lVBDfD zQrsa(e1RpY-;%kleS{X}tpMUn@UTgTfY%^m31()Aq)sKQRvIzw<9RP_X9p2e%P7Dy zsghpBzdB5=6~+uVe1~r$y80SJP?hJ4z7x@>^i}bOC`Eb4y=U%4NC%K$W@~FmA+6N| zg`aLOFV7WnLxHO7pdu#Hn^pZZG8Pjy)33rrK0oZn0^K?gghn{`*R=*Il0IBxgJ*?| z6apZ^%r5e-;W2=#EJHVoAi|`Hrh0x&;FuhrN1dT9Gx4jON9$zFdCwp- zgs>)Ot!7iSw-JGi&l{AL+)oLJ6Guo%BHrVDNB{alM`%wQh3`aUHwO-EPud2ht&)SB z5@`pTIwV?L+y->`d8pK?q_nK5qT;fu$zA%1^gu*Tn$+*+gzGJMAaUO%S023Ip0A_y zDn!mq~1t_99GEIHkQ)KYu7~45#CRB`a zgTB~P&ZbjjZ-=pLu;9VO@B#b1hx2pg4R}OnqfhG-SA;Kbkxt8K<3FPUBkfH1;NT7b za<*LUM@`C#ZtoliEqH9phdd8_%lTSSYB4Oznk{=D1wGH5)a>%^(+)iB{Znabm+*WK zC(SDj1dn5?WiBZil7(DKl6~GJi2zKZcjsH<*BOEd>ce&$t% z`woul>dqb|%VQR02qyhMk(PpqfFp|AiOHT-J|9%#2W4kn$9}`wKge|anHnoA`zZB+ z)s{t;NX&&jRR=_+xZVV+k$n@Ek%X5`HxTyK0L;>sPaVN+0~bjS;+J3_*!`=>7~zf6 z+YuwN0n~4jFd10P%1RT}*mJ+4t64~|aR(mJQuvp5+?_do5UsX>N23~1Uglf%Zx@&_ zniE8`?-oe5)~&puh%at;VS7l#~&lSZFV>Ik1_rqnXLK(JS& z<9SXN{n+;3WrW;B3`ZZAG6hf#A~+&i(m0RMnuW@#s{W}`%nN=49B0ypDqPjE21Fq! ztw7CrgSs|b45UFM>sLmQcIn|b|C{fYrMggo)gb)QGw6l}k_H+95jG6%8v;r+fu!+} zUadnkwGkNpBOaG)fJhWmN~~V_=HV2rq!6eJ`@Zdq66GhjDo4V=D$Ih~PQ%2^-4BE& zI?h(K%FCxH5SifdsKU|?1PMXJg=#0gZj^U-?+DkX#=7887QNP3{!c`tR9lZSuopwAVgF3umlhD(4CQPn1x84)a9i%=f2>-i*|XD!7;!4W`18 zlnFd-{#am}6Za(abl8=|k3hVLrLG;2gd5X8k~+@P>X0_MzlypmKQiI*O}|2>$JE8< zepb*9M0R6qH-M2#Pnn@bp@Tq1bPCdmCDen&oIH?*fd@;=t&I(ye!s$8&BM@ZCITgP z4#(w|9j0CI0rwww)|y(6CF zE!>H4&^|BF=6N585)sjJ(+IsjcLjf^m=$1bEWP5g`*TWyZ1wyHxN>(3dfdS1bSRHLt;%5o;P zhWU!O1JbT|IujiPXww2PX1Ty#KTMzIG21^{y~d7vt8^OQBY4;X?Su)}SSo&b@NkU| z$|EARnJ(4@Ruw+LVT3iAQpG4g(NGAWh!m8WSrWxaA9jS|@9rI;^mCcCM6&pQ&Q-61 zhezK2VLr0qU7~jU9(d?{9z^zKuj<zEHN#q~ z*d+1{=48&!*VWHcw5I+IBCb5d2Xn;7b5Cl>9Q&*qt<^vCIY~ZH(`CSsrUNO%wG-Z% z639YkjW$rMNa;9VQ(4B5L?O*}hWThrNMp{bUQ0M`2Pp7XHDxL5E4dGn5`O4}8336; zX1`r?2Mck1MSQa&cfc{MpNtyI6`Xvel{z^BG7ln1GGdDOO$Zg$j;wDgkAEjCNAg>V zCIj4ekz4Z=16Y5SJaB7oqdHUT}u*#e8#nM5G+L#?Y>yU5g|lzhN`})YLADQA2D;S#}qCJHe-9<CQ|z?O()y-6*xHLW5gfi<|pFL4>kcL!)}0MpysfGaVWZNuB@%pk;J3w zrlKK{U6dCGV#!l1g~;EvC6?A7pK;Qzy*%^$2s{iSjwIMD*?z3^=7t8!m=WJmnPSOf zn2HzAeB|T52D~g9Y%6#F-QqeoAo46g#PnmgSq>{z-7W5@1}#rD1_eQc;J)_^XZW3( zDqGsSQCUW6uB^OtzvB=*ZuuU0b7({b#E&HwLaXopTFg95c7a46EP4lXxD6?}SgD+X z4PUiMH^%8RfP{!#xD{1DNES*O%RGMZ+E|}4iwWe+6B0)#Kh)_nxxj?uEQBPnSxDcd zcV)aGzN(f0IDaGAabKmiZ%mYb8!1w9>NbtG`2c{?edke5YY@rXx@ckW-A=#35=83s z=$8JB#IY)fbe3@y6W^S2M?N8bp6PwN2t2ml@*=S$gdc&?nH`@<9VXodJUCNn1@Fpe zectX3q6zLPh+tzdB+1UpGUB#WSd;FVP83*JBuLHX2dXhO0negW{sU>He-$@skAz@I!!>wr>Va~zCLEjP2GWTu zOX{WEA}fCD_oYvwJ+WB@k;AkCktpm+foIz`jyiG>`KeJrD?jSver-B6iZx`Z5|l9> zBOA9ZN_sCu>eXd@1tPu9(jzrsU_VQ1=K8y5|Hjb^y5VbIq_t#_y=x6FR#Jy6-w5O< z5`&}&NNgEDnR;(dl>IP8I>%4rw9T=Na#)nDVCd_=Trn#UajG0*y7}&hO-X(8`NKvM z=?ff@x+Y-q1Rq$RA<~OPuhA}AKfRrH1XA7SvsV*_@6jY&qdit-@|;gbYiaZjM55Mt zLSa=W+mes=HukEa6^KM0(QhRM{1zcNe+eRcDa%v#te%j0F;Ttlsl z8ge|*gneMLZiyUE;(GeE)*!MfB~qi{QZBBKKUFVf`wMUOC}zHw_WKl%2D3l=ebsalG(#ebx{G$ zzItYAzwfh_7Ht`kMc|lqzzx5jHD%C@QZ2)vH6JM95ViHHR>FaXL0dDOpOb!)R80_E za?98!c}2=Vnx6%?6fm(s@@X$8RzZnX!-NjY&Y{&A~Y6v}h-5lm-WHySh}Dbdpf!^}BSIKO*jVd*0M5s({1Sela9>aFD3qys_mm=##;S)E1>z#8|b55_Ii=c>>94-=8ub0OMGat=HW zrH;@8{pig&WtOgQQlx#d<6!qjKJCS-HmX#^Kz(&vF*GtwTMW4ESaw9KC2s+!RJz3=fesUzyyh*-XvPdiK3U4Befg||ye0tpAH)#&eU3g7MEzfoI- zZ6ZJDuEZ1Z4)avKTAIqM45`HK1r;(j+5faOLUafuWIkMyXK(U+1_l~Th)O{+wGz9m zs2d5QQ(~0xns@yT>4*gK#It~LqE`E=%#6JTUKaVU|L{q#yEtBH# zAYoor{c7GO?UTEyzZR&J;Y35Jo2gsksrCzVCU?9Q*eL&p;4xRq8a(7W1BADyzrM+T zvNz&XSk^o)s`wk#%(r6IoAicEg$XyCpwtnGIH>P7HrY{F=5No#c%S0l?uchoh3&qn zCG1+k7^$t+ZOH38*-QBrJWfvXvr@@gm6g}!^UeskGyjP>GF;ndSaY?8%Y(r^G$3LyZjTqcdROk-q|?B$?xE6uejzN0gKxbm$Pc`3Piryl(*Nm z*O5wRRa!1p=o*vrHD|pCU}=*j;#m^Yd_2pe(K7TM$>xdvt3U&PKhIYPJtZIG_-Os? zeknyja}=iC7+RbJSNvcojMK3hqRk8TC-Xo*KBf^jk$-Vl-PW#(cIvHtl8`DEM2HYb zqVtni)@7@N3seY>zIRj#@3?2YJ|paL8&1A=yb&L_%g6l+_d33uG%4wPL)d#>ttRsz zBKLnIot*MVWcFKF)H^?wWAgD?Z>GnwrNnZJiN=}er+n%+$w?kBB_T{qe!cG^3`b6U zKaoD(YLDJT7Wv!~{`pkWki}p*w{MX7TgSxH*G_DtPm9KAyQ}(W4I)IqfxJ|0Mf)F? zSVqLtDiTE3+w%LsgDXp=pX@{I(kzREdr|&9u}vJ0)rbCx6!Ionkd8RwZ@Ba6(JPpy74GkUjM~7dzuqIz>D8j_HG1x3UrV$vR#POF+!ADr zcW(qDgMRh>kwM@Or4U?zr+ROz-k6)QIgq$A6en{~sY>F|CE{@=@$gp!)Jh+}6-@Cr z_7yOlI61CWTK;=2%R=w%1J#vfBEn^ID5+{cgTwY3V+J1YKC^AgBYhpwAzV*}qgs8F z<+m7_VuxT|j#djvyH`3Es--e6jyw zCAO%(zZ{F~P_46Ilrh(Q3Ld+(-!IFhr?Y6sBE<{0ZX_eFMCBnlO*5lIGla?6_kV4$ zu$TmEkAHpIVi>eGN(*|NSyk=SR;OmY7C59WO#(r5`lqe0q7$pKv^&B!e8)77pPV4e zM^WJM1Ah`##!pZ`)eph-i}c-Q`tRw_!%cEJTVUAo--t)GBh(Ra;QbYNM3RS>%)W2i zPt)nijM=;K{e7NqyzHkM{C$Mv%*+~rq~+=3QF@!6XRG$LYP-lH4xig+IPW7?=1Apy z;MIO)kp*<>ja5~al&{H)2;N95K?1oac$f-~4vqSwNWDx`eN*iHgZEc4^L55}18ZA; z1ZF$-b&*0-(-$E5+txgi-g0MAlh5_w{w#@I{wUvD@Hm;mS=&y>X9swreIRj!Akr7W z(i|GOb~`qQ;34-^e(qApE-*B-b>!usJOq2z_pna+hm!Ehl5+vL<)!KQZ{;})U zMiaJYz9Wc?c5TdiPqjcR$>SJ2Qb$6*HKE&_q$Lf?n*P>1GD}uX#iYGQIU%fVy(Fry zLxev8krPQsj<(GboOfu+JMgx2cr0y)*3ayKd*f=u=RpBKo)~#KG^NZT#r|tMZ~g;8=i_oCehC z;FqN_c4-b%ZDJ^L`n`9|UT7H?fQY11ACYe@S`~P_EaRbvKGJZtaZZW8|Db|zz@yeW zI`r)fYnhlZHS`#hV^aCc;AM5vEukxY)a;SRb&btr|PNI z#{90#G2NU);LP8?bSO3E?j#z z%iBeQjzDAiemrl`|HOSu1FEToAgzbwWGRjABy(b^g8D+0u0nlLd~I?X!B8c#YdL(vMJl^E;d(U+%$p=OQoe`gaavDXqTAYMuxu z`BE+OCM`E#H+iG2rRuLy06}xpMQ)Amp5=EQ3vC+upY+vvT^a zZyb4yN9Aj4r^Wb&QczUy^catx*&?IQa%8stOW@(9k24U7+xN4NH>3|}lckdpGmLrW zfl`=3HjkvCe+V8sugiIO{oE`IO=trk`R#+S9G7FAZb(P)&77NlTk`m|1RP~LHy3($ zmvy1zU%qF^fh_t#P~kZ{;IO|F73`sU;;XPPhDuqBr&3HG6SY(ZB)^&d7eItcC1#0; z`{r0>L*!TOO7g5E&~fiSJc)JZrA`#n-8WG^(sBcOxnsnAFhx3(NPh|*Nc>M#Mb_dX z^wD?~$0|rW)8$#=`gxMj=OfLN)oqB`iBsA)X-QCax`H1hM>q`UofF;SZn1rQz-4}u zQYl0vj1_peKCr$Y^_wQ3{HWb(U%?ap@SP(QVj5>_6zXeq0V0L=(@E5hOhjav+ii;$ z0^|U_=cQFA-lHx2KcQQ@egJ?r47Fc9z;2Mh694(3szwAS6svC`NG!w z{4}gP(Lvbuze`8l=t~5A4RV<+w4r7WJWi#iM2PwmKmiaT3V;sWI1&9b^^uS(-EfY;?PfUh)U>AYMZxGv^@yA&idnx3&tiyw`zD*xN z^FL%P)1*9PzaW^I4$$fG{?Pd@OIaMOG(YRJ)NS~Qna@ihM86}6{Mw$!7v(f!5kFbK zmsiKZk1@Wi|DGmG2)T3H9B|sGIw_s%0{i2x^fN5T_rtX6HHd`yvHhzMK1vWQ;kEc~ zSP&sH<0pUnyjJQh(?)F}^>^UmPUZUxG2f)OVWIx76H6>Cb$4N!`&&tf`EQ7Rc_s6* zl=35q=`HwJondRkne-7kMXA7TV?W62H>T#?kI1t&UN|`j4Iao(WPm#{JtA zT7CVYdTG^ORBi1xcYm@!VmZCHAoA_zxhZ9vBa@olj2+U4R)_pmO7}goEgaJKkD8YN z38vNN^(;n94decfLC89Tx5dH%&a1J~$Vt2H{heRXyqA}ofrnKc{db4K&2*~1O7~6< zLYREjuE60?je4PTz1=rzp-B3Of{jHY*tv9ytUKd%Snb&FRLK~(;t0^6@u zK3{+F|HhyGztX{UG5_K=pQ)}?8-D>jvU{VB14dHKiOldOlY9=M^dh}1rHtPvbo5P% zz=Mdqk#F0i%B2tW@{39E8=;An-E#B~^1J~Nht~gty2x)q1k+yzV%(_?mhT=)8)sj| zK#7q)ev)`m7J^6^%rT(3LQs?1^yO**vOS5U?33x80Z9%X9zs??(%QENk`vXHWm?wK z%^E!T=J9Wpz6mcBw+Gkq{nh3<0*Af-*Qwfpia5Q8AN5ZaQ0f*Ovgf-FD)p6ddU?$M z)U%i9`j_c9Mc}s~ng4SBM4r_@uJ^0=68S@62rkku+m@t^V8`KA0=1-Rvd^&j9Ta^6 zBB!aC|78_hP}C;60gwQpe=CusZGG9-2t3sD1Vl3M*g2ircP)6F)Gg^7hCGx$eyG|? z{Xm33ogVOh+MykCFS&mJ5BDh8y#BvP(uT+aYqi&Nn$8cMir&rlGuCA-qNl!4S^bX1 z*ru_6Bs@Cnx9;#}rIB!xxs3lP5}c2_mY$9iY@kGDwMXxpCcJsg?XN)hnTdFe2UB6a z$sQ#ueVp!n{AKzEJa*|Vct|?ki~dVT$PW>X@a^s7!KMx>M05v;B_T&vXLV%h8_O$q zpvi%E`Kx$Z{eI?OLi9Ukz^4bxVA`E>me;i^bM1BQ%Q^#(U9zUHd!i;U099ON`;XGf z%oNiKJXYqcUxUZ2I0TWS_oCki5)Y9Ei(R{z$`(Z4`cOpsP`@3KI<9vmb?AO!@W=E- zz!#2#MDZbatCUF}JGxqGb^9R9x3J6pg$O+C?>C#vF0^rEWt91KJkM6tveJj$u-|d* zqDTA-@YvNErJXJ`JD&8X=DWJAbMW}as^%}mdJl{}aH``Fl_e*XcL+JeIx$ zC+X$fcd@m82qNzQF=G(=-+9O0c$sXcl7B=FNREp)&_k(+rRt#^pH+&j1umqZ&PxLaIzq^#Fv*L)d9z4|VNOcv4 zIf(}f)mNGH@zw>&!4v-VI4fR8YwznR=>Yi9k0UwYlqT&y}b@y^5O*5 zP*wIqb#>4b-mqs;cF4%pgfa)s2TY_e$?KW!#Q{w2D6k)0fyBe>V+Q(1{wj_Mn183$pARqZPBT=?#;czHi{Pfm(Rbe-{`7~&1fNv}wN|>n@TpjN zniKskc&yUOa%?ro-&mYUV!mHlP`ybJfqw%cYq8pwZSULkpZH(C;A(y|vfFD9`+*wm zB5%o|^zkKlgl%=!U(uizbG>>#%PQ_p_RGBc$8#qGk&!B>qW%M=BN3T1(sMP|srKbB zfFe2z1TC_vsuade2xevy`@fJBcy!Q0wO14*%{oIy&XG~t<*^?bIf>z@bht`ZVsyJ+ zdA5FM{+QkSHZ%$I%VB^@y%_HOno*u@xvya)Tg8n z)7=Rk<{kGP&g$Oz6GamC-ftt`$4Jjf`GvHihl(*Z;PHP28bnhHsYHQr+P2omMEF0I zLUy{x2t1N_SXr<{e6|Xzc#>Rr6`DYTBI-}$Z@uBZvPHAvu6E!df6oH=dCoEgA9vf9 zU(BEPQb@GHe?l1Ea>z4t@Nboh{c*POy;WTWCC1XoSyHf`VB_er>hoIqut}uZNUGho zaU+FAk_bv8n73W;!BG9+N2n`#YOUI4 z`HK!ePcO$vB1GpO>?a%FM3Z}{RHsVoY9NBr$Sy_dt21xzv$W&N&dbROYj}o9w%P62 z*zY=e;#q&9W=o}wU;GBMviQfFYP6|zQZ~<9Oj;V*Y1Fc$z4Y~crP~1T*e!u9I`kX0 zQVJrg-V|xrlswjZhOv43s(-r>BboH^t>oe8>OtD=zo6fL|2sQ&#`mlf23xd&7bEky z-`snNB#~c>3zOMbM-hTSB6%CC;HPGO(Zt@F;9_2PFPIUhQUJy_(j2S)U%$C(nfc@E z&lsPKn~kIre3q5_cbmUCcx2qO{jMo^$ZsQgpd^z0+a)wx9slOj5Ik1jDzE3jfjMch zlAM!{Mv;TZ`)*C;=Iy6zEeq&q(~E27NAwU#b|A9*w@Vg8 zi1x^ToTMB8i!hG~0?9Y078jWQ+Kg~Dxqr%8pPV<65p545e@3N8JKp5^GY|;@yZ?Ow z{I00gI|+!Kq?J}q-|ts1z~h@V0T9k=3I5j~z_B9q+4#b{_!0mKzI}kbYzc>Jr{{%!1^Qpa&Mg0F0@c1plwc=u3oe432 z6VoUnz+d-eA%&czXfI?XrC`2QIj)T-|I`bARaB7cOVHRqo51lK;BlfsaQ;Qh0n+2( zu8N;m-!fKf^Y=69gD3`xOv`gBg%F*Chz%cAiQ-A8_A8Ex?s>D3l1`-IkY{HjAhG{k z@uw1r0g>H={C0yYJU;~wf(p!h|04Z&Dwm%ptJdllWG;Hj=8^o}&xO;C^ivRt8??`| zdo|tU-szRX7xLek6q196l|jrucv z{SY0xeJ_2?_ujYhry#O3-+e>G|F6$f{g_KeMQscMP1Q z_yPapbMQ#jsz49IGtisJ0R74bMuEmk^6pZI-Pg$4`&$MnyR_5LSSbYWeY3y3IMUV& z|J$ASI>n_l&x&{$Vr@ahot?1(lBj(+NYQ{rg#K4I*$-1Bwq{aKzOS&UV-ch0+hpTT zrK+uHp{J(9aQ%jL9~`<6mvucho%?@$X~vtXO7Pj1ZT>;}u3V>z%0Pn*Sisz{!13M; zH=jR^=r`#jI{9%0AsJ}s-}$(OMhgcf@vD4cI^FxMW&S%&zd-B8iG+oe;6cQG??dva zdx7*3g2&O$D1Jay2MnBgV>D-RzsUdaY*3O*ZV7T3^Yuv??yi(2FTa@ zlg)A+hGY>$w2c2~ILRa$+s2n|vG>1C+i1M2=(`b}>iIKT^wY5Jm_DVqO`6C3Ki{4! z{?ll;k-rGh*G5(1^18ES+o~z$>Nq<2s zec1MzN)Dl9mMqlMZ(v(9|C^8T&i_JvAI0UpOX)XOV-kQz1PIRfCa*V>fVb@;%;T+{ zl=Kns9DaUn>Y#HF`K^Tha1uwjOyT0L1@K7BXL+8YsN(l@`E zQ$%Odmz6?75IIR-I2@kT6CG(wMD%GK+N}vR4lXXLhhKYzzmW!hE4Ir|)JQKX@z*O* zI~X?sk)0G0zH6k4bzbUu|2t{KroEafAdu$zCoF`sPCH;oR^P#onI~qM8jB$h0?BFm z(f0~9R2n?qNE?5ZqP>PA$z^+ElT2TuI!~o97ndtwgfXQ1D-hXPliKHjTfr`cClbm4 z9gA;7#LuG&!;l{*gga74{r15syY!ppZ`=De+ZW!SBRaqrhuYW_frXVu{_=A~r}kwl zd)16NXWzleH{fxSoX-FS;1O`X7D#?dpr>>2svs|Y?7-vOcK!66n%En>|4n9y%;6ts z{9d>3oA_$&{XCfB5r`xadRrU*7VT>(MAdBE+RZflXB!)ZOt&unnZZLmwG7-9~NoyZ+1c#1|L-KN zLc9M|D^uZrl0H-m(cO0JnI57_RG&(85k2?gc)OvuC4bP;3EJ&8DWh=bzlox!;E{6r zK?s0v$X9+~-((sRLD7zZ$SHR9O}MGkPz1i2DZL&W^v^M+?Z!5=CZ5 zq(A8fuu|p3waIK0a=9`j;8 zp6B3^?dL|P6AGQlBogP})+_$$Jdfzxqjj zAgbR?N0vvwC5bG+F86Od0~&&a{x|n_ zRmwcvYiYGt2+C`-)eAPi@B!KzgMF}-D#DIvHE~nWL=`aS7}K~t=JN^-zuFD z#GPM>R&i`ggL3DcLjL-szMrHM5K;NnB9EOm-DdNuJhkVj&#H*Ha?IoQHderaG$w3^ zly5SBk+uXg0TKN#kUF4K9$HuR#Yq6e0*6*{{mw)?-Pf}q68)RcHIV!#QV1lBHCTxR zALdpY4qfRlP)Qcr21HKM^E@d4{S{E8do3FZ1SoFtX{nvNL)^RWuy)Z z7LkRTZOWUbxxJBWPW{plMn(lhdqyS%5>#a!N;2Q3C`fIsviC9m)4)XbLI$uLuG0(* z{)?2M?+TT_*<)Fq^RZNCEdnL}MtZYv6iwFO0*|0J_%FHZIQQ?SV~_-+3JWTyUjEK= z+qMOISmE#L--;vcg>KsQ5@u%=0Tn@S-&=yjPAW$@TZ?>xA7b{CiwR@9dpfIPCVPQNK|^nTS-JW zKwsDyl<^(5eZC#5A$S2CzibHVOq72pHDprA_Lz+?k~%_{D43VEIG>PO>daXEMp6&goBoTvk(E7uOwSC*7?tVvKE7v=e`5qzl9fDv3zVyO zWCJ*Ed^gKB)F_oOzDTcM3!6G`Mw|O#F)0CG+WMNl8r94WH8$y2fMZ?IpQN|u<(U+M zAX3b|6Dh>ziRf_6#8q8TPBOy#0wh0mp${6I>8HcCz4#DsL1gtl(a)^1EO;o>y+7C- zn`JLJ?S;-dR7L2EJhA!f>OH&v8&XXvZS6VTx&X$aUi&vo$a}aM5)ku3xf5WxFeliKz&wUt14mrYuczPM3yDC4h#ZKh^d-F>Bvw_f zxHh|*`o9`~@UUU{^FRu*z_9|1Xwy@joqQOYq&4-w-~4xA@yFm{$Kpi#7=**-iJG(E zP*X7YVv(Xr`3_9vcT~-a;g6nW^^TZg=AG!e*w#q=H;&)2@1B6?4#U+qhHv9Q#8jR! z4m-(o-QfH^b{F5Ize9mY&G$BaH2OU}t0P)3wRDlb;+fx&MP>e(w9JCY z{Ep`1E(S8Quu57*anF;qkyd{_{b4;n32)!tw`$PDI41xR`R($(q>=ovmX1sCBLWeh zd8%xR`8_k7`-?=tu{2@)57U2D_iuv7$=gUg*xXak)qNor)>gl`_lZam=7>11+BwPx z;)ZR`^7}TMCUNXkmZi|+K2$vfH5#EqR69&S5H@Ytjaldx0{3c?*Mgq4eA2%{t;e$M^hLqvANDrm|Cys%Xd|WsZ z_j#TW{Z8Y;{Pjs&r{`?^MM|S__P-ol+rQ!avM(eP$8eoTZF~Jj-X?qA2vh&P*idI8 zDs`%r+QbE&kQ9_RzNw>Z{%AlkkNk8Db=CO-dNi7ek4pQ++^(NesCGapCH^q#C(p5p zrm33?W%m0bqFb6;K)u-WXwVS}DQ*X=yX-FgOZ&N_%7;0Us`B2@pk=KnBEEY0tMvAO zvmMPl4Z2!NA*x*kRoe025iPdh5$zF=xhMVz1v4}EkH_@z6My;yz)!IK|H@kpH}2#^OT^7*D^!;L1*lSB#Uo2V z&_YzW6Lp{rdxW?YA4m`Eg27WL%GqscRA3zY1>k79}@+kHl-wSK@9qpn17fjH~ zSLNF_SP%PG=g<6Vd^u=S0vrTKc1!jiN+Dw5s7ob&o{`@z za?u4daQM)&Wh&U4H*+I`SX~=>{Lb%>1H)7GWm2-8GiqUC;Fu>Zz)K>U`=kIOWBbYN zqi7I8()rih3n{oEgY-o@1rA%Ml|)(_9`*c0LMf$q8u-m&bF~n`{)d#CozE^Q5+FW& zx7Emfj{_%xBE|#{$jz+?Gkhl18m-EDONx&|j{Lz%hN}EOR%sO?BEE?v5F>Ta?e#8e z7VsDa34I%`KmL7wXy*@79Hq|27(c$~MRFvINGQ@V`Dy1_=|7N(ZtavwY|1MgB+Ozc z80W1IPEoox+qj@nkkP$nm?;Z-g_3WwOvqeCzoSP=yg&-&v0Wm1MBQZE3BYR`sXK8OtFZ%QIU{0DhO zX|G%jG*l&15*anwjRUjLYjg_YYtv;*219Cj`2PIuxw85AZ4FV4Lc}T+dW7K-POm?g z`PQCIODcr4Qf+&HK+{|&tM*!=syxAn+Qgb_dHL&f%ZI#lT*pgI-&s-mh)mFD!;JV{+r1T=<9%I!S(oSWowZ{fMySM z7K~$C)PA*<%O476_gcidPLanAzzn66;W(Eg;aJvLKYsU>bq`7#NWg5zQ9C`=V4e=t zG8gZtugEhot(7)vu3pNsBL#HPCELdcRtuHL^1_TLHHIrq%#018z! zV;?599}FP$Eo2O^>5?zteWEpzrBm{n^*PU-5I2(f|DLc%x&o~>1O^8{#JwZT><^?6 zqB9V2?{wO4&3!-k@woS55QbWa?^!r<90U~=nj(&X$edh{w#JHpgaRpqsQIIaBw{-B zQB1~#1$EnInQydBLUF1%l2tJ z?}b^AihMv+TMa;9CS3RSnfh*ps62DN$@dU-)+}R6Z_$C@vR@Vs};-7j5)Wm6h#>;GXV~7B-fg-zK52NGGj>VX4KK@e+~j zl#PXc6?$&%aPmA)lN|bD9Pg44*C~l09nvlAe*;Nlr9x7i6M)EDC@DmA8DO-@f`Vw;Zy8$EsA}ED~-R>`; zh%``dvloF%To%&H>fm7vW|CKv=(7$ewr<1q{>CE?@|-y%e<9 zn#azLn5$lT<-Yd-To{alB9hQ$W+K!iWE;ZE`3sq?BocOl^$iCk3I&2W6R=~d-&q@@ zhfFk#0R&K1--QyovrKysIW9(Dz_UIZc!EevF+K8oCb=rBTxIJ*a0()6*GiW_>;|K7 zVwGTGT^)@`6Wjt*LlsUG?ulS?hR7>YNb$fC0m-scGB}A2F?ursk%77vne4Tv-;)R+ zQH(ai!@d-w%K8xVKeGDQsQOV>p&{F zW>i^9@5WxOm4V0-mEj#L0;ZmruwI_0jfp$t%KUBD?yRm^q!1PNmcBEHN%^!*bSioy z-B&vLBH-*Te7u=%tjjO%S=3z2KM39??`%u{Y%Kcz;NhSx@Vo{K*(Jp!d4fCZ>rxGf ztE)|Z{kz29XM7v^S$ezs?_exsL*KX6lPE2_nzjX?CpVC9N=?JdWco{KKvB7cUO_*S zMkf&b<23tP;J?28tFP%T#W=JSXSi_wMDW+H#wjVTXVvz^MStmjGd1uizzI#J0Lz~? zbEjVI*~zAnli@>;0+EySVF<&{S0TE2fJiI!1}>^~=aPMp{vOqf__>LCULqRYHg2kC zATl~7L>+u!I&eU$_6`T%_a!nJoh>Hrcyo%SkdMZ z*Od*65qYdv;99N3)quyesd!KeRaSZxB?-N|;oVIVB0Y7zaUQ+E)k6%XAZIsQ1g7Y}#gzzD8!d(5|T8&Kxw%1$p4Vs+`D7jX^tr z?UlB4dQf_&9%`TxS#U}FY@1tg`i5;rfxD9TG2iCc7%2gjY_99NeL9y^%(^s%O$Gr! zG}469zlV(N1QDtawcsJIWjF^ceDjn>78)J}HUxekgDboGr|JlGm5aC#J>24VHXkge z>#B6qL-i36sHlZ*y{KnB0zA1$OOL_zTAauFAP^f0K2!q>U7;lJIN8QUoGjm@haS2} zR8}~9n`7WkzbGJ*wL6QAm7V)7QEU8tDI*~N%NtOC$%#btw%|M3`{Nu8tInDg5GAq5 zVwz)bRTpWQLpszXS@s=TIliPK9qT{DOd2_Kn_r#H(aXMQPn;BZPz=Uax@tG$5rD0x zmZ~P8(%#2>ERptwq-E`%#u~FiwI4Q$joPR0O^dJj@+9*hu9@~&UlUh!HaXb*h3ww) zZ@yGu1SOBmcZJyjfSFk6Z9egLCd8`E0TVg`w{~?Gs;=WZ(#UxFc# zTvHku4KnW#5QH)(cC;5FKtk{iHdqzi)mu`Il>ktGe3`wIQ68waEaNfy6G$a?ZJi`5 z_=>mxv?RN`-cqO*pq;oTh4|6 zye#*Vl!!wMA;!kvhbGhSO|jP$=v4YhkBERUN(dt3`Bn1wue(`cjmByKZ`WNXHa=V7 zF`hCh3&&%rq!4Z!K#`-N3}$oy)#wa>NOp?W1q9M=!bkPl>v7MR$wiy&mOcwf8_!E| zD!Z}ib(JBBG@6lnv$w^aQ|H=^4=m+CX%LH0Z6trsJ_|C3jXZ@Pow?+sab;4b`wZ??>N%jUn;hgN{MAlx`jQCtL<2%yoVu~ftjF;!l&p-?zB1>YN znhIJNNrtQ6*$^;4(`vIz5q85o+W&~?4EmGA?_3Fey67WvDVp*+dek|Jw%9At#q|8s z2TKJsY;iN#r8>&U2@_gYm62Bgy=fPgrSvl>l?3#Y z$b*O6%Qn%q6<6$(IO%p0t@jzxcxlW4&D%hQ=7!x!=R#D=hxobGfHy|vHVuMKG7(ET ziFCpOBF0k7T4P1dJt#e7bkW{p>amhk0I_1kIBv!PWp+roSt6PW<2s2f>K6It7Ox7W zv`&ED#MYXNJ<@N=GbDuI|kBItOb7V=$B z*9k<0aoPYU#CIP+@T3k4S*NzR$|!qSX!H$sRCctJ6lcL&5yImRZnnn@Qa3<|Fw-V1 z2U>ZJJ&;FzT;`if1prv6v_=zD^w+m{`48XL15+7kXkrmA53d#fI7Q9s3?Q|6rF@hD&Q9~Lx~V+e-XWJ z5vi{<61V!whRd-qaj-(D;6S&v&aN81V->9o) zC%Zm~o_s^XvZ0PA;gapxVt?f3U>>6e<5-Mu27Eev581;|?I@;Pv$+ym0CFR~cqatu zNvAMhfZ3xhHch}{J;v~UzI7^eLvpr}mHl1;k=90J2Rz>)BRYTs_B-^ZIjg-22Oe@= zy&(?~Io1DZTrI`$^?Th9WP-?5rtboG*2ILJ(GyZ86duw8XIfYwDdjWo6TE91OQky} zQdjSEjywpym=PY``Sqq^>L;>zCwi9U7qexxM*<&Msk4e$SNEl5HZv@Pcba5zv`O%g zgiL4vOF*>0J_ucn6)iA#FX6_B%z;xOD?R%F?J>C>(=s;F6_}y9v>|l;w=y{=2OCw?aF=%l6-p1CK&g;elLHMFkGVsDJpR8|~6W>3uQ`>qcRis3>a zxpzqf3QNJGw@Da?hsYT7%)Ae*&te8a_%+*pKQnaaBC9H=7L%V|NKk4eT~mQ|hMa(I zodypL98&JMez>=fiK#{D7WaT}l4v+q;DK5Yq!*Yza;8M&sG>kbdP#HA%LKZp(o-HP zSEc7}dbrZnF^Utgi<61U)l@028_zQ)(D45-Xj+J-dGEo#!7kw^N%@8V_tXLFVOhPWJBsiiX*CSnd`?P*N)~K~0)7Dwz8vn$#7V5o70* zkQ`3w#k3&;1lu0fUh~0na-g6~Voz=1ymY96z(?p`$mC}e6d!lt>g`AHpqR;3t`8BHA_Lq1jWdL#JfSW9T9|esqEfF$|5+FW zJs$k9j)irVYs?ru9e`8l53bTdqmueswSx$@-}aA*wA4{>Vv(_a5}?rMjPUc&Q>ky7 z$Ky9Tx{*FyUB^k0i{qp~gk*o`>QYGGW@MDU)(y;nN-YRDu69TrUE|G!j&x`X8xb6R ze=K}5sv;{+z&CbFhZEIW_=0bj)5+U105mM&o7swb#ZF$v`=$V~DpJ;NSms~=a7enB zdf~ws6e4;`fq;cXBiwp{a+uUtvdywQh8r5V;X$(i5CW)}cMFl`Mwb;9rD7(U7HT(^ zCq&fLL&X%?$D@Gc(THvzLn0@o_%sP!m?MflAq3qtrKM-=50CfdNK5I%QT!?$|4e-F zL;|xx6J0Pwld7_unhwlSgbsE75H&C<^$LMY`t)&C_Letw3XPSitVa5v&#>-H7u8`x zR)q*sDRq&NZVRjnmGYw-9B0PSYU0D^u z!#oSUf*>*m7k;)fr{rhhKibgLIoH7yO_eIiM!3U@pqvGHi|ZDu5F3E#45ra-nE3KXpzZ3QGl(sh~nYf*1-@|MBIG=Dr zWJbMA5<$3e>G522fHLlThHb^aj=3XaX}{;FzmJ>@pg}D094|z~cg#*$@Q8-w3Bbc% z(-jAr31eZ|iH%F5^baG?#i6i8^6mGi>CT2-vK=7Saj#Gea}ySb4}%jNJ+1sxE2NV& zJu~OM2z->uTeP7c?8nG27be+KLcA>->;AEC(+ltz70B2s4vcB4qjckJj4%5%>{rxQ zpBwr?%ai|#s$csP3oTlW74y>;t(i9uHLECkfQv;!M<{Usa4l-X4Ot~!A#rkqsu8on zd~qwNajRuW^#tKy;I%6gq-;!T5fI^h@oWXD>GrtBP+jfQP zh6W9+Z`V7ve%)I!IqxbferZnWq!sZGGS znO>;-UbGF7Twj-@oe3Kf$uK8ugsXA`5OLsfY3qy@!PlI_?!v*xli&no+zU|mI+G>Z zc9AA*tGQkgI+aJJoC8<<3c3Vs+8Y^->wO`99->4to`eh=(w=9hycGLGh+vR?$Djz< zFad~ApHtuhKR_mI5p+SygTb&?@`}%QWj2vw%!V$#+|&UT%GaMt7_xR3nZ#*WXwkBfzr+0H~hb%-_B0>MiCs<}nh2_f4HDWPVba8-oa31SB4#a5lser2@Mz)QFwr6($78v^!f{U!!y*1a;o( zyXcM{{Ms z??=^ZlMN65JWo4crPQsHOtz-eBW-`0dgm*KZ>e$sPs}h`5P1a-i-a5KB^h8ijWtPo z!aa)SPAHuHHjVygqm0Bg3a!(g>9(k@n0~dz3Q?7zplp7sdj;&w0`MtBjR9V$-?H*G z=4yDBeAW7$}H@al}} zG8wNk|gsDr$4PCk%-W>rO;RK=P4FsJ6*tO-$Pm%rw+hREBME zsio~>Jc0F3ViJXFE2L%oJnF_IM$;NeN_(iOuDGj^Mi`OJc^;9cxJ}VGY!g&wC!7#G z)Z04_7TS7)I%@?WyEEd60650){F$T&ctcInRu%O&Vf%=vxGyj>3K39a!J7M0h2bS| zm5oLq`JHTLo2R|z;8aHY2F}Mp`6$UXo_A?4vla4&uAt)%C9;*UYUmj=Nrh9jiu-A_~o*qJ#WwujAM)o!%qUY-&E z5K1{4%Nv&?8c8S?(1gf^&v_s=j{BRiE z=*;S3b@))h=>Z^#5{7lh_E+w>P&$zOThnheM1w6H-HQJoZ+#S+O%MpK{iLKH&X)lIGH%L z=Ble`0b_CpQjzBz<(i+SPE=g;VpA@YJXrO6^7N{WCA^F?VA=K;S{vCs2|*uuEb&q= zlKu$P`w|q0#7k&e1}6T^!-OAYaoYwtl$rAg07zp8on}`^rP|GE+ifw9DWy2jDbP_V zLLAmE1ylmd0vbFluW-#%Z(hYU&R+f$9)H3Ij(6)&VdHhwyFHx*8MAlB6z1yy8=xPf zEC3Wd6NAVZ7pTtkzKSX|o`3qMWp)NF0UB808+d61#;@D`idv6}8eA5p@)Q#DyrC{* z=|Z;Ffh~cv@Fs|4rmI)4_XYH}8xP z=%o|dZiR&#`j{TOfrUWF^W(Wj$+t92EfSmNI4RLmr*%c$S@{Hg&h9GiN%~~I6~o^M zJmlSRWzZP8>_ZLjkYWEd0T_- zTaDiUR19LR4Ih~ikl9u(e@tD!uX_^H>3*ew7I-S-vqwmI=V8PPNinFTA^Rge&aI9ac(A@O2=LRMZ!o20~a_Kn`3FddTzP zLjms*k+KIs(j~l2QR=JN-`X<_)nJ9PxC2U=0PfGC0e~}|^Cu_f`>b01C4B)uw65Oz zJoiKOs+x<9R9R9OfPqLK!sB6UT!TnGPPy(Z;X*`aQ;xW`Ao8m0ph0!$FOyZ%e>dwW z6$H9-724c(rMe1Q~#@zN9rkFYy*@$(btPk`yeQbChRDV@hLJxo2_B?ILxg07;kgav;7|NOFIWGlFE!` zJr!>4dEB_M8Q$ZZiL<1bz0SQmrszg4>Nc?P6IZ(E;hSJbXtYn}^q51!sM-z-Eiv62{=H?!Bpm%= z;mfV`Fln=kICUB#5tX7B3vi~Z{^aTX8?70gPb=~LlLWEx z;yTQ4#Ss=<^!+)$>l_Apo#xIU2rk@;{%hMcL0Tpm>nOnnl$_+iGt-VB%1eX1n!eA? zdtXkJ#(sr>2eq-is2LoP0EJ;}=I^5FjhPV)S0nj#f34)n(8u28srI4y{kfws?tEEC z!mMSIOSDF*3X{$JDzI;4oCAlZ04l`ze%R;nS5(2USE1C|G{+aeHwN}(yGLA02llx( zOX=#XGV5qf~H8S4ostz0x1F-$B!>IG%<}_)YVbE@oGgX@U*E%MiOsqaW z*+$kgWW<)l=VL>}|BICI1_}@?W5}KWwP$_ds{AvuuiSCp4Dea-GBhf0^M94#+@LCf)Pe!9ur2gS+n4mKS6_ zzGKWCHG*0N)W#Z_kUc`G6vK-a%GE%@2S9Zj<`KeQ2pE)pP}~F(C=eu%S&kX=eqRhm zF^o{VZ|woOw@i*qXx2_+zoY-Rj=a(onB-*=?VvdmBzqf%pA5_su|RBX&54RTx>lOO z^Vw5dV?I);SWmTvinCsS7Hp``fMbSJfoIS+Gp(;_iDfCn%Oo4>jY!Y1@2wv1taq0B zBwU)u8WPoKD)%;xJtfSmq@zM~N2;~}oPG&qRWQY0OgIIYa-38Vdyz8)as-MJAiib% zD6=rVsEQt@s0qji4|}2d2k8^B`S2u{xWgG9DoR7&K5;Ze4X#n1CbK-EM!Um7N&VC<5M*NU3#T zKX)_%)mC`lK(GvBEkN*L3fEssIAzjypTq#Yi8@mN?^ZlcOTj zyh5XzVrXUi8+aroY!&&Bw38D z)%#Z192rZS#17P#m;no0x!L?ny0t=>8Q zNuRPZqP$5S*Qs#AOFuk_>mp`LEjZd@CkRYVpGX6W9dio+Vbos8RjJTZx`mbO1;{Kg zQ*(fSfi{=MfK2ZEq7g9hGO`%={U@XTO*(yJfDyL_h$+~?!F<7#7_>pAE8S6zQ^={ zB&FJVELR<>z2Kf^b<(uUvICK@ESM$IziT)uYKlE|m+_Lef&Chw-m1c?-M=zs7)doi z4;+t-C>K{q$%)*C10*$zq9-uBxS7k2+2M$KyH#oWe~hsydMs3bFXxb=%=u~d< z+Z%e#d{Moql08g25Xtl`H{Zqr3v15FkLzhb zS;XXTMR>#hU1E*%861pjg(W&Hb@sqmXu=%XBG5F0eQz_k7 z!lghA`gD@bqY=C ztJX^yA`ImLXhxKg9~dtbGU1eHGN>YHw0}j_i_~UP9qk%x*S-buRdnc<|90PuNvuZW zc`CW^J^5ZAQ~yX}!aIPY1IaFI_~_6k%Izr-M0wS@81}%WlT;NS6A(fB8P9-YNMaW4 zn3Ux5fc_EB2H}}unoZl9r7SxrLNpRk7=*!29^Nws%D~DD#p*kb(Q^|vh;)i=iwZ6F zky%)#Jj97X=y-BhDAuby6pT3$QNd$b1MtGKf#qW&Ev*D#B4ymvfe)uqLJw?ceQZxe z{2R>&0zCc;K$2D0i5BC4l4r4`0q99zyFH6#!52@X-EJP z-^RGl!Q*WS{TH$$LF2hQgW0h8H+x?mQ)KQzNeG(P!{jcQB-dnvIbcU-1`EM}WS9|+= zKMVt8&j2E5t5<^ee|L|fdv|V`shOuG^l`jC2(lq+PIVQ%52`EMN$Z%mBm6j7RC}Ra zS5U?jW2tgaDg?`r;Mf7}E>X7rm*Ph!h0{lDOY&cayef^ns&o=8=jK8zRp zp9k+jRn`v2b*~WYy7q}J1hD_p8^AM<9RgD|fSCSwlnUgP?_!$miKMU1%?0dR)E+3q zZuE~*&HGixh&+QBy(9C8HbhKQqxQgpCYt^Pol_JQqP&m-Zo8+VJ#rV{dcL>dIF zdJ8Ao+`nOB{zs*Fp)m@kxHH+E-2SUCy-ZGe;N*TSF4BQyw;n}=GFJ>SLs7l9^XR?6>t#wip-(xmR#+rr%ZG^H9Qg2io=Fh37Ww zQqoke(hfZ;=7hpt?(Wn-=6hL1QD{_*bHmjD#}?3Q1>{Rx6ekwCO;eSajLSDDN;w2a9Rn|_5e>Vany zL^SCUpu^utV+zG{B>MKF4+)42#-r$)+1#-nH>KClq-EqNURwkv$S??SOk#}`of|ka zHH*J~E&X%+Xn}D;a2w1729mr7kSMJN6QL)3E4C=`7}d#h(79ke4*@8VPQ#b3iO1bXHYF&V((>)5^u5XnDUTvDbec&$6J$b|Je1 zuAJo1o{zBfxS>9dx9!BDhw5(SKEu#Kmxp5)H#7qYyKy(}xwnzSE8}-mo^pR7znR{1 zrtMtkFr_dlkM(t_5xYyFS9w%I5o(L~AWsb%FL^>^YEvFIEBuPs4c1}5ti_S?%aITy z<7CUp5Ln=-!remOP#5n~Z%ee+6@AA7CBMB_fVeXO$dalco=w@8&lj#l#1-#<*_a>g zFBANTNJT1W&%yr;QBPMgIwznds1$sD?O!uDPUsWlw=T786WO%2mUIdj$1pp$ql|+QlZN?bWWW zVIaOcK1C;Fr5|86r%(ru{(^uJu1RJd{wd850k`N`bIgMvleXxx3&jp@uU}0sbB(u= zOrS%aO`?iRPiG|I;(P9T5j~Ru_A0Ud*H~6o11{RMxeUTzsL~XLjXBi#jtsYWjLA@2 z5fR~8>BE{+!l*#I1(enNG8~f`nhUa&LiBMcd~@@pfdRmzK9thkv)ojYNR`8-u1a(_ zBKXjwOzlQ_wa{${YQ%Lapa@L#!nP~I+82f*syTSX(4bQ>s=6I;WGOwD1we#|lw_en zWPd{8aa+vqccsSU8iOMuC%!5_Bm7ajLV%EkV7TYJOl_xxjZ`tXDA=PBDqDc~j&>MO zWaM-gKtenuoE66pw0fMGJ)TX>()q^?4IV;VRr|sGeFf@MN|+DuUg@m;1ni*SiBu8c zhoupr^xL|q^Y$^au_`Q})KVr8*Y3$4v^6@s&CWHn-qIatR(W$y7&C*?S|)%vv@J}e z&UF_@?|ytFKTT^>p`%p?+Mg>c&uc$-kywRCk`3;HzwQUBM*;;MyeX?`M=Cb^x#6#E(6Bi+x83QMG-@@8qq&1wX_ouw<1 z$VE>V|0-3w=^+~D9$bph)T@|sF)(3HJPOg!<>o0Tfu7jwRhQ~a&Fu;*tv3W$71<>T z6`eKQRPU!VV!l64OnajP1gv~BKQCW{b=)sbpA5P-QYa}vIr^5L2M-mn*VmOt?U+Q1 zHD{{Xxmh;?Tj|UQO(sIMRq6haESnybfnn{QZ7)z(0S|c=nP)J~o8una`{3Z%k6I-e zw=#(+_uM%sU@eh5`?Nu7Y9{Fi`j*HsuSo@_gPB*i*-j^-=~iHA!YRMVA}Nfn$ykUb zRhy`D^PtpV2#V%!5 zZj0kKzO>^0*EuF%=y(+F_I#TMg38PYk~w^Gq*#>041iwZXCh)a1N{lO$lA{kA?i9c6SY>P5IG+s@>DN%f=@eWeVSJOGZE0+ccb@rhsh9Wu=rw_TN&z1 z_A~XBYSp&hHHct&dR`~c#Qqs?8d8GCF1NCKoy6U?#u62IIEdKN(Z+Pa@x}@>##7ut zulPn%)sjw-cNK`OpBe@{!x)Wm!{$;N`j{*|M)mtkt2;FQy)x$SE z{CHqWXw>G>d2;=0rLtmUTbe*(Yb}(Wk8Mk~X%{MGyeo{rpQS@|1CDa}Mvy^3#E)KH zmc1WAT?TlO7RKJ1Mr@r>jmG3e%AtrV!*@D#(hK%uWSvL%+rO@L7=#Z1)0W?xwqk01 zx8nb~c1WKZS4>1&qLI=(10mNPb_-lD+9mI59~>3EQJUp9ID!& zZU6}%lZu%{y>rEFMIV>Da{x1NqzT6?CYBi&h)|C0UjSK1nA6<)J%(#k1x$yec8w)t zczYe@BpPwku0EOcz35=AsLY@bzq)M{fn!?pc!OvP!H!tU=C7(Dc6yu#32CD2ii9zg z7H2J2#I^sJYiTh{n21ZS-JPjo8eAp-$#$^a?8oTRjo9VufYo0feO{#uggLn$FNqM1 zoeKuD(7cCZt_#L6<(&~52ceWP_PTJHNAe_=0#uW&#-fmNV<=?&;AH5p><~L|31;h7 zh9vW|lZ=FT!RYy025DAfD)StgSg6OsT60X%{XMB`Kp~;lyERb*e~oeae{BulH> z?%+LbO~wO6oCI+{RIdqII{I)FUrN4e4kV1Rs~4$U#qD8a#Kb-;>dbWLTk(s0On#;& zxyZX%@aR^dOx)jfz2$kf5=RCe?%c>zO!#4ni5xqEl#t_|+W78(ae_z=dQ5}%ze-}^ z)XLCisJ1xA3`Es202UC@EUNT;Z{gn^AVxO){&K!t!bRchis)K2Dsv7@M^It+W11H= zz$b6j)s+Xh<|OZ9q})lA6zJ8ftJ*dPy2%)|{fjchp5ag^R%@mY5^iO%OUU>DUnSt= z&I1XpoNiV=7LqO`7D#L*fMU7kseCdSYoW>j7+nh4fe6tAi(OvHH-N-SBRv1y-OuIf z&XX2Ih{&7wvK+4&q}8PEm5w7W``>IcT-6(~tO62&nXKhh(G*z9brS^In025PYs<#x zNf5`yTKgGzyveKw@CfT+u{%Nw<0cGX6fbJkk~dOF)CM%0wWiBavY%#pqsOiBQcvf~ z@tZ&Txj(4{?gStbH9A=lM6PW6+!s*Be`84&Hys;CfI4Mpw9rUf@IL?`(R9Gt$+3}Y8*>6}YBnZDb zR8|C&vGlrX-`Iwzy(ei0G?Kh;3)46X7E@_Jf*>MoCyIe&<#(~_NxA$x(LMFN*E7Y` zLKpdH!mH%kt6ajRR853DpkPicI{=ZuG7a;nNt+k_J4w;Sj1QGx<~>JK`;BpLuBh>y zdajU5ALT{IYC)C&S)%W~BBwU#@eh~-qEO0;RA;HFc+k$qa~KpPcNyL*WW7KTfqYvt z3f7trj8pOTNRi_}rjfT3R+`VmI~@<{3V~3fj03ZRd!~NR)$g}Ov}T;)ajFi6gZI>@ z+< z+a6~tY8=nXx!7M3ka!UBR`Y{6ipsA4-g?$Y{eX#6sA%go>_Tud;~Rj{b?OP9Fm~#O z1ChrccYDO`zia82uf7}^m@7=!&kh(+x%9Y3Xt^%hrHEPICIBc^aH8!sG0vVb4|u48RxAOQ zIZbsRqj}h!^CR`B5bWdlZ2%~~hs1>bGPO)VBY3OSIyd&slwthEbGYRG=S={@`+0~f zQGME5N1o5UMmZk0iH96zQRztzekaO7!{$lPM2CG5o1y@cyS5>V zBp2(jr$XV=IYC05y1Ku(p^2+M+r)y5T5La%JQN?0PMW-q^wGg!iNq%1AKJZl{jz7c zm+>D{*IAQZB|HI95(5C8ScUmCTUrXy3U?U(i(jg9sJU9nWBk$a{$;2~gGt{!%z2(iD0mDDKbXty#waK72UwwMe=vSjA+A1BRq+2Q*Z z5Du$VHQ=n;f=49Qoz}Z)zj9bJ|?IlFlwJ0?nB}kKb~3OJ26+O0AKW|Bfyh$N!9QFH%okGEYV1d$gsnut7*Ob{`H>p*0`&?MhU8eF`UID}IQig`o$ zM|*4;^bSPcj&EAAO_BQI7m9oHPFN4TrnMRvL!po$XL7s;Zg9nr`zO`-5qJ)Q1MW|Y87R&?s{9$zw?T6t0#Mji+CtK7bKwYQ>th~HofMSG;xrcw1OmE77 z_jfi9QFo*8*0XGB)Vf?Jl7{tQx%*;v&m+!D{_4l99n@NDO_RutV+GFG7uk1is&4~k zgT4tcW^<+Q@pe#p4Is^Gs_lbNjZe-C-y88;enXG&GW`NAfBN3Y0}_zL+LwfCfL~_t zdL!Bx+qIq5w^(oUFU-X@0cVr|+%$ET%$uejfyWP1#x>bw+Zyiez{@8L`fKSUBHjlT z`L?g9EdG&ir&~8b1nO3*lmmpZ9DejnYP~z*9h+X#7-%RB?+xHkLXq#{Z zB8eyv=@P)mYu^3;Gxv9EwyP}HFbbxh{{gS3B5Omz27*bi8uNZX=QU>aBt}i5H2zif z({H6MO`#830c%gyg>nnEPg~0~h{WC?mhsx+#!93MDF+k0(~V89y#){VvJw{VA54=s z;6b$WKO1f5{o7Kk^L7w;eADcMvJydsI6+B zV>_Xoh}$-^5llq6Yv9&paO?&J`W+NxCtzhDVwFVa^ipMwHdYEG9G4oniO6d#!n3sC zX~nE+D-Rs{eup!xrX%Up+SokNNj)GD;rnF107M)YvNL<0WYEXJ;_&677n4dMS8t2B zqQ?yoVI-XYPo$4;ipG0n=N*V#gMh0cz3{Ta{Zp>r0*U{g;k=&^Q7t4WW6P>z>Gt8i z_~ghYL@+6axa#Az*WjUl-^>vhXZ3!Pmg4k7g33g>if0=qzN5W!Blh7@qU-O!D5eGy ze>SU`^(!J=3~=D^Yjvm%|(sxGyjQ z7%+%#1r>1XtXFJdS z9s?Hf{QUiS%h?!+KveoIomyWTv!`wq`e5^`>jeA_M?N0(y#c*ln{uIS_pvw-x#_yD z%}o{CY8JZrx6-9U#J?Ao9VbDFBrsQtesC1`9W7*9slKh92~NDgWXs7|%pIMOaV$}a zN)FED-_c&T;8KDT`YZDtRb$GybQ!#_f-z%VB~}!_!(M)u^k=l3LWy`OWXrK>1USkN zvCgid!s9Ofgna{9XQC?ckztYbXAsGKg&5(VizG^VD5IqyLLjv;j2Co)!@TZbE{@a< z-es$X@i1Ilwo4{r4V{2x3loKjsLyq*-g2#7bl+?H-6-)m61A<@4Jwhz*z0~i0}`wqy!N=A68kVMybX$ zh|c0)D_cVb9JpY!b@Usd9@TYO@S9~`m}5Kw$2^%?H0T*__@0h5fuS6+kMZgf_#; zN^O;8to_#|d)}&S{Gxk)-ab`aY(!K7Lz-Z756t)q70APN+RDCXX8;&!=M4>Kxv4lx)fg}#!CuytK6}$V6)gpC)-oE8go?==YsqM-Q#zk|M0x=u1 z)D=h{ql)dLzGVn0FBs|5+YA{ZD$%DiH*Ml(Lhs>k1v9MEL z;fQUGFcG?TS)G7PtpbngoE5cY%$cBnpFFHk4~O;xXGALYO+U8#M1&t(@WkJht2yx4 z>ajnE8#{IB11x3*<{7CE$4uvcR^Z)Tz%w*RSKs(UjX#75y__4|3{<_lsWw#hb##ZwV)}*_`dq6^FR1n>hIuZwhx5OX(?rVt!Q#9XhqtZFcyl zQUvad7ZaTep~_074yVU3@DQk1jqVJN`=n4po4QKV+AIl1qeI%O_c?Y0Z%SY4kw-d| z96Z?4;aqK8yBe*%%8e6w$Am;sSZA4ZgF-c=3)YkP4l9OfWWacg14&bbhXI}>k+f?F zoQaZ%wx^QF71UY3DV;)A-Z-9kqA+fMaCkA%7B94}s z*XPtpsSAZ@zIP%p(kZum89|7fcdcb7+ezJ1U?-Iv?+9go3-1;qa$b9j@!F}qtiD~F zASs%aRBzz=o~+oK&#qM*&u=Ia-pkw1q%36>>i(BQ>&o<8?wChj)nn&PN}UX*QRH>q z1J~5VdO~G4j2|0G0z68(t~Dlx6`Zwv|jEN!bBwZ;OZ-B zrMiCumPAbzUF^0O?k?fP)aR=({6)#QD(Htl^X~>8ZXURAVVnk`U_bBS4!99N`8$2a2&SXBBJkz*05Ov^17GknepdX^gI)*^KVACnT)EBV7yCg|EtdssJMX z2X?sB6G~v0{(N66QL(UWZnC~VfQL#U+E0x62>B{-V8c8ahn*0TY^kG>S6ei-j1bs8 z7W}Dz;b=G16*%68gNt!-A1VMx!F@JP0u>|C z1-*DW7N(Hs%}>nJujT6J@3Y)Kuo2i;Oq26O&SNH08+9fz2tbmJ*~U&akhlqt69!jS zXjYQ*@yuKMhXM!o=6&`fWz=)ocChnAAfC@d3D5YY@#ei3;|?Mz-=xhoS_ny$s*S3i z(a?>RJb27Y81u2sr*$MamifQkn7)tKm?XD85GDXbXhx31tI}o>Fsxme3+~yW20lf! zvvH%DA$J@%ZZnrv5D7ByFtW7Imqt8dYceK<;rj@=m=7EmwiT{!LsAA@!GnlM<5!`g`7Cb-Es47=rg$HHjkx^^NYb(QPW;ldY8!Vrdai>f#dA+ZG0tkAZ_BPTQ@S&V zBBKk>WocU@1@-X2(gipc>NxK>GwE&$?sM-))R zfroLe(3`6lrMpRVIMo7pk+tK*T(LiuMo?oeDQ3TtX0~eJ>iDJM+F>>X`Q)fFqVJ zvM~-aNv_Bq6ge@*y|m&}+q^r^zH!yUc^qi^jipWn*RorK+aM!AM^=p1^*J#&=7_xw zQpnqTZD&YrZNA{>L_ro1BHbI&Pc{Z_--F7Fb@W);)u`Xam|k?0spPT%Kl=oj zvSb?Ovu$p*%ZsnT?Ih!5BDvS*I*~_YBA3;di72Kz$QoArO^Yoehl~5U8@t(w_?nZC zHmO599o1KGsGS#bfFWQ*iPDH4nI3f-(g+YvSj?w)36BWen4Mks){L}!mUxi$c_J=K zv@%2b)3cau?8Q*WTU%|J4xE;No;~vEHAD%*5whyu38bT7=wV+H1%OBPZO*n9&qnLJ z*SH)H4x|4pvcCFen-l``)CR)vvuNlHIM1b%Q0q)1kYapoN{S2Si`OJv39mx-SF<>I`+i3u#NX%iuO2w`rw6pj1>-}&d-C;QwuAvG zh`6c=h-}T#BAEH-5z^Tq{4_b3~BxhVxUxb{zP_BWrOJ4V6AAKI| zYQSx=TIq7zJLuAlrnT|Ei(0}z#Zv8`{$G?HBq4oNRV(dinV;xgsQ)H?e3$VrY{%Ps zr#p$7Fy|#q!aqo|MP2Dqghe(%$>FT=-AekKw zd&p?r-YM_z*02D^t@n=e^BktI4s0!cGd&X0_Bc%R`Tn%8NJzHD90})&X~4JxVHy9z ze=B4TJAsiVi#J=(ov{@@){W(+G1YDu4gQPe8)8KS0f7Vre*Wv-zo*qlq$T)fN23DK zhOTYbN&t^_I@D{~h3!5YCk}+r&f@1!Sp+60Z9hY|ht}qUe@kVYQwa?PuYL;>eUEJK z)RIx+Kl1%98o}{IsIiWyII@x9uCM?eBq5E>2}E9lN5$kN)xKrcPQidr1wrI%CNbI5 z{qEg?Tn~Xmf|3z{$7)jT2LK-PrC{6$!+HH@05e0Py8?mkI7UP?$1{le4kQi~m`Gi* z3`7=EY#(cJuYHrMN)Sos#R8ELd>J}{`&7I2oi+nWrwmI6Ggu_FpS6p0-} zyvb-O2O=TQ5ZGmgw;J#1p?+F%%{wB$*0CtFxquy>c|O#T&nKXSZ*hx%=lI_D>AJD0 z?pMi#CuDZ?k4E*;^AMiN0;F?Nnwdacfve(x!Tzt|CSbw6$wQ%?x|sVGhSs?5!_ zV(GopHJ>-Cv#d_Ao9OS6s(sw6?+ibxSC{eDh92Ydm^w=q=MdFeGFcjKt4-aBO=M|D z(ZD|XP^~NOc18v`o0nF65%VT@FR5xly~o({7tvI4X~evD9Sy$#tlWYJn~q5prfQqd zpMb>iIg;nT+qNc58YjlOO|sI}pD=|CLWu6WP1zl=8Sq;#*V-dU+Po^?`_ zz?6(2lQO>o34!tXDNe`FMm*!L9EgUv|GPAd?!MSWY9tq;W{i}WQDppILmcv_SoN|i zr!(Q)Vcw|pq0jQWNc4y8Yqw4dx5eKUzPz#j1R}oz3nH@%IY2F*`@RuG21ngyP^5A6 zIR0=yraSkW`HW1&nErj#x$tIBwuFW{>$7yaBsbAa>gcxJMWRY;LL;m17Lz`Q@A)fy=V_IlJb}mCHNA}LFPO|7OMv5hl%9wIWQnl3-|NeShflFck2t~YSk z|FU2}p#?!Qxou^!WM?x2dY0E`U3;cm2qHwa2pb3I_R7FZtnGN!*dX*Fy*|N2)yh+1 zGCZK0m_9T`lOc&%x|w0n_U$lrh>q_a>>o@s#rd*m8WBzwVW3X#w@;M~-$3;J_-QS8 zGgjO>+q&{YLXgusTgCNl5=<%DuIA8tDH(PA^<6n|;Qilaj$53i#0Kl$%Zqm1F)4de z!>Tc@^~(N~P;WF;Kfn77IaZtd-IDgXUfh|$>)1NRvdNUqT64h5Y;FouGLdwiW=rf; z)cY<45gC1+z2!!+71Zf=|9(|8Loc}9nn5`kjZ4>YkZ8<}7F}I+&)^65;@GQs9);3F zhSBI)klho{Rqn<_17Or{qDJ^#645ax=j`{O(n!?CKmXqVdq9N0=rjgP{m@FM653!2 zbytl@JeAn4doowSPFoMMH>dn$bo1?m(qK$+4~vOp=K z)N}aF!p;oYNi8}_9q++I+&}vT&7bZ(UED$bZ3~{6a*icbymF%Okx);ccjtIt2hzSG zN^vqzh*^F{SL^K^+7ob8$i|Oj>to$011jpMKnqCrhVVPWx5Tki{_gewPnb_cCN1H0 z%(Yjlu%09nOe>-TYt&55(|aE|UlJ*#np%AEat}KM5@s7tRi*07UTR<%PLfDw19?+U zz_N`CRZO%4ukPnz_7hlSeo_I5T*JG?9@X5ZCKyfKGwZ&#I;pgpW~G&6GBVBJBc9hG z=!=Ok*bwh?9O^s~IWObp+{emZnmDP)0IVD$g8%`xfZ6qi3U6p;M6hw;5!f}GcyYGIdfP}{Pt<5mg z87f893*ODi%;!&AQ(VHN4y$#5z^J6=1EEEbZNeLiGH11c$dp9{LX>o_Cqz75&vr7T zoHu}i$1VthN>t{VGMSMhF*=-fUUE+jd{twVufSt5zuUceGX-eWH+<8AJGGykAL;O| zeBJpgg2!v%AXsbS&Ya(I4vialC|XbErc=g%60r>8I`Ty+bG8GO`J3dqYjMQwLs(+x~UTtQV5m} zM4AVYZRf%lY2G;`QOsVRj-ANngdHr-f-%fBHEdRGZ`YHGx7_sMYwaZx77r5oXfMfB zT}Ze!)w^*_a}tk8a+{$<(67h@8eXYSHf{^JZJ}xR|H#Zyx3QENVALQ&<|Rl35tQLw_2l`K*&5Tc6FrWW z$*KNm9U=p{7!M)lq5TpREGW()JAasjR zXu0!dAgGd!Eoe3n8TU+3P*6d@5Rmq4qRsCJDNcxXAVE=D!a2gWg_h*S@d4}N)ma`y z&bKelF=_R1^uD|U4?ma z0}y(%BjK0304j(O5z#G!h##boHi?$SN;o&x$sN-msA?C<_6kYcb{<>a^;hr8rV10e z9L!nWD2=G0AGwI(RdG@8_r}Fb_-!1;z4kANrCLTDuQ4qkBE`bbBJ9dd@%pQChzP-9 zf=JiQpX?+PCyD5xacn~dQCDOP@PlFa5+sCi9p_B~CfYZ=fZq~yyB8WjMnKE7(cQbL)+bN65u?5bennJ-0f!eGUEt zRaR@)E?Ed|8*gPLrj@7Ajwf|Q`~hi*VXt&0`U(nQ9Ut5hIS}d74iTjdrr~90oD8qY zZ{NuFhbN*bg5%C;K1KCNr;-JOIzV{|CS^oC$0f8D@nRNuzi+!juAz6*&y>g3pW(COq81G5^)x z54YhldsJb)-JMV%(pk&v+*u924A?!!VRIm|Z9b)wCcIGAw(jwzGZ1k@KuM`B9If8}!a?m<1&@xO?8!LFTsr2ztz}B&@|Ba^ZVM|f|PC*`}lDGj? z42Rdtlc^yqy8Tv-Alnl5Fjwfxq(L~D*|?l6Hix8m>vZ5gKB-A zFM_RohP&N3F^=Z(RX8bROJd@FS0F;{>t@W4@d9Ct+4#84;A392YqyE)Z6SDs?ly46 z^Pl*@dS!q@GQwjCEnOqBa_j>PgiwhHcsF_U;j?Xnn{D{i@9v+F!{nGmqfr}PVMZ)w zTM5v`MPB7h(qT8pSx;Vb#s^?9n-XNE{ncjQ0Hf=5@?8mdToeQ5Xt~c)(%(E`1iuTl z#v~I3Y~v&%>Rk=#;y^+>5uyP+q?46gbh$k@QP_t)d2al6shEywW#%o{h@>CG**CVz zJg=A*c&u~t8y9?!eM8>A)m!z}8rOWMaf)v*Z2aj-m1C&j6T-Ykp#7oJ#|-1;MlNJ0 z>nT6AnB?xB%i;0tj4PDjxm0>_^%rpA0=8qODYo;9 z*^$Ae_kwv5x(AVH!zi!mScquG8TADYFb`6P1YUpheL}vQ_(~R{@7?%|+}#{+0uGTr z>d)~`-hs#X9=?s(=JDRcwa5EW5DWx5tsgqeo@%Y#d~lANCn~n)4MZF2+xdqFDV1=LxiCGAJtJw#xOEG7buStNL2 zx!f$jC>a-RKa2g*BX?j2n0;45WjaTyyAq@slsF>il9dG_v*I)iN4GnMKtszeo5gkD zWSfX0af&D)LG+Os>R~(ERPun|3L*-65kw-fH)1^h*8zM7rzC6G;Ce|kL4=41q!IVM zC46hUZ=BI7t+~JCC!fLmVbk*xaCG*KCy)>k;RaXD5w~Uccmp0;*rtbxeD)yma0QqM zNBrPE$LTrnkfcUZUI0c`UbMX5Q-Q@3AR~hG^`4J>Va8a%`^;My6-pp0&btaSg7(}R z&{l;ET#T_1N>0_5>}cM_My$HZ%~?R_9ngjRgcwZwQoG20 z&#tn1YybW{R{R!Cuq5fvMMi8$kvijGkOIZ%d%Z2|%t-E4|Ke;f%Q!mvWegk1(+fvqbA{wH{> z05p+r5ND($%QjNkV$jZlKoSjv==@;Pqk_EL#QZVUrakYp>CA(nGsfXHZ7;0sU=PdQ ztd7&iXpe{V0KcfXvl~f~h6I5G%z8)|mC;z|GEzUsC+U^1N|p&3Fw;&(DLei+sStYv z5_W%%rMmu>TxE>SPGILq5=4osPuIZ$5hBg(ppTh-F;<|V@b`gc-2q%0t^v7s$>Tul zGf89(upUF{h4ow9=zXLb$GljbrWDebG2Ie{WqWLN*(OI^Q1SXn68ZH+yJf_L%nz?1 zg6{^2Lk1#<077K@eJp|soNDsYH#grGALgF<%P!0&3?L#hZ3(QNVn3VBqyL}G?!+dv zs>4QP?YLh-$w`I^kUT6rG6}BQCLCad$2mz^F2g}s_|le~Nbm>J~$Wfh@NFlF2&;&T;AP_{X$}J{N zB8b>A(bU89BF1qB&6^ZtBxz$Cv*eRHK9fyI01*TNq7h-kL*X->cZNJkUNpdb6Z&B$+>+z|( zJ!3|Ev)QlTz(}oS8(NU%m8#h4I%DtAfl)GJj%z$$DLA(??=&nil8NojEXi%DwI0yp z>%wykYk)hW0^f_*+>_-!l@l88u|7kHQE4F@SCxHX9PaR1xhS9Jn8!;N4D7L(yF&2p^w zNZ;Gbizoi*d!zGezrSeBQ7Rv^;BI|h+U_n)nWE3v!eU^IiVg)0)5{K|lL*#B;QyY8 zd-8eLwG()ht=0PgX0I&+5hkJ@fn+u>aG!eX*}0bt_So72X5QU5qx)Oi#;M@DeUw0M z^voe5k^v|YM6!V~UtXovo4N8EILUhuS?v~qg|n0(@~tgI`wJ)i`j~bO+yOAOFYO;;SxavpO4V+O+=kF=#GgX_mxSMnmPpbUmrJ5 z!}cWZ%KaNy1P*){`Z~31xDGS(Fs3t6`>^xGa$Nr!w*9qpb6#p7C#tT9GP}o_W_sZA z7gF^Zd(X9biimi{aq&_M#``7yGLBVZg>+m}v=(nLPr+DC(w}wF{pyK{CGZI>l>4y7 z;bdFR%4Ty+dU7y`+%dhPviAAQFoj)QQG>ZmrqQe^|aX zs1xNtl7R@pgjt{fcL@6lHLq(*5slK0eMD(R$D&fm5tv>)9}}5T`_`a5O@R+|i-|}Y zo8%}$ORWXc2=4m0Jh7NSkFo>tq{h0Jf(Ei)bZwHSe80oyZN;f;-&JSs zovNfo@D3&+6E$-xGkW0l4J1~b!Mv>N+NK|;e=>h{XF7ZjzHGGo%_1To^kFEOx@uh7 zA!;Oh$Sju-#`U=mQ_0eC(AS=KyHJeZ>i5S12 zwmre`=z%1UOYpD|ouIfF)4KQZkzB3FYFh7Ti+k~^B4cSMY0(R02nT5h3{pn3h)Cq; zc>vX*4ka4dQ;#&D%{X2kNcg~+M}``|Ky|OGyF_lHPINa=vFMAP`tmzSBH1*E9C|z# zZ6%gPH*AkzFmistgdy%{1HAi*g>WJ`CyJ(R{R!k>oIi*cmW<-{DxD$ zjTp}&1kaE#Em(%j`pe!^=RZ%N5z7fgsuFD` z(qW&FyKRAc*nBKcQi#>*9x!x6rgVSu(7oiR(`$}EXqI09k+`PtIKDoil_$)GCbXmw zAt&`{12#d#=h|tX=m|9@;i$T}83mDQrs1b@ZLQ}(dAc>n3jXL|YS}peKtZJ&OZO6RM*8kSMN;nC= zUkN1ZN7q14sJA(iAhQBs^5l7gZi8c@+A#kx}68a&M&mgWA z{MU_xF}i~9$tEaf3!;_Q20+9C2QN^tfKOhW{TlTHH;T1!E&vF``b>*+)d4A>n=}^t z1cu!a8?23!isk}_wl=~f^VNoTnr#eJl~LC(2p7K*9>jLCq%pIbu#3(oCa@2Mq&@C2 zD=NDmX5S#K40qsnie_7(ut<@n#@sEl$H-ozsFQ>4Fla^`K@h+esu!|EdX5T zD%{%243Mj?I6GVq4&B6E1Dv`CIX-wFj7sXc$_ zK@D8?q~(*9hH|~$IK48|R^$BohRa^KfZe-T>#zpU+{CQu-2WAb5anO}Hw_=!%3O3$ zzS0ULH;VO#ex152fC#2EAp*NVpwE5oZ=tTe#%(IWnDwq;?;xn>E=&;y)n9u4BFz0y zYeOkrWZOsbZ3|W=BHc^n;*0K}z9=0XR5A!4LZ6=@{Q!s%kpmLcy;`oVaU~Oepe#UO z_nTb1@nhrbU;t-hAo?QSX|~(|Civ+ikaRscpCkwsdz-TAVV#&_JGK)`W)#H|GvR7E zX|%UyvUYI{_P=$Z#rBc$4DIbK&jMR#rwFf0;~*{C&(Vp6{%J z!J3O| zb>3OL#{t|Sc*faFfKW#%#9WXUCd^xWGDXDzltR#bd3j`s5gRQ`G?F*2p^(6!m}wZV zwGSd<*7VS~&mLU7BmHL}(gum%;9h8FRgq<$DkcSyKI1j|w7CG^G8TpsVWf`w5|eFi z-Su#A0*;->1lxLoXQQdU0Dhh=fkeW+MDE)!W?-3{qoiSDXatlEp3e`}l{a8g-hlUJ z-$6Vf!l?eGE}L%%Q3H4Tgh3T7)lovw`}|kr`e9vQ2HX6`c9vacv?oNHPcJre+H6Tn zoe87^Y4||Oezqm`fb#5F(rK&11Rq4X>=vyQc$^Q0hCr6u^m8wV?VHR-JZ(Z zC?;PB(cevW)f&-C)EKuP-pT|K4NRmT;DSCsbAo@Imx+0h8P`+Hr6AGy_>T(+8a%*; z6zmq({uD5-Rh>u{Q-H-w8mo;(@Bp~j|H#&?^(m{mclkcAlQDNPjsA=BuyB{&JVf~8 zy~qqDjc4>C8-{aa*jgaS5mvQO9U(&is%=30Xeo00mWw}+gJjx4q*OBnS!r|;UrcR2lM-C6?y+b{J>j|2 zN>znq&Ok!)pHojRo5%1GEi}r7x_&7q8 zHJ*g*pC~~-KA>j2*K}im>lg@%_pBI|IalWkA3!i`7LtcM56(kzoxRRy^>7`(wMh=} zI^Gt0b-aFi8`KW(-kD%Ls{zPc`-g(a+xkXq6T+tZrT>e9;8BT?rT5h=rJ$hqK46}K zh}*^Rsd4YtdrPN}?ioLU&ku0v3@uEx2*yz`)VDDd1>JmcsH%3-R3$`Y=}RuU zhD4&dcQ$ns9K$!c+cf-xeJBGDBJw*^>M=ZDEFJb~NfY?t)QDiBG&{pXUebc_NUM;8 zNbWV0fk^y;OV!`Ee%m}Z0LlRaOGUhFv#&1-N_V<+vsNnKp{7*IS<^GNW?gxfRRXcj zzPq*(07rdnv5!3cm+_!vCr0!zVO+NYa2 zzVE}=gf_t4CF6#w>EH#XyzR8Voau5makyJ(hBE=+()zZa-?;C-DY4z9zG_5`9U+es z__x7?4m@!2d;wq@Pf~)&+wz9s0Ej54AV*<*>8#g2!!xK&5OM9ODdB|olc z57Ocmhn~|UiJA@cYDD*nLRZ84$yV0;{I<8yeck35P!$6aI3}7B(P2S6EtZ_$_hri- zHowDs68Z{}tHPepngo*$3RGZ0+EZylCasEu(jk=l=Q#6Sv}8AlU)~%+_Fg=daX5|1IW2F(^~Xzg1<61)D;dV zi*Uf=eOj!~7Y(M1TJI_3(HI+EfF5USt&&P~jtxRKiJFc-YCHfuBF{1arOgDE5+sq; z*`so8TR>snf_Nr*l}&QP_!3Q-j*`BOEf8v(5=0^67jZlX2Rtl*XPF$q_ zt~3zmqjXXQ+94P$x7}_Wg-RQ`poD0|Ra{Ir4RwzLksl#;>Cs0$7nDa6cxXu{Zn8_< z$Ga|~Ps~7&_%lF+hzl3@HMZ${Up&O31 zrs*@LgLfcQC+X)jO?$$Qjd{-Yd;LiEYw?FP((QQUadoz5ov|W`9dEM>+R~W}(g8$p z?wjOJi}=4PGQWj?Lef+wN<%cx43l0G5p_Z-tdn$Q0>&Kq6@Z-f0C}@p;mMf&&!mvm zBE1EXx1JJMjv$vb=dumYXwG=cJ&o{25Sca*QO-!`xv6<{(Hbc2Lt?BK2+XT@KZBdb z&(GvYek>VcqZPrPsQ?R_Ig%<`7teMM^GRDs8B?Noi??KNP0UBIAx1q8$&Yor2QDkp#JCQLR z#n>|KfEzh%?&R+0Mw%LU%qNDsXIs1^0c#>GI8`gVE>x1hok6w!1MU0v+v~iE;4%8T zZT-+>xJ1Mr%8zTbCE~$@=noRe+roSisq_J5Q(wVk)ff3z3i*|dBI!3P8oBQBbscCd zURKW~H}}K+N?_5T?n0a9_!LBlhAXXBvEau57)wZCBSftL6*vBat{jU7H;uiTg$Oe0 z+D?Mgs2=IZ+|LLiS?#{RTMvLebPJJE!M7d*B^3O(}Kp*)n{|=f!GWh#FxM#y;n7OY6@{`FKxH%>E#<07AT6=7ns>L!rgo zF^y;MJwdQAP^L8vQ1W;Lkwglq-05+BA8mYSzbQ7>(?poV2Ven8%oC>Ip#?JqZvYZE zWPd8Dmrinxh^Fzp<}CjLB8L(FI_Bj512FAnQJoD`<_`ps8(e|as4p{J$4$4Xx@=Mz z^__0Nc!D%#dOUl6FAGd$k2TOAz;dqIU=NWQ*^WRg<5@0hMAwpLqVyWQ<6OU_i$DQr zO#YZpX+2BzkiN~<$QG8~haeI*1j*irCl=a%GJq_lAsRv#cF5yM)yMY1#mYnm_R7_KGvJIS6j9;kC#Adna|9lL+l7E6S*Xb zOZ{*Fcj=|y`jQ;3VMLt7%@Jnw_PW>+?#46ayGl)JKquFL8!Hy4dP)}BH}tcs`WvJX z^jy(Vp{_UstxDQ>0u3Tvhmu~SHG~y^^PqbDD~JdYI4I=X;F{N9_Wr4mLU;j+O@F}W zX1^=2csS=c>71Z@)!^a!qSCvM*rE^z%h}{O1Ub+OEk6+P`Ai}h% zu}VA0ltP{a7qd~^p4hv!%@=j?TP#@IUf7CcM9-NEtkBx&Z%!#qGr~+$HAH3~84NM{r>{w#juhPy zF-kp|;6iIs*eUe8vD^_A+%cHi^T z2y(`eXuSmuZQpU9?qI7PR6u#rwUfEmn`H4Kcl%IvbdsrhT+bbz_ys2ESVoS&PLQ70 zH8V2(-;*`JO3B8xEilcT=nx)D`ICqCT4NsrMY@LWqa-2j$2$;-*Q~Ed0|9Ked{_W- zC@C<}wxeX1+f>@P*;`x}=F&}`=|C|0l@T4cs6Dx%5$)24md(#R*%0c#E~v&bm&MaK zUgDV&!#DM~IQGANzAKfAG7%aVn_WkO8=gmVToI8^dxGPVABsB~ZiU&2zN({dGYyMo z=GEaeNXfD`TROk9YIbDO2AFSNw=?eR8-Oh!+0|NlJyWv`B2f30Z51ogvlsWU2}C0v zr&1L%X)*Zlq}q}}G%P^Tqy>E+anpeK4t9pn#@WR{!~vsTpYvoy;4E-$!Nr@V?dw=F z5SjnWzRi$ARQg!&$4MD1rIGvuA}Bc;6GgNLc#uLAM2P%%9N#0)cNVw8jdp3P7yBx* zUZdSKYwMqQVJ!}A0ZC>1IH|7?ME(ouLrW%wU~g~0Ebj4MT_f`916RW2ZXkuA?PPq! zaR4&(U^b zhOe#neeyQEe`&s6*gSG#uRn$h@M;^^>d3}Ijn9vZAZ0rcD-tuf&RaA3J)UJI+C;LbbJtWSGXu3Q)ebJfIygz*Nv3Me zxQ0hbg7p2w^C6k$R4;99%^PmozlrUalk|_z;G_y2$Hi%X(MF*f2d0rDPw&-pi}9N8 zOUT`ij}e(2O&oaykdq-Dr4XwR(ulkAGI)^azXgu0*h>uK*+7KX`K^u9n5z|3TsDI` zfQS|)ATH$zG&J04VOdH79D3=enz}HyA1S4hZ0V^^bzE5>lHIienRp|fBKHK-oYPwM z{O`9>=Qg%O>3s&jMJ_)X4pYMA1(jPygkxDib9!d)YP?@BZ=HP{z%x06imGlI1m*-> zc^#>^J0nUvN$MxZC0H@aghgEK$y(6h@> zBN>nc@sHML@3f@UxDLke^IL!OO$PUUzAniz6R*8YcQN;HXLB#cA3_u9UfcvpL0>~S8-V8`W1#jW}qWs&a^tYY<>e6FTq6vA>vg`%WkDIeZ)BDN; z314c5QBeedB$+yYofek&sjwgXs(mK6<+JNgB@uPgcdFy{o^J<_Vq~HthNKL29R{B-+QeWBG$Z2 zJQz$fNTcMXee*uI#~w56+O=*qiQxp2#sq{_jV{v-n3gmW$LXHoBtfvRcO;$|Sa>zJ zfs}Tx9Dk?at)(^yqLH1U01+nt-Oy%%-g?g7EdCnJHcVrYJd~3{;&bY=WEZgxX&lG{ zh@7O5ZE|#RxrsWh{kV@Vv=5R7y3_uaoE-1_kE9MF +UNOsTcHv^H&MgPM+ye8lF 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 Tvzw@k00000NkvXXu0mjf2)!rN literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/cardrender/background_texture_black_retro.png b/Mage.Client/src/main/resources/cardrender/background_texture_black_retro.png new file mode 100644 index 0000000000000000000000000000000000000000..78a201c2e7c2a31f3a02018aca3ccee79f0121f4 GIT binary patch literal 416423 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 z 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 z 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Ξ%tFm*10gisHBn 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�r#dD))1Vs=7ijEwR}6KU8cYmNLUz{HYD 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�a%Yd!0wM?X?7Uo1f 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