From 360cde3c8299364cf44559d8fa035270f99bf1b8 Mon Sep 17 00:00:00 2001 From: Grath <1895280+Grath@users.noreply.github.com> Date: Fri, 20 Sep 2024 23:03:23 -0400 Subject: [PATCH 01/33] Fix Zenith Chronicler bug. Sneaky bug, it would trigger on every spell cast after one multicolored spell until the next multicolored spell was cast. This change makes it only trigger when the spell that was cast was multicolored. --- Mage.Sets/src/mage/cards/z/ZenithChronicler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/z/ZenithChronicler.java b/Mage.Sets/src/mage/cards/z/ZenithChronicler.java index 9a46850d0ab..20f7bc10662 100644 --- a/Mage.Sets/src/mage/cards/z/ZenithChronicler.java +++ b/Mage.Sets/src/mage/cards/z/ZenithChronicler.java @@ -76,7 +76,7 @@ class ZenithChroniclerTriggeredAbility extends TriggeredAbilityImpl { Player controller = game.getPlayer(getControllerId()); Spell spell = game.getSpell(event.getTargetId()); SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); - if (controller != null && spell != null && watcher != null) { + if (controller != null && spell != null && watcher != null && spell.getColor(game).isMulticolored()) { int multicoloredSpell = 0; for (Spell spellCastThisTurn : watcher.getSpellsCastThisTurn(spell.getControllerId())) { if (spellCastThisTurn.getColor(game).isMulticolored() && ++multicoloredSpell > 1) { From 291a28d1db231017f8124cfb5b30cfd3215b0270 Mon Sep 17 00:00:00 2001 From: Grath <1895280+Grath@users.noreply.github.com> Date: Sat, 21 Sep 2024 00:40:15 -0400 Subject: [PATCH 02/33] If Emerge reduces mana cost to zero, pass noMana flag to allow spell to be cast with empty manaCostsToPay. (#12896) Fixes #12841 --- Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java index c1678cd031c..1649aca17b9 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java @@ -112,7 +112,8 @@ public class EmergeAbility extends SpellAbility { Permanent creature = game.getPermanent(target.getFirstTarget()); if (creature != null) { CardUtil.reduceCost(this, creature.getManaValue()); - if (super.activate(game, allowedIdentifiers, false)) { + boolean reducedToZero = this.getManaCostsToPay().isEmpty(); + if (super.activate(game, allowedIdentifiers, reducedToZero)) { MageObjectReference mor = new MageObjectReference(creature, game); if (creature.sacrifice(this, game)) { this.setCostsTag(EMERGE_ACTIVATION_CREATURE_REFERENCE, mor); //Can access with LKI afterwards From 7d1ab7109278ecf01f06fbd3d0cd5877a1f3c6be Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 21 Sep 2024 14:45:48 +0400 Subject: [PATCH 03/33] server: now idle response timeout will be disabled in local games with 1 human player --- Mage.Server/src/main/java/mage/server/game/GameController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index 50689e270e6..2a768f4ebd9 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -96,7 +96,7 @@ public class GameController implements GameCallback { this.tableId = tableId; this.choosingPlayerId = choosingPlayerId; this.gameOptions = gameOptions; - this.useResponseIdleTimeout = game.getPlayers().values().stream().anyMatch(Player::isHuman); + this.useResponseIdleTimeout = game.getPlayers().values().stream().filter(Player::isHuman).count() > 1; init(); } From f7d702d5df00716204922342bd5f45b30252e53d Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 21 Sep 2024 18:41:53 +0400 Subject: [PATCH 04/33] images: added The Ring image support (part of #12274, #12899); images: fixed miss dungeon image in choose dungeon dialog (part of #12274); --- .../mage/client/dialog/PickChoiceDialog.java | 2 +- .../cards/repository/TokenRepository.java | 4 ++++ Mage/src/main/java/mage/game/GameImpl.java | 17 ++++---------- Mage/src/main/java/mage/game/GameState.java | 5 ++++ .../main/java/mage/game/command/Dungeon.java | 23 ++++++++++++++----- .../main/java/mage/game/command/Emblem.java | 3 +-- .../main/java/mage/game/command/Plane.java | 2 +- .../game/command/emblems/EmblemOfCard.java | 2 +- .../game/command/emblems/TheRingEmblem.java | 15 ++++++++++++ 9 files changed, 50 insertions(+), 23 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java index 8b2693d25ca..c1b25b1e152 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java @@ -313,7 +313,7 @@ public class PickChoiceDialog extends MageDialog { // as card name cardInfo.init(item.getHint(), this.bigCard, this.gameId); } else if (item.getHintType() == ChoiceHintType.CARD_DUNGEON) { - // as card name + // as dungeon name CardView cardView = new CardView(new DungeonView(Dungeon.createDungeon(item.getHint(), true))); cardInfo.init(cardView, this.bigCard, this.gameId); } else if (item.getHintType() == ChoiceHintType.GAME_OBJECT) { diff --git a/Mage/src/main/java/mage/cards/repository/TokenRepository.java b/Mage/src/main/java/mage/cards/repository/TokenRepository.java index eeed1273346..ed25e535d39 100644 --- a/Mage/src/main/java/mage/cards/repository/TokenRepository.java +++ b/Mage/src/main/java/mage/cards/repository/TokenRepository.java @@ -33,6 +33,7 @@ public enum TokenRepository { public static final String XMAGE_IMAGE_NAME_NIGHT = "Night"; public static final String XMAGE_IMAGE_NAME_THE_MONARCH = "The Monarch"; public static final String XMAGE_IMAGE_NAME_RADIATION = "Radiation"; + public static final String XMAGE_IMAGE_NAME_THE_RING = "The Ring"; public static final String XMAGE_IMAGE_NAME_HELPER_EMBLEM = "Helper Emblem"; private static final Logger logger = Logger.getLogger(TokenRepository.class); @@ -306,6 +307,9 @@ public enum TokenRepository { // Radiation (for trigger) res.add(createXmageToken(XMAGE_IMAGE_NAME_RADIATION, 1, "https://api.scryfall.com/cards/tpip/22/en?format=image")); + // The Ring + res.add(createXmageToken(XMAGE_IMAGE_NAME_THE_RING, 1, "https://api.scryfall.com/cards/tltr/H13/en?format=image")); + // Helper emblem (for global card hints) // use backface for it res.add(createXmageToken(XMAGE_IMAGE_NAME_HELPER_EMBLEM, 1, "https://upload.wikimedia.org/wikipedia/en/a/aa/Magic_the_gathering-card_back.jpg")); diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 8c122146d71..2f5d748a26c 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -583,11 +583,10 @@ public abstract class GameImpl implements Game { if (emblem != null) { return emblem; } + TheRingEmblem newEmblem = new TheRingEmblem(playerId); - - // TODO: add image info - state.addCommandObject(newEmblem); + return newEmblem; } @@ -1966,16 +1965,13 @@ public abstract class GameImpl implements Game { @Override public void addEmblem(Emblem emblem, MageObject sourceObject, UUID toPlayerId) { Emblem newEmblem = emblem.copy(); - newEmblem.setSourceObject(sourceObject); + newEmblem.setSourceObjectAndInitImage(sourceObject); newEmblem.setControllerId(toPlayerId); newEmblem.assignNewId(); newEmblem.getAbilities().newId(); for (Ability ability : newEmblem.getAbilities()) { ability.setSourceId(newEmblem.getId()); } - - // image info setup in setSourceObject - state.addCommandObject(newEmblem); } @@ -1997,17 +1993,15 @@ public abstract class GameImpl implements Game { } } Plane newPlane = plane.copy(); - newPlane.setSourceObject(); + newPlane.setSourceObjectAndInitImage(); newPlane.setControllerId(toPlayerId); newPlane.assignNewId(); newPlane.getAbilities().newId(); for (Ability ability : newPlane.getAbilities()) { ability.setSourceId(newPlane.getId()); } - - // image info setup in setSourceObject - state.addCommandObject(newPlane); + informPlayers("You have planeswalked to " + newPlane.getLogName()); // Fire off the planeswalked event @@ -2028,7 +2022,6 @@ public abstract class GameImpl implements Game { @Override public Dungeon addDungeon(Dungeon dungeon, UUID playerId) { dungeon.setControllerId(playerId); - dungeon.setSourceObject(); state.addCommandObject(dungeon); return dungeon; } diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 13499c2fb69..e9238b39ab5 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -1239,6 +1239,11 @@ public class GameState implements Serializable, Copyable { this.isPlaneChase = isPlaneChase; } + /** + * Add object to command zone. + *

+ * Warning, all object data must be initialized before adding, including image info + */ public void addCommandObject(CommandObject commandObject) { getCommand().add(commandObject); setZone(commandObject.getId(), Zone.COMMAND); diff --git a/Mage/src/main/java/mage/game/command/Dungeon.java b/Mage/src/main/java/mage/game/command/Dungeon.java index 8643378c17a..407da093bb8 100644 --- a/Mage/src/main/java/mage/game/command/Dungeon.java +++ b/Mage/src/main/java/mage/game/command/Dungeon.java @@ -154,24 +154,35 @@ public class Dungeon extends CommandObjectImpl { } public static Dungeon createDungeon(String name, boolean isNameMustExists) { + Dungeon res; switch (name) { case "Tomb of Annihilation": - return new TombOfAnnihilationDungeon(); + res = new TombOfAnnihilationDungeon(); + break; case "Lost Mine of Phandelver": - return new LostMineOfPhandelverDungeon(); + res = new LostMineOfPhandelverDungeon(); + break; case "Dungeon of the Mad Mage": - return new DungeonOfTheMadMageDungeon(); + res = new DungeonOfTheMadMageDungeon(); + break; default: if (isNameMustExists) { throw new UnsupportedOperationException("A dungeon should have been chosen"); } else { - return null; + res = null; } } + + // source don't have source, so image data can be initialized immediately + if (res != null) { + res.setSourceObjectAndInitImage(); + } + + return res; } - public void setSourceObject() { - // choose set code due source + public void setSourceObjectAndInitImage() { + // image TokenInfo foundInfo = TokenRepository.instance.findPreferredTokenInfoForClass(this.getClass().getName(), null); if (foundInfo != null) { this.setExpansionSetCode(foundInfo.getSetCode()); diff --git a/Mage/src/main/java/mage/game/command/Emblem.java b/Mage/src/main/java/mage/game/command/Emblem.java index 1070de0d475..294e05f76ea 100644 --- a/Mage/src/main/java/mage/game/command/Emblem.java +++ b/Mage/src/main/java/mage/game/command/Emblem.java @@ -19,7 +19,6 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.game.Game; import mage.game.events.ZoneChangeEvent; -import mage.util.CardUtil; import mage.util.SubTypes; import java.util.Collections; @@ -59,7 +58,7 @@ public abstract class Emblem extends CommandObjectImpl { return frameStyle; } - public void setSourceObject(MageObject sourceObject) { + public void setSourceObjectAndInitImage(MageObject sourceObject) { this.sourceObject = sourceObject; // choose set code due source diff --git a/Mage/src/main/java/mage/game/command/Plane.java b/Mage/src/main/java/mage/game/command/Plane.java index 4e48daaf7cd..c87153f97a5 100644 --- a/Mage/src/main/java/mage/game/command/Plane.java +++ b/Mage/src/main/java/mage/game/command/Plane.java @@ -65,7 +65,7 @@ public abstract class Plane extends CommandObjectImpl { return frameStyle; } - public void setSourceObject() { + public void setSourceObjectAndInitImage() { this.sourceObject = null; // choose set code due source diff --git a/Mage/src/main/java/mage/game/command/emblems/EmblemOfCard.java b/Mage/src/main/java/mage/game/command/emblems/EmblemOfCard.java index 422ddc5af4c..ad9f4f4c560 100644 --- a/Mage/src/main/java/mage/game/command/emblems/EmblemOfCard.java +++ b/Mage/src/main/java/mage/game/command/emblems/EmblemOfCard.java @@ -94,7 +94,7 @@ public final class EmblemOfCard extends Emblem { } @Override - public void setSourceObject(MageObject sourceObject) { + public void setSourceObjectAndInitImage(MageObject sourceObject) { this.sourceObject = sourceObject; // super method would try and fail to find the emblem image here // (not sure why that would be setSoureObject's job; we get our image during construction) diff --git a/Mage/src/main/java/mage/game/command/emblems/TheRingEmblem.java b/Mage/src/main/java/mage/game/command/emblems/TheRingEmblem.java index e99284e9dd5..d7f3aa19a14 100644 --- a/Mage/src/main/java/mage/game/command/emblems/TheRingEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/TheRingEmblem.java @@ -12,6 +12,8 @@ import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.abilities.effects.common.LoseLifeOpponentsEffect; import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.cards.repository.TokenInfo; +import mage.cards.repository.TokenRepository; import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; @@ -41,6 +43,19 @@ public final class TheRingEmblem extends Emblem { public TheRingEmblem(UUID controllerId) { super("The Ring"); this.setControllerId(controllerId); + + // ring don't have source, so image can be initialized immediately + TokenInfo foundInfo = TokenRepository.instance.findPreferredTokenInfoForXmage(TokenRepository.XMAGE_IMAGE_NAME_THE_RING, null); + if (foundInfo != null) { + this.setExpansionSetCode(foundInfo.getSetCode()); + this.setUsesVariousArt(false); + this.setCardNumber(""); + this.setImageFileName(""); // use default + this.setImageNumber(foundInfo.getImageNumber()); + } else { + // how-to fix: add emblem to the tokens-database TokenRepository->loadXmageTokens + throw new IllegalArgumentException("Wrong code usage: can't find xmage token info for: " + TokenRepository.XMAGE_IMAGE_NAME_THE_RING); + } } private TheRingEmblem(final TheRingEmblem card) { From f85a70780f958112014fa01fb4885a3477a5e230 Mon Sep 17 00:00:00 2001 From: Jeff Wadsworth Date: Sat, 21 Sep 2024 10:06:14 -0500 Subject: [PATCH 05/33] Fixed #12883. --- Mage/src/main/java/mage/game/stack/SpellStack.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/game/stack/SpellStack.java b/Mage/src/main/java/mage/game/stack/SpellStack.java index 1cbd03da71e..f040f698825 100644 --- a/Mage/src/main/java/mage/game/stack/SpellStack.java +++ b/Mage/src/main/java/mage/game/stack/SpellStack.java @@ -81,7 +81,9 @@ public class SpellStack extends ArrayDeque { counteredObjectName = "Ability (" + stackObject.getStackAbility().getRule(targetSourceName) + ") of " + targetSourceName; } if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, source, stackObject.getControllerId()))) { - if (!(stackObject instanceof Spell)) { // spells are removed from stack by the card movement + // spells are removed from stack by the card movement + if (!(stackObject instanceof Spell) + || stackObject.isCopy()) { // ensure that copies of stackobjects have their history recorded ie: Swan Song this.remove(stackObject, game); game.rememberLKI(Zone.STACK, stackObject); } From a3f38bc0a4700e7c7276d950b49261f7663f3156 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 23 Sep 2024 17:35:41 +0400 Subject: [PATCH 06/33] GUI, game: improved player panel icons for better look in dark theme --- .../src/main/resources/info/energy.png | Bin 6475 -> 2921 bytes .../src/main/resources/info/experience.png | Bin 4445 -> 5819 bytes Mage.Client/src/main/resources/info/rad.png | Bin 1392 -> 3465 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Mage.Client/src/main/resources/info/energy.png b/Mage.Client/src/main/resources/info/energy.png index 5cdfba5dc49f5b205fbb3f0d127bf2035813afe8..d08030750221e58eea0aeddf97b262749abf95f6 100644 GIT binary patch literal 2921 zcmbVO2{=@H8y{mx5uz`9rqN=F*^N0FOGAtdQ&ebS=FAy}+02ZLrA&P-_=r$xAyh7{ z;wnppbfuD1x2x@5ZmyQkR+hAodj@&B-*dn3KHr^Z&N=^c&hP(w-}m<}&q-nVdCi?; zItPJ3%w>3ctc9;`szXZ?KA+}C|AIiMQTTK^%bhFcKQ00=GEZ- zZg!&j52gR4@6yruSI2N(2i3e)?F6SiZ_k=NLqTV@KHPu))tx6syj-NxFZa~eGcgGL zWzjc`I|j6r?abPKQq$)of@QIfx>Z=M8MG*QPFvlBQcXOnrQmp^zMitw?vk} zXEW9u=>)>(_DG_BsPc)7W|>B<*O7**`C4p_Qif#d{un$Ec;_?}{GflS`{ImQhxfG- zt1b7Y_!vIAzs9I}Ks^j^DWF(Xudh6NbtLHvZC?8jBs%V;<+hw=6zr}jbVyofydt}Y zb5mlXJHU<)SEn19rM* z2n6a4KQKrh#PkI~vCx67($_&Dl)z{N!r4_JVS`bS9L<5k`63!-ptcT!=5uM74P+*c zDWO9VeD8QE6cFzh2*yW&R4&HV1?{W=U;-f|XQLHDfk*}@XqZV}0A8yeV=?GSh&+mh znM!C7lZB>>r4X9z;AjuxNdz>7>VS8|Q3<5wXaWvT!Qv=bJkcJ92XKx6k&2%FVPI=g zE)Q7iv1-~Dd`H7X$mJ3Mi;azqb%-T8h^66JxLBxI905xp*uw~WS)54DR@jSVR$m!B zAQ>p-OXPg92(4mdbHp)n8V2@sDg>cqhE^n-mI)RNt6)p8cn6#+q)8wboWV(Aq=HG~ zTo4NhAR#1@%U~>i1}ljW%f+$?@qeM7dHn|iu(V9(jEz6_MJSvxA(MMq<*_MbE|m-7zwY-CgE6YSe%;UgKll4f z`EXyc1%I1I)orSR0KEA!I8|}e{SW{}Pp<@g^kfYHY*1C(Gz_RpD#XQ1Kj!~!1*T|Y zBOsXcf24Z~CKL1Iv1}>i9uBMZ7sZ5yUB{|=a4HJye+D@D?wfx73WtY)YB@7~;G3EG z1&QFHC55L`)@no~JoLI49`1pPW8FtLCa+s(tgMSRKUK8KtU^0LJ;aY4`69>!^3m|T z@4U-~mYjA1bsyBA8SlEw@-})?S@M0sT#;2 zB^)1_h&p%m9{*EV!?B8q@s8G--lRTqK{y_#lc?4*RORY8guA8#CI;O9jV;lS@K4-s z=#*O_5>X?Hfw+?27c|4t5J44K%&wqj7q+^mnjznkKZ z{Ib5;!L2`0hjcCOoeOTxCSjXne$?}p`xz%m>3%iK62&uLQAD<)TU76^Yl=D{>?YZH3-GR36r=&`Y1)MX6UJ3 z%a$F6e7!1k&Yhy4qsJA>e&>Uj<*BLpD6f*dD?Lgx?Q)C36X~GC_RZ16%Iqr(s$P^n zd-T!=nLVsDQ<4oJ8_gYkKb}`VvfJfDnPyi4&f>#3@|i|W0xf3s#=Upc81LD=aIv7(G#cUv(^V7!gkKxv2MniH@b==%pK1ztvZ0<`$7Y z8I(t!^(<}QYm;j%jizny*H~ba?y~zyjs%~5(X?=PZ(pxr{R*=Ddv&o>?-)uxd(7l! zNSM~oxu1=5UxGRcR8W3og~jM|%v*kQQ&Dkf!Ih`B_20RyLgs3iFZk4cVcjp9f_b(V zc-4aUwqwtBO83ScSov{DXJ1=Hj&rh25vfsjpsBvgryDBQar$m}z;@}s%)@^_Wmh9&+RTB6Pe z$8STKy%MAymV4=1^N?Fo1ZP$V!%SYXi?=k4W=>@B4Bmp=gnyU~RdQahxozV&_?YqK z!+=_UhTwvRA(1t9KJ6s!VApG%&NIlq_YV*adYKnpHZF8{`h^=t2D=7msU0W1D@;CE zb$vc+^K#y2owqgP3D3)p&OKZ2YXC7*d!*)9DMjg#&UTVRj}gy#&kp7G^_3g8X^xq? zj~1jCnr0ZCtB=hk)JF7j+8SS%n zw$)(P*EYxUv(L5v`?cY1%S)Fy3rka)mbVmoGm-JVR}G$kKdqua+#$Mo>4>sIx4P?f zM*v{+LUMgxOA6sl?atGBmAY$ng7dPjHtu1&6u*^>D7)*;2QR;94ca`xsKL!T6}hC7 zIJmb(D<-4^EZ3?^-X2qx{N=8^u^hEIZu7-aU+a8CYnSeqD+ls2ozp*#{&b}?BjoVx zyN-i~ncMF=E{fBV4KJ}6o1M^?)FAThJ|w8^`L)@#kK9&YKQwEr>PwE{>F05NW!TQ& E0grr;)&Kwi literal 6475 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 diff --git a/Mage.Client/src/main/resources/info/rad.png b/Mage.Client/src/main/resources/info/rad.png index 363c4412b0988908f10d07b5958f5e63e016cb68..8b8c53d340312dad86032894f2ecc0acce5e3194 100644 GIT binary patch literal 3465 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 Date: Mon, 23 Sep 2024 12:20:25 -0400 Subject: [PATCH 07/33] Update Commander ban list. https://mtgcommander.net/index.php/2024/09/23/september-2024-quarterly-update/ --- .../Mage.Deck.Constructed/src/mage/deck/Commander.java | 4 ++++ 1 file changed, 4 insertions(+) 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 63e9bec779a..3724522e671 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 @@ -20,6 +20,7 @@ public class Commander extends AbstractCommander { 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"); @@ -29,17 +30,20 @@ public class Commander extends AbstractCommander { banned.add("Griselbrand"); banned.add("Hullbreacher"); banned.add("Iona, Shield of Emeria"); + banned.add("Jeweled Lotus"); banned.add("Jihad"); banned.add("Karakas"); banned.add("Leovold, Emissary of Trest"); banned.add("Library of Alexandria"); banned.add("Limited Resources"); banned.add("Lutri, the Spellchaser"); + banned.add("Mana Crypt"); banned.add("Mox Emerald"); banned.add("Mox Jet"); banned.add("Mox Pearl"); 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"); From ff775efeea518a1778efa413a08c6a1026706678 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Mon, 23 Sep 2024 18:13:15 -0400 Subject: [PATCH 08/33] normalize another unicode symbol --- Mage/src/main/java/mage/cards/decks/CardNameUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage/src/main/java/mage/cards/decks/CardNameUtil.java b/Mage/src/main/java/mage/cards/decks/CardNameUtil.java index f6ae95d8ef3..5e85e5d211d 100644 --- a/Mage/src/main/java/mage/cards/decks/CardNameUtil.java +++ b/Mage/src/main/java/mage/cards/decks/CardNameUtil.java @@ -20,6 +20,7 @@ public class CardNameUtil { .replace("û", "u") .replace("í", "i") .replace("ï", "i") + .replace("î", "i") .replace("â", "a") .replace("á", "a") .replace("à", "a") From 952da01dbd15df36ce9c7e2cbb10835582d8ab64 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Mon, 23 Sep 2024 18:21:05 -0400 Subject: [PATCH 09/33] fix rarity in DSC --- Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java index c12c4a2d15d..a2f2f567558 100644 --- a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java +++ b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java @@ -72,7 +72,7 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet { cards.add(new SetCardInfo("Command Tower", 96, Rarity.COMMON, mage.cards.c.CommandTower.class)); cards.add(new SetCardInfo("Commander's Sphere", 244, Rarity.COMMON, mage.cards.c.CommandersSphere.class)); cards.add(new SetCardInfo("Convert to Slime", 37, Rarity.RARE, mage.cards.c.ConvertToSlime.class)); - cards.add(new SetCardInfo("Counterspell", 114, Rarity.COMMON, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Counterspell", 114, Rarity.UNCOMMON, mage.cards.c.Counterspell.class)); cards.add(new SetCardInfo("Crawling Sensation", 173, Rarity.UNCOMMON, mage.cards.c.CrawlingSensation.class)); cards.add(new SetCardInfo("Crypt Ghast", 368, Rarity.MYTHIC, mage.cards.c.CryptGhast.class)); cards.add(new SetCardInfo("Culling Ritual", 85, Rarity.RARE, mage.cards.c.CullingRitual.class)); From 6693f75695fccf4dc230da23b90b47bfb4680d9c Mon Sep 17 00:00:00 2001 From: xenohedron Date: Mon, 23 Sep 2024 18:21:27 -0400 Subject: [PATCH 10/33] hide some reminder text --- Mage.Sets/src/mage/cards/c/ConductiveMachete.java | 2 +- Mage.Sets/src/mage/cards/c/CursedWindbreaker.java | 2 +- Mage.Sets/src/mage/cards/d/DissectionTools.java | 2 +- Mage.Sets/src/mage/cards/e/ExperimentalPilot.java | 2 +- Mage.Sets/src/mage/cards/j/JacesSanctum.java | 4 ++-- Mage.Sets/src/mage/cards/k/KillersMask.java | 2 +- Mage.Sets/src/mage/cards/l/LongRiverLurker.java | 2 +- Mage.Sets/src/mage/cards/n/NashiSearcherInTheDark.java | 2 +- Mage.Sets/src/mage/cards/o/OracleOfTheAlpha.java | 2 +- Mage.Sets/src/mage/cards/r/ReaperOfTheWilds.java | 2 +- Mage.Sets/src/mage/cards/s/SawbladeSkinripper.java | 2 +- Mage.Sets/src/mage/cards/s/StrengthTestingHammer.java | 5 +---- Mage.Sets/src/mage/cards/w/WinterMisanthropicGuide.java | 2 +- 13 files changed, 14 insertions(+), 17 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/ConductiveMachete.java b/Mage.Sets/src/mage/cards/c/ConductiveMachete.java index afc99303f5a..dfd0fd82ed6 100644 --- a/Mage.Sets/src/mage/cards/c/ConductiveMachete.java +++ b/Mage.Sets/src/mage/cards/c/ConductiveMachete.java @@ -29,7 +29,7 @@ public final class ConductiveMachete extends CardImpl { this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(2, 1))); // Equip {4} - this.addAbility(new EquipAbility(4)); + this.addAbility(new EquipAbility(4, false)); } private ConductiveMachete(final ConductiveMachete card) { diff --git a/Mage.Sets/src/mage/cards/c/CursedWindbreaker.java b/Mage.Sets/src/mage/cards/c/CursedWindbreaker.java index 0adbba0c400..0b54debf9a4 100644 --- a/Mage.Sets/src/mage/cards/c/CursedWindbreaker.java +++ b/Mage.Sets/src/mage/cards/c/CursedWindbreaker.java @@ -33,7 +33,7 @@ public final class CursedWindbreaker extends CardImpl { ))); // Equip {3} - this.addAbility(new EquipAbility(3)); + this.addAbility(new EquipAbility(3, false)); } private CursedWindbreaker(final CursedWindbreaker card) { diff --git a/Mage.Sets/src/mage/cards/d/DissectionTools.java b/Mage.Sets/src/mage/cards/d/DissectionTools.java index df90c072f42..8fa11a69cb2 100644 --- a/Mage.Sets/src/mage/cards/d/DissectionTools.java +++ b/Mage.Sets/src/mage/cards/d/DissectionTools.java @@ -45,7 +45,7 @@ public final class DissectionTools extends CardImpl { // Equip--Sacrifice a creature. this.addAbility(new EquipAbility( - Outcome.BoostCreature, new SacrificeTargetCost(StaticFilters.FILTER_PERMANENT_A_CREATURE) + Outcome.BoostCreature, new SacrificeTargetCost(StaticFilters.FILTER_PERMANENT_A_CREATURE), false )); } diff --git a/Mage.Sets/src/mage/cards/e/ExperimentalPilot.java b/Mage.Sets/src/mage/cards/e/ExperimentalPilot.java index d01efe3f128..9e55c13e9b4 100644 --- a/Mage.Sets/src/mage/cards/e/ExperimentalPilot.java +++ b/Mage.Sets/src/mage/cards/e/ExperimentalPilot.java @@ -52,7 +52,7 @@ public final class ExperimentalPilot extends CardImpl { this.toughness = new MageInt(2); // Ward {2} - this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"))); + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"), false)); // {U}, Discard two cards: Draft a card from Experimental Pilot's spellbook. Ability ability = new SimpleActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/j/JacesSanctum.java b/Mage.Sets/src/mage/cards/j/JacesSanctum.java index b8848165daf..e08a4647d82 100644 --- a/Mage.Sets/src/mage/cards/j/JacesSanctum.java +++ b/Mage.Sets/src/mage/cards/j/JacesSanctum.java @@ -40,7 +40,7 @@ public final class JacesSanctum extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); // Whenever you cast an instant or sorcery spell, scry 1. - this.addAbility(new SpellCastControllerTriggeredAbility(new ScryEffect(1), filter2, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ScryEffect(1, false), filter2, false)); } private JacesSanctum(final JacesSanctum card) { @@ -51,4 +51,4 @@ public final class JacesSanctum extends CardImpl { public JacesSanctum copy() { return new JacesSanctum(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/k/KillersMask.java b/Mage.Sets/src/mage/cards/k/KillersMask.java index 2c73b58f9cb..d7384a64d8c 100644 --- a/Mage.Sets/src/mage/cards/k/KillersMask.java +++ b/Mage.Sets/src/mage/cards/k/KillersMask.java @@ -33,7 +33,7 @@ public final class KillersMask extends CardImpl { ))); // Equip {2} - this.addAbility(new EquipAbility(2)); + this.addAbility(new EquipAbility(2, false)); } private KillersMask(final KillersMask card) { diff --git a/Mage.Sets/src/mage/cards/l/LongRiverLurker.java b/Mage.Sets/src/mage/cards/l/LongRiverLurker.java index 1247a07a329..8143d2b3bd1 100644 --- a/Mage.Sets/src/mage/cards/l/LongRiverLurker.java +++ b/Mage.Sets/src/mage/cards/l/LongRiverLurker.java @@ -46,7 +46,7 @@ public final class LongRiverLurker extends CardImpl { this.toughness = new MageInt(3); // Ward {1} - this.addAbility(new WardAbility(new ManaCostsImpl<>("{1}"))); + this.addAbility(new WardAbility(new ManaCostsImpl<>("{1}"), false)); // Other Frogs you control have ward {1}. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( diff --git a/Mage.Sets/src/mage/cards/n/NashiSearcherInTheDark.java b/Mage.Sets/src/mage/cards/n/NashiSearcherInTheDark.java index f3efe3694ff..f2535b439e7 100644 --- a/Mage.Sets/src/mage/cards/n/NashiSearcherInTheDark.java +++ b/Mage.Sets/src/mage/cards/n/NashiSearcherInTheDark.java @@ -36,7 +36,7 @@ public final class NashiSearcherInTheDark extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // Whenever Nashi, Searcher in the Dark deals combat damage to a player, you mill that many cards. You may put any number of legendary and/or enchantment cards from among them into your hand. If you put no cards into your hand this way, put a +1/+1 counter on Nashi. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new NashiSearcherInTheDarkEffect())); diff --git a/Mage.Sets/src/mage/cards/o/OracleOfTheAlpha.java b/Mage.Sets/src/mage/cards/o/OracleOfTheAlpha.java index 3ee8e9dc668..54651e078ac 100644 --- a/Mage.Sets/src/mage/cards/o/OracleOfTheAlpha.java +++ b/Mage.Sets/src/mage/cards/o/OracleOfTheAlpha.java @@ -42,7 +42,7 @@ public final class OracleOfTheAlpha extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new OracleOfTheAlphaEffect())); // Whenever Oracle of the Alpha attacks, scry 1. - this.addAbility(new AttacksTriggeredAbility(new ScryEffect(1))); + this.addAbility(new AttacksTriggeredAbility(new ScryEffect(1, false))); } private OracleOfTheAlpha(final OracleOfTheAlpha card) { diff --git a/Mage.Sets/src/mage/cards/r/ReaperOfTheWilds.java b/Mage.Sets/src/mage/cards/r/ReaperOfTheWilds.java index bb74c64aa27..2e1c087fc35 100644 --- a/Mage.Sets/src/mage/cards/r/ReaperOfTheWilds.java +++ b/Mage.Sets/src/mage/cards/r/ReaperOfTheWilds.java @@ -31,7 +31,7 @@ public final class ReaperOfTheWilds extends CardImpl { this.toughness = new MageInt(5); // Whenever another creature dies, scry 1. - this.addAbility(new DiesCreatureTriggeredAbility(new ScryEffect(1), false, true)); + this.addAbility(new DiesCreatureTriggeredAbility(new ScryEffect(1, false), false, true)); // {B}: Reaper of the Wilds gains deathtouch until end of turn. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl<>("{B}"))); // {1}{G}: Reaper of the Wilds gains hexproof until end of turn. diff --git a/Mage.Sets/src/mage/cards/s/SawbladeSkinripper.java b/Mage.Sets/src/mage/cards/s/SawbladeSkinripper.java index eded8a073a5..9c0d15130af 100644 --- a/Mage.Sets/src/mage/cards/s/SawbladeSkinripper.java +++ b/Mage.Sets/src/mage/cards/s/SawbladeSkinripper.java @@ -39,7 +39,7 @@ public final class SawbladeSkinripper extends CardImpl { this.toughness = new MageInt(2); // Menace - this.addAbility(new MenaceAbility()); + this.addAbility(new MenaceAbility(false)); // {2}, Sacrifice another creature or enchantment: Put a +1/+1 counter on Sawblade Skinripper. Ability ability = new SimpleActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/s/StrengthTestingHammer.java b/Mage.Sets/src/mage/cards/s/StrengthTestingHammer.java index a3c56ecdb5b..3ae943bd288 100644 --- a/Mage.Sets/src/mage/cards/s/StrengthTestingHammer.java +++ b/Mage.Sets/src/mage/cards/s/StrengthTestingHammer.java @@ -41,7 +41,7 @@ public final class StrengthTestingHammer extends CardImpl { this.addAbility(ability); // Equip {3} - this.addAbility(new EquipAbility(3)); + this.addAbility(new EquipAbility(3, false)); } private StrengthTestingHammer(final StrengthTestingHammer card) { @@ -100,6 +100,3 @@ class StrengthTestingHammerEffect extends OneShotEffect { return false; } } - - - diff --git a/Mage.Sets/src/mage/cards/w/WinterMisanthropicGuide.java b/Mage.Sets/src/mage/cards/w/WinterMisanthropicGuide.java index f6df98bcf64..689ecbc1f50 100644 --- a/Mage.Sets/src/mage/cards/w/WinterMisanthropicGuide.java +++ b/Mage.Sets/src/mage/cards/w/WinterMisanthropicGuide.java @@ -33,7 +33,7 @@ public final class WinterMisanthropicGuide extends CardImpl { this.toughness = new MageInt(4); // Ward {2} - this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"))); + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"), false)); // At the beginning of your upkeep, each player draws two cards. this.addAbility(new BeginningOfUpkeepTriggeredAbility( From afde449a0febf682880451d31ee2b8275fe96883 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 25 Sep 2024 09:52:56 +0400 Subject: [PATCH 11/33] typo --- Mage/src/main/java/mage/game/command/Dungeon.java | 2 +- .../main/java/mage/game/command/emblems/RadiationEmblem.java | 2 +- Mage/src/main/java/mage/game/command/emblems/TheRingEmblem.java | 2 +- .../main/java/mage/game/command/emblems/XmageHelperEmblem.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Mage/src/main/java/mage/game/command/Dungeon.java b/Mage/src/main/java/mage/game/command/Dungeon.java index 407da093bb8..f6798b3d89c 100644 --- a/Mage/src/main/java/mage/game/command/Dungeon.java +++ b/Mage/src/main/java/mage/game/command/Dungeon.java @@ -173,7 +173,7 @@ public class Dungeon extends CommandObjectImpl { } } - // source don't have source, so image data can be initialized immediately + // dungeon don't have source, so image data can be initialized immediately if (res != null) { res.setSourceObjectAndInitImage(); } diff --git a/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java b/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java index 37f58a14114..8202226d44c 100644 --- a/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java @@ -45,7 +45,7 @@ public class RadiationEmblem extends Emblem { this.setImageFileName(""); // use default this.setImageNumber(foundInfo.getImageNumber()); } else { - // how-to fix: add emblem to the tokens-database TokenRepository->loadXmageTokens + // how-to fix: add image to the tokens-database TokenRepository->loadXmageTokens throw new IllegalArgumentException("Wrong code usage: can't find xmage token info for: " + TokenRepository.XMAGE_IMAGE_NAME_RADIATION); } } diff --git a/Mage/src/main/java/mage/game/command/emblems/TheRingEmblem.java b/Mage/src/main/java/mage/game/command/emblems/TheRingEmblem.java index d7f3aa19a14..e5fb21ccf99 100644 --- a/Mage/src/main/java/mage/game/command/emblems/TheRingEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/TheRingEmblem.java @@ -53,7 +53,7 @@ public final class TheRingEmblem extends Emblem { this.setImageFileName(""); // use default this.setImageNumber(foundInfo.getImageNumber()); } else { - // how-to fix: add emblem to the tokens-database TokenRepository->loadXmageTokens + // how-to fix: add image to the tokens-database TokenRepository->loadXmageTokens throw new IllegalArgumentException("Wrong code usage: can't find xmage token info for: " + TokenRepository.XMAGE_IMAGE_NAME_THE_RING); } } diff --git a/Mage/src/main/java/mage/game/command/emblems/XmageHelperEmblem.java b/Mage/src/main/java/mage/game/command/emblems/XmageHelperEmblem.java index 1fa47c17fbc..d4f396adaa8 100644 --- a/Mage/src/main/java/mage/game/command/emblems/XmageHelperEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/XmageHelperEmblem.java @@ -28,7 +28,7 @@ public class XmageHelperEmblem extends Emblem { this.setImageFileName(""); // use default this.setImageNumber(foundInfo.getImageNumber()); } else { - // how-to fix: add emblem to the tokens-database TokenRepository->loadXmageTokens + // how-to fix: add image to the tokens-database TokenRepository->loadXmageTokens throw new IllegalArgumentException("Wrong code usage: can't find xmage token info for: " + TokenRepository.XMAGE_IMAGE_NAME_HELPER_EMBLEM); } } From 701bd68d97120576b34a3a8a0259c6999b9db7fe Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 25 Sep 2024 09:55:19 +0400 Subject: [PATCH 12/33] tests: added test for LKI from copied spell (related to Swan Song fix from #12883) --- .../mage/test/cards/copy/CopySpellTest.java | 54 ++++++++++++++++++- .../java/org/mage/test/player/TestPlayer.java | 2 +- .../main/java/mage/game/stack/SpellStack.java | 2 +- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java index 9c13397692f..4c01474b53c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java @@ -22,7 +22,7 @@ import java.util.Set; import java.util.UUID; /** - * @author LevelX2 + * @author LevelX2, JayDi85 */ public class CopySpellTest extends CardTestPlayerBase { @@ -905,4 +905,56 @@ public class CopySpellTest extends CardTestPlayerBase { Assert.fail(infoPrefix + " - " + "cards must have same zone and zcc: " + zcc1 + " - " + zone1 + " != " + zcc2 + " - " + zone2); } } + + /** + * Bug: If Swan Song is used to counter a copied spell, no tokens are created #12883 + */ + @Test + public void test_LKI() { + // Counter target enchantment, instant, or sorcery spell. + // Its controller creates a 2/2 blue Bird creature token with flying. + addCard(Zone.HAND, playerA, "Swan Song", 1); // {U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // + // Until end of turn, whenever a player casts an instant or sorcery spell, that player copies it and + // may choose new targets for the copy. + addCard(Zone.HAND, playerA, "Bonus Round", 1); // {1}{R}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); // {R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton", 1); + + checkPermanentCount("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, playerA, "Bird Token", 0); + checkPermanentCount("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, playerB, "Grizzly Bears", 1); + checkPermanentCount("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, playerB, "Augmenting Automaton", 1); + + // prepare copy effect + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bonus Round"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // cast and duplicate bolt + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt"); + addTarget(playerA, "Grizzly Bears"); // original target + setChoice(playerA, true); // use new target + addTarget(playerA, "Augmenting Automaton"); // copy target + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); // resolve copy trigger + checkStackObject("on copy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", 2); + + // counter copy and save Augmenting Automaton + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swan Song", "Lightning Bolt[only copy]", "Lightning Bolt", StackClause.WHILE_COPY_ON_STACK); + setChoice(playerA, false); // no change target for duplicated counter spell (non-relevant here) + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, playerA, "Bird Token", 1); + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, playerB, "Grizzly Bears", 0); + checkPermanentCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, playerB, "Augmenting Automaton", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index b9c5d2c9e1a..57fc6ec6d13 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -1291,7 +1291,7 @@ public class TestPlayer implements Player { private void printStack(Game game) { System.out.println("Stack objects: " + game.getStack().size()); game.getStack().forEach(stack -> { - System.out.println(stack.getStackAbility().toString()); + System.out.println(stack.getStackAbility().toString() + (stack.isCopy() ? " [copy]" : "")); }); } diff --git a/Mage/src/main/java/mage/game/stack/SpellStack.java b/Mage/src/main/java/mage/game/stack/SpellStack.java index f040f698825..83458c5980b 100644 --- a/Mage/src/main/java/mage/game/stack/SpellStack.java +++ b/Mage/src/main/java/mage/game/stack/SpellStack.java @@ -83,7 +83,7 @@ public class SpellStack extends ArrayDeque { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, source, stackObject.getControllerId()))) { // spells are removed from stack by the card movement if (!(stackObject instanceof Spell) - || stackObject.isCopy()) { // ensure that copies of stackobjects have their history recorded ie: Swan Song + || stackObject.isCopy()) { // !ensure that copies of stackobjects have their history recorded ie: Swan Song this.remove(stackObject, game); game.rememberLKI(Zone.STACK, stackObject); } From b42d65fbb72b748077ac1ae299d36cc3dc5a9541 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 25 Sep 2024 16:57:20 +0400 Subject: [PATCH 13/33] 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 14c39af809e..8402770ab52 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 = 54; - public static final String MAGE_VERSION_RELEASE_INFO = "V2"; // V1, V1a, V1b for releases; V1-beta3, V1-beta4 for betas + public static final String MAGE_VERSION_RELEASE_INFO = "V3"; // V1, V1a, V1b for releases; V1-beta3, V1-beta4 for betas // strict mode // Each update requires a strict version From 26fde06d1611b142d5bbd167b9d296a40e56125b Mon Sep 17 00:00:00 2001 From: Jeff Wadsworth Date: Thu, 26 Sep 2024 16:27:45 -0500 Subject: [PATCH 14/33] Fixed #12900 --- .../src/mage/cards/l/LegionsInitiative.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Mage.Sets/src/mage/cards/l/LegionsInitiative.java b/Mage.Sets/src/mage/cards/l/LegionsInitiative.java index eee0411ff05..f7558cd045c 100644 --- a/Mage.Sets/src/mage/cards/l/LegionsInitiative.java +++ b/Mage.Sets/src/mage/cards/l/LegionsInitiative.java @@ -95,18 +95,19 @@ class LegionsInitiativeExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { return false; } Cards cards = new CardsImpl(); + UUID exileZone = CardUtil.getExileZoneId(game, source); game.getBattlefield().getActivePermanents( StaticFilters.FILTER_CONTROLLED_CREATURE, source.getControllerId(), source, game ).stream().filter(Objects::nonNull).forEach(cards::add); - return player.moveCardsToExile( + return controller.moveCardsToExile( cards.getCards(game), source, game, true, - CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source) + exileZone, CardUtil.getSourceName(game, source) ); } @@ -120,8 +121,8 @@ class LegionsInitiativeReturnFromExileEffect extends OneShotEffect { LegionsInitiativeReturnFromExileEffect() { super(Outcome.PutCardInPlay); - staticText = "return those cards to the battlefield under their owner's control " + - "and those creatures gain haste until end of turn"; + staticText = "return those cards to the battlefield under their owner's control " + + "and those creatures gain haste until end of turn"; } private LegionsInitiativeReturnFromExileEffect(final LegionsInitiativeReturnFromExileEffect effect) { @@ -135,13 +136,15 @@ class LegionsInitiativeReturnFromExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source, -1)); - if (player == null || exileZone == null || exileZone.isEmpty()) { + Player controller = game.getPlayer(source.getControllerId()); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (controller == null + || exileZone == null + || exileZone.isEmpty()) { return false; } Cards cards = new CardsImpl(exileZone); - player.moveCards(cards, Zone.BATTLEFIELD, source, game); + controller.moveCards(cards, Zone.BATTLEFIELD, source, game); List permanents = cards.stream().map(game::getPermanent).filter(Objects::nonNull).collect(Collectors.toList()); if (permanents.isEmpty()) { return false; From 85e4d4d420aded732177d714caa56da2d7784bf2 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Fri, 27 Sep 2024 00:41:39 -0400 Subject: [PATCH 15/33] fix verify --- Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java index a2f2f567558..9cae2f3ee12 100644 --- a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java +++ b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java @@ -134,7 +134,7 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet { cards.add(new SetCardInfo("Harsh Mentor", 165, Rarity.RARE, mage.cards.h.HarshMentor.class)); cards.add(new SetCardInfo("Haywire Mite", 247, Rarity.UNCOMMON, mage.cards.h.HaywireMite.class)); cards.add(new SetCardInfo("Hinterland Harbor", 284, Rarity.RARE, mage.cards.h.HinterlandHarbor.class)); - cards.add(new SetCardInfo("Hornet Queen", 184, Rarity.MYTHIC, mage.cards.h.HornetQueen.class)); + cards.add(new SetCardInfo("Hornet Queen", 184, Rarity.RARE, mage.cards.h.HornetQueen.class)); cards.add(new SetCardInfo("Hydra Omnivore", 185, Rarity.MYTHIC, mage.cards.h.HydraOmnivore.class)); cards.add(new SetCardInfo("Infernal Grasp", 143, Rarity.UNCOMMON, mage.cards.i.InfernalGrasp.class)); cards.add(new SetCardInfo("Inkshield", 221, Rarity.RARE, mage.cards.i.Inkshield.class)); From 571eb69890e9a44b819634c6beb5d25600ec6204 Mon Sep 17 00:00:00 2001 From: TandEm <76133892+T-a-n-d-E-m@users.noreply.github.com> Date: Sun, 29 Sep 2024 01:44:19 +1000 Subject: [PATCH 16/33] GUI, deck editor: improved toolbar icons in dark themes (#12873, #12920) Co-authored-by: TandEm --- .../java/mage/client/cards/CardsList.java | 2 +- .../java/mage/client/cards/DragCardGrid.java | 2 +- .../mage/client/deckeditor/CardSelector.java | 7 +- .../java/mage/client/themes/ThemeType.java | 78 +++++++++++-------- 4 files changed, 51 insertions(+), 38 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/cards/CardsList.java b/Mage.Client/src/main/java/mage/client/cards/CardsList.java index 0b810c4106f..3f6169d2958 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardsList.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardsList.java @@ -114,7 +114,7 @@ panelCardArea.setOpaque(false); cardArea.setOpaque(false); panelCardArea.getViewport().setOpaque(false); - panelControl.setBackground(new Color(250, 250, 250, 150)); + panelControl.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor()); panelControl.setOpaque(true); cbSortBy.setModel(new DefaultComboBoxModel<>(SortBy.values())); } diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java index 53c3492ce60..38b89230725 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java @@ -857,7 +857,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg JPanel toolbar = new JPanel(new BorderLayout()); JPanel toolbarInner = new JPanel(); - toolbar.setBackground(new Color(250, 250, 250, 150)); + toolbar.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor()); toolbar.setOpaque(true); toolbarInner.setOpaque(false); toolbarInner.add(deckNameAndCountLabel); diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java index 1ef48a9400e..cc2c4cc9657 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java @@ -10,6 +10,7 @@ import mage.cards.repository.*; import mage.client.MageFrame; import mage.client.cards.*; import mage.client.constants.Constants.SortBy; +import mage.client.dialog.PreferencesDialog; import mage.client.deckeditor.table.TableModel; import mage.client.dialog.CheckBoxList; import mage.client.util.GUISizeHelper; @@ -97,11 +98,11 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene jTextFieldSearch.addActionListener(searchAction); // make the components more readable - tbColor.setBackground(new Color(250, 250, 250, 150)); + tbColor.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor()); tbColor.setOpaque(true); // false = transparent - tbTypes.setBackground(new Color(250, 250, 250, 150)); + tbTypes.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor()); tbTypes.setOpaque(true); // false = transparent - cardSelectorBottomPanel.setBackground(new Color(250, 250, 250, 150)); + cardSelectorBottomPanel.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor()); cardSelectorBottomPanel.setOpaque(true); // false = transparent } diff --git a/Mage.Client/src/main/java/mage/client/themes/ThemeType.java b/Mage.Client/src/main/java/mage/client/themes/ThemeType.java index b26c4102542..e52404cb491 100644 --- a/Mage.Client/src/main/java/mage/client/themes/ThemeType.java +++ b/Mage.Client/src/main/java/mage/client/themes/ThemeType.java @@ -13,29 +13,29 @@ import java.awt.*; */ public enum ThemeType { // https://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/_nimbusDefaults.html - DEFAULT("Default", - "", - true, - false, - true, - true, - true, - true, - true, - new Color(169, 176, 190), // nimbusBlueGrey - new Color(214, 217, 223), // control - new Color(255, 255, 255), // nimbusLightBackground - new Color(242, 242, 189), // info - new Color(51, 98, 140), // nimbusBase - null, // mageToolbar + DEFAULT("Default", // name + "", // path + true, // hasBackground + false, // hasLoginBackground + true, // hasBattleBackground + true, // hasSkipButtons + true, // hasPhaseIcons + true, // hasWinLossImages + true, // shortcutsVisibleForSkipButtons + new Color(169, 176, 190), // nimbusBlueGrey + new Color(214, 217, 223), // control + new Color(255, 255, 255), // nimbusLightBackground + new Color(242, 242, 189), // info + new Color(51, 98, 140), // nimbusBase + null, // mageToolbar new Color(200, 200, 180, 200), // playerPanel_inactiveBackgroundColor new Color(200, 255, 200, 200), // playerPanel_activeBackgroundColor - new Color(131, 94, 83, 200), // playerPanel_deadBackgroundColor - // card icons - new Color(169, 176, 190), - Color.black, - new Color(51, 98, 140), - Color.black + new Color(131, 94, 83, 200), // playerPanel_deadBackgroundColor + new Color(250, 250, 250, 150), // deckEditorToolbarBackgroundColor + new Color(169, 176, 190), // cardIconsFillColor + Color.black, // cardIconsStrokeColor + new Color(51, 98, 140), // cardIconsTextColor + Color.black // textColor ), GREY("Grey", "grey-theme/", @@ -55,6 +55,7 @@ public enum ThemeType { new Color(172, 172, 172, 200), // playerPanel_inactiveBackgroundColor new Color(180, 234, 180, 200), // playerPanel_activeBackgroundColor new Color(99, 99, 99, 200), // playerPanel_deadBackgroundColor + new Color(250, 250, 250, 150), // deckEditorToolbarBackgroundColor // card icons new Color(158, 158, 158), Color.black, @@ -79,6 +80,7 @@ public enum ThemeType { new Color(243, 233, 164), // playerPanel_inactiveBackgroundColor new Color(204, 236, 201), // playerPanel_activeBackgroundColor new Color(106, 0, 255), // playerPanel_deadBackgroundColor + new Color(250, 250, 250, 150), // deckEditorToolbarBackgroundColor // card icons new Color(246, 136, 158), Color.black, @@ -103,6 +105,7 @@ public enum ThemeType { new Color(219, 193, 172), // playerPanel_inactiveBackgroundColor new Color(204, 236, 201), // playerPanel_activeBackgroundColor new Color(99, 72, 50, 255), // playerPanel_deadBackgroundColor + new Color(250, 250, 250, 150), // deckEditorToolbarBackgroundColor // card icons new Color(219, 193, 172), Color.black, @@ -127,6 +130,7 @@ public enum ThemeType { new Color(172, 195, 219), // playerPanel_inactiveBackgroundColor new Color(204, 236, 201), // playerPanel_activeBackgroundColor new Color(50, 68, 99, 255), // playerPanel_deadBackgroundColor + new Color(250, 250, 250, 150), // deckEditorToolbarBackgroundColor // card icons new Color(172, 197, 219), Color.black, @@ -142,19 +146,20 @@ public enum ThemeType { true, true, true, - new Color(43, 45, 49), // buttons, scrollar background, disabled inputs - new Color(49, 51, 56), // window background - new Color(58, 56, 64), // inputs, table rows - new Color(58, 56, 64), // tooltips - new Color(25, 25, 25), // title bars, scrollbar foreground - new Color(43, 45, 49), // mageToolbar - new Color(43, 45, 49), // playerPanel_inactiveBackgroundColor - new Color(64, 61, 56), // playerPanel_activeBackgroundColor + new Color(43, 45, 49), // buttons, scrollar background, disabled inputs + new Color(49, 51, 56), // window background + new Color(58, 56, 64), // inputs, table rows + new Color(58, 56, 64), // tooltips + new Color(25, 25, 25), // title bars, scrollbar foreground + new Color(43, 45, 49), // mageToolbar + new Color(43, 45, 49), // playerPanel_inactiveBackgroundColor + new Color(64, 61, 56), // playerPanel_activeBackgroundColor new Color(50, 68, 99, 255), // playerPanel_deadBackgroundColor - new Color(172, 197, 219), - Color.BLACK, - new Color(0, 78, 97), - new Color(220, 220, 220) + new Color(50, 50, 50, 250), // deckEditorToolbarBackgroundColor + new Color(172, 197, 219), // cardIconsFillColor + Color.BLACK, // cardIconsStrokeColor + new Color(0, 78, 97), // cardIconsTextColor + new Color(220, 220, 220) // textColor ); private final String name; @@ -175,6 +180,7 @@ public enum ThemeType { private final Color playerPanel_inactiveBackgroundColor; private final Color playerPanel_activeBackgroundColor; private final Color playerPanel_deadBackgroundColor; + private final Color deckEditorToolbarBackgroundColor; // card icons settings (example: flying icon) private final Color cardIconsFillColor; private final Color cardIconsStrokeColor; @@ -201,6 +207,7 @@ public enum ThemeType { Color playerPanel_inactiveBackgroundColor, Color playerPanel_activeBackgroundColor, Color playerPanel_deadBackgroundColor, + Color deckEditorToolbarBackgroundColor, Color cardIconsFillColor, Color cardIconsStrokeColor, Color cardIconsTextColor, @@ -224,6 +231,7 @@ public enum ThemeType { this.playerPanel_activeBackgroundColor = playerPanel_activeBackgroundColor; this.playerPanel_deadBackgroundColor = playerPanel_deadBackgroundColor; this.playerPanel_inactiveBackgroundColor = playerPanel_inactiveBackgroundColor; + this.deckEditorToolbarBackgroundColor = deckEditorToolbarBackgroundColor; this.cardIconsFillColor = cardIconsFillColor; this.cardIconsStrokeColor = cardIconsStrokeColor; this.cardIconsTextColor = cardIconsTextColor; @@ -292,6 +300,10 @@ public enum ThemeType { return playerPanel_deadBackgroundColor; } + public Color getDeckEditorToolbarBackgroundColor() { + return deckEditorToolbarBackgroundColor; + } + private String getImagePath(String imageType, String name) { return "/" + imageType + "/" + path + name; } From 5e1dace9467b5c06362c7842c909abc23f975257 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 28 Sep 2024 21:05:44 +0400 Subject: [PATCH 17/33] GUI: fixed black colored card hint popup on some systems (#12857) --- Mage.Client/src/main/java/mage/client/MageFrame.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 2247134dd55..1c6d6b01218 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -445,7 +445,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { return; } cardInfoPane.setLocation(40, 40); - cardInfoPane.setBackground(new Color(0, 0, 0, 255)); // use non-transparent background to full draw, see bug example in #12261 UI.addComponent(MageComponents.CARD_INFO_PANE, cardInfoPane); MageRoundPane popupContainer = new MageRoundPane(); From 43b28334e6e90c002029a4659a7de23d6bae3edf Mon Sep 17 00:00:00 2001 From: Grath <1895280+Grath@users.noreply.github.com> Date: Sat, 28 Sep 2024 16:11:33 -0400 Subject: [PATCH 18/33] [DSC] Implement Zimone, Mystery Unraveler. --- .../mage/cards/z/ZimoneMysteryUnraveler.java | 89 +++++++++++++++++++ .../sets/DuskmournHouseOfHorrorCommander.java | 1 + .../IfAbilityHasResolvedXTimesEffect.java | 13 ++- 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java diff --git a/Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java b/Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java new file mode 100644 index 00000000000..ac66eb07011 --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java @@ -0,0 +1,89 @@ +package mage.cards.z; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.LandfallAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.IfAbilityHasResolvedXTimesEffect; +import mage.abilities.effects.keyword.ManifestDreadEffect; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.card.FaceDownPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; +import mage.watchers.common.AbilityResolvedWatcher; + +/** + * + * @author Grath + */ +public final class ZimoneMysteryUnraveler extends CardImpl { + + public ZimoneMysteryUnraveler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Landfall -- Whenever a land you control enters, manifest dread if this is the first time this ability has resolved this turn. Otherwise, you may turn a permanent you control face up. + Ability ability = new LandfallAbility(new IfAbilityHasResolvedXTimesEffect( + Outcome.Benefit, 1, new ManifestDreadEffect() + ).setText("manifest dread if this is the first time this ability has resolved this turn."), false); + ability.addEffect(new IfAbilityHasResolvedXTimesEffect(Outcome.Benefit, 2, true, new ZimoneMysteryUnravelerEffect())); + this.addAbility(ability, new AbilityResolvedWatcher()); + } + + private ZimoneMysteryUnraveler(final ZimoneMysteryUnraveler card) { + super(card); + } + + @Override + public ZimoneMysteryUnraveler copy() { + return new ZimoneMysteryUnraveler(this); + } +} + +class ZimoneMysteryUnravelerEffect extends OneShotEffect { + + public ZimoneMysteryUnravelerEffect() { + super(Outcome.Benefit); + this.staticText = "you may turn a permanent you control face up"; + } + + protected ZimoneMysteryUnravelerEffect(final ZimoneMysteryUnravelerEffect effect) { + super(effect); + } + + @Override + public ZimoneMysteryUnravelerEffect copy() { + return new ZimoneMysteryUnravelerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + FilterControlledPermanent filter = new FilterControlledPermanent("a permanent you control"); + filter.add(FaceDownPredicate.instance); + if (controller != null) { + TargetControlledPermanent target = new TargetControlledPermanent(filter); + if (controller.chooseUse(Outcome.Benefit, "Turn a permanent you control face up?", source, game) && controller.choose(Outcome.BoostCreature, target, source, game)) { + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + return permanent.turnFaceUp(source, game, source.getControllerId()); + } + } + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java index 9cae2f3ee12..0c83af0054a 100644 --- a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java +++ b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java @@ -292,5 +292,6 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet { cards.add(new SetCardInfo("Yavimaya Coast", 327, Rarity.RARE, mage.cards.y.YavimayaCoast.class)); cards.add(new SetCardInfo("Yavimaya Elder", 208, Rarity.COMMON, mage.cards.y.YavimayaElder.class)); cards.add(new SetCardInfo("Yedora, Grave Gardener", 209, Rarity.UNCOMMON, mage.cards.y.YedoraGraveGardener.class)); + cards.add(new SetCardInfo("Zimone, Mystery Unraveler", 8, Rarity.MYTHIC, mage.cards.z.ZimoneMysteryUnraveler.class)); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java b/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java index dd89bb9ad8f..40f0d879b58 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java @@ -18,21 +18,28 @@ public class IfAbilityHasResolvedXTimesEffect extends OneShotEffect { private final int resolutionNumber; private final Effects effects; + private final boolean orMore; public IfAbilityHasResolvedXTimesEffect(int resolutionNumber, Effect effect) { this(effect.getOutcome(), resolutionNumber, effect); } public IfAbilityHasResolvedXTimesEffect(Outcome outcome, int resolutionNumber, Effect... effects) { + this(outcome, resolutionNumber, false, effects); + } + + public IfAbilityHasResolvedXTimesEffect(Outcome outcome, int resolutionNumber, boolean orMore, Effect... effects) { super(outcome); this.resolutionNumber = resolutionNumber; this.effects = new Effects(effects); + this.orMore = orMore; } private IfAbilityHasResolvedXTimesEffect(final IfAbilityHasResolvedXTimesEffect effect) { super(effect); this.resolutionNumber = effect.resolutionNumber; this.effects = effect.effects.copy(); + this.orMore = effect.orMore; } @Override @@ -42,7 +49,8 @@ public class IfAbilityHasResolvedXTimesEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - if (AbilityResolvedWatcher.getResolutionCount(game, source) != resolutionNumber) { + int resolutionCount = AbilityResolvedWatcher.getResolutionCount(game, source); + if (resolutionCount < resolutionNumber || (!orMore && resolutionCount > resolutionNumber)) { return false; } boolean result = false; @@ -62,6 +70,9 @@ public class IfAbilityHasResolvedXTimesEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } + if (orMore) { + return "otherwise, " + effects.getText(mode); + } return "if this is the " + CardUtil.numberToOrdinalText(resolutionNumber) + " time this ability has resolved this turn, " + effects.getText(mode); } From efed3a3715912a17f7d3880afe1a319f4cce57af Mon Sep 17 00:00:00 2001 From: Grath <1895280+Grath@users.noreply.github.com> Date: Sat, 28 Sep 2024 16:24:06 -0400 Subject: [PATCH 19/33] Smoother targeting of Zimone's option turn-face-up. --- Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java b/Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java index ac66eb07011..dc5c08d4de3 100644 --- a/Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java +++ b/Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java @@ -76,13 +76,9 @@ class ZimoneMysteryUnravelerEffect extends OneShotEffect { FilterControlledPermanent filter = new FilterControlledPermanent("a permanent you control"); filter.add(FaceDownPredicate.instance); if (controller != null) { - TargetControlledPermanent target = new TargetControlledPermanent(filter); - if (controller.chooseUse(Outcome.Benefit, "Turn a permanent you control face up?", source, game) && controller.choose(Outcome.BoostCreature, target, source, game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - return permanent.turnFaceUp(source, game, source.getControllerId()); - } - } + TargetControlledPermanent target = new TargetControlledPermanent(0, 1, filter, true); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + return permanent != null && permanent.turnFaceUp(source, game, source.getControllerId()); } return false; } From 554ba8739e93ce1c3b50bf6243906fd288d422d0 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Sat, 28 Sep 2024 22:25:16 -0400 Subject: [PATCH 20/33] fix #12928 (The Mindskinner) --- .../src/mage/cards/t/TheMindskinner.java | 3 +- .../prevent/TheMindskinnerTest.java | 39 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/TheMindskinnerTest.java diff --git a/Mage.Sets/src/mage/cards/t/TheMindskinner.java b/Mage.Sets/src/mage/cards/t/TheMindskinner.java index 98580d540aa..067e522d99c 100644 --- a/Mage.Sets/src/mage/cards/t/TheMindskinner.java +++ b/Mage.Sets/src/mage/cards/t/TheMindskinner.java @@ -66,11 +66,12 @@ class TheMindskinnerEffect extends PreventionEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { + int amount = event.getAmount(); preventDamageAction(event, source, game); for (UUID playerId : game.getOpponents(source.getControllerId())) { Player player = game.getPlayer(playerId); if (player != null) { - player.millCards(event.getAmount(), source, game); + player.millCards(amount, source, game); } } return true; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/TheMindskinnerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/TheMindskinnerTest.java new file mode 100644 index 00000000000..ff076cfbc91 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/TheMindskinnerTest.java @@ -0,0 +1,39 @@ +package org.mage.test.cards.replacement.prevent; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class TheMindskinnerTest extends CardTestPlayerBase { + + private static final String mindskinner = "The Mindskinner"; // 10/1 can't be blocked + // If a source you control would deal damage to an opponent, prevent that damage and each opponent mills that many cards. + + private static final String piker = "Goblin Piker"; // 2/1 + + private static final String bolt = "Lightning Bolt"; + + @Test + public void testPreventionAndMill() { + addCard(Zone.BATTLEFIELD, playerA, mindskinner); + addCard(Zone.BATTLEFIELD, playerA, piker); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB); + + attack(1, playerA, piker, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, 1); // bolt + assertGraveyardCount(playerB, 5); + } + +} From 2b9b1c01bde5382a40b4be5a98e328e123ec4581 Mon Sep 17 00:00:00 2001 From: karapuzz14 <149368028+karapuzz14@users.noreply.github.com> Date: Sun, 29 Sep 2024 05:26:24 +0300 Subject: [PATCH 21/33] [WOC] Implement Tegwyll's Scouring (#12891) * Also add all reprints from Wilds of Eldraine Commander. --- .../src/mage/cards/t/TegwyllsScouring.java | 63 ++++++++++++++ .../mage/sets/WildsOfEldraineCommander.java | 85 +++++++++++++------ ...yMoreToCastAsThoughtItHadFlashAbility.java | 25 ++++-- 3 files changed, 139 insertions(+), 34 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/t/TegwyllsScouring.java diff --git a/Mage.Sets/src/mage/cards/t/TegwyllsScouring.java b/Mage.Sets/src/mage/cards/t/TegwyllsScouring.java new file mode 100644 index 00000000000..b01786f5bbe --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TegwyllsScouring.java @@ -0,0 +1,63 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.PayMoreToCastAsThoughtItHadFlashAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.permanent.token.FaerieRogueToken; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author karapuzz14 + */ +public final class TegwyllsScouring extends CardImpl { + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creatures you control with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + filter.add(TappedPredicate.UNTAPPED); + } + + public TegwyllsScouring(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{B}"); + + + // You may cast Tegwyll's Scouring as though it had flash by tapping three untapped creatures you control with flying in addition to paying its other costs. + Cost asThoughCost = new TapTargetCost(new TargetControlledCreaturePermanent(3, 3, filter, true)).setText(""); + CostsImpl costs = new CostsImpl<>().setText("tapping three untapped creatures you control with flying"); + costs.add(asThoughCost); + + Ability ability = new PayMoreToCastAsThoughtItHadFlashAbility(this, costs); + ability.addEffect(new DestroyAllEffect(StaticFilters.FILTER_PERMANENT_CREATURES)); + ability.addEffect(new CreateTokenEffect(new FaerieRogueToken(), 3)); + this.addAbility(ability); + + // Destroy all creatures. + this.getSpellAbility().addEffect(new DestroyAllEffect(StaticFilters.FILTER_PERMANENT_CREATURES)); + + //Create three 1/1 black Faerie Rogue creature tokens with flying. + this.getSpellAbility().addEffect(new CreateTokenEffect(new FaerieRogueToken(), 3)); + } + + private TegwyllsScouring(final TegwyllsScouring card) { + super(card); + } + + @Override + public TegwyllsScouring copy() { + return new TegwyllsScouring(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java b/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java index e074d014b12..3e26046f505 100644 --- a/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java +++ b/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java @@ -20,20 +20,24 @@ public final class WildsOfEldraineCommander extends ExpansionSet { this.hasBasicLands = false; cards.add(new SetCardInfo("Ajani's Chosen", 59, Rarity.RARE, mage.cards.a.AjanisChosen.class)); - cards.add(new SetCardInfo("Alela, Cunning Conqueror", 3, Rarity.MYTHIC, mage.cards.a.AlelaCunningConqueror.class)); + cards.add(new SetCardInfo("Alela, Cunning Conqueror", 3, Rarity.MYTHIC, mage.cards.a.AlelaCunningConqueror.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Alela, Cunning Conqueror", 34, Rarity.MYTHIC, mage.cards.a.AlelaCunningConqueror.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ancestral Mask", 119, Rarity.COMMON, mage.cards.a.AncestralMask.class)); cards.add(new SetCardInfo("Angelic Destiny", 60, Rarity.MYTHIC, mage.cards.a.AngelicDestiny.class)); cards.add(new SetCardInfo("Arcane Denial", 84, Rarity.COMMON, mage.cards.a.ArcaneDenial.class)); cards.add(new SetCardInfo("Arcane Signet", 145, Rarity.COMMON, mage.cards.a.ArcaneSignet.class)); - cards.add(new SetCardInfo("Archmage of Echoes", 9, Rarity.RARE, mage.cards.a.ArchmageOfEchoes.class)); + cards.add(new SetCardInfo("Archmage of Echoes", 9, Rarity.RARE, mage.cards.a.ArchmageOfEchoes.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Archmage of Echoes", 45, Rarity.RARE, mage.cards.a.ArchmageOfEchoes.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Archon of Sun's Grace", 61, Rarity.RARE, mage.cards.a.ArchonOfSunsGrace.class)); cards.add(new SetCardInfo("Aura Gnarlid", 120, Rarity.COMMON, mage.cards.a.AuraGnarlid.class)); cards.add(new SetCardInfo("Austere Command", 62, Rarity.RARE, mage.cards.a.AustereCommand.class)); cards.add(new SetCardInfo("Bear Umbra", 121, Rarity.RARE, mage.cards.b.BearUmbra.class)); - cards.add(new SetCardInfo("Blightwing Bandit", 13, Rarity.RARE, mage.cards.b.BlightwingBandit.class)); + cards.add(new SetCardInfo("Blightwing Bandit", 13, Rarity.RARE, mage.cards.b.BlightwingBandit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blightwing Bandit", 49, Rarity.RARE, mage.cards.b.BlightwingBandit.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bojuka Bog", 152, Rarity.COMMON, mage.cards.b.BojukaBog.class)); cards.add(new SetCardInfo("Brazen Borrower", 85, Rarity.MYTHIC, mage.cards.b.BrazenBorrower.class)); - cards.add(new SetCardInfo("Brenard, Ginger Sculptor", 27, Rarity.MYTHIC, mage.cards.b.BrenardGingerSculptor.class)); + cards.add(new SetCardInfo("Brenard, Ginger Sculptor", 27, Rarity.MYTHIC, mage.cards.b.BrenardGingerSculptor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Brenard, Ginger Sculptor", 35, Rarity.MYTHIC, mage.cards.b.BrenardGingerSculptor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Canopy Vista", 153, Rarity.RARE, mage.cards.c.CanopyVista.class)); cards.add(new SetCardInfo("Careful Cultivation", 122, Rarity.COMMON, mage.cards.c.CarefulCultivation.class)); cards.add(new SetCardInfo("Castle Ardenvale", 154, Rarity.RARE, mage.cards.c.CastleArdenvale.class)); @@ -42,11 +46,16 @@ public final class WildsOfEldraineCommander extends ExpansionSet { cards.add(new SetCardInfo("Cloud of Faeries", 86, Rarity.COMMON, mage.cards.c.CloudOfFaeries.class)); cards.add(new SetCardInfo("Command Tower", 156, Rarity.COMMON, mage.cards.c.CommandTower.class)); cards.add(new SetCardInfo("Consider", 87, Rarity.COMMON, mage.cards.c.Consider.class)); - cards.add(new SetCardInfo("Court of Ardenvale", 21, Rarity.RARE, mage.cards.c.CourtOfArdenvale.class)); - cards.add(new SetCardInfo("Court of Embereth", 24, Rarity.RARE, mage.cards.c.CourtOfEmbereth.class)); - cards.add(new SetCardInfo("Court of Garenbrig", 25, Rarity.RARE, mage.cards.c.CourtOfGarenbrig.class)); - cards.add(new SetCardInfo("Court of Locthwain", 23, Rarity.RARE, mage.cards.c.CourtOfLocthwain.class)); - cards.add(new SetCardInfo("Court of Vantress", 22, Rarity.RARE, mage.cards.c.CourtOfVantress.class)); + cards.add(new SetCardInfo("Court of Ardenvale", 21, Rarity.RARE, mage.cards.c.CourtOfArdenvale.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Court of Ardenvale", 29, Rarity.RARE, mage.cards.c.CourtOfArdenvale.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Court of Embereth", 24, Rarity.RARE, mage.cards.c.CourtOfEmbereth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Court of Embereth", 32, Rarity.RARE, mage.cards.c.CourtOfEmbereth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Court of Garenbrig", 25, Rarity.RARE, mage.cards.c.CourtOfGarenbrig.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Court of Garenbrig", 33, Rarity.RARE, mage.cards.c.CourtOfGarenbrig.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Court of Locthwain", 23, Rarity.RARE, mage.cards.c.CourtOfLocthwain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Court of Locthwain", 31, Rarity.RARE, mage.cards.c.CourtOfLocthwain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Court of Vantress", 22, Rarity.RARE, mage.cards.c.CourtOfVantress.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Court of Vantress", 30, Rarity.RARE, mage.cards.c.CourtOfVantress.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Danitha Capashen, Paragon", 64, Rarity.UNCOMMON, mage.cards.d.DanithaCapashenParagon.class)); cards.add(new SetCardInfo("Darkwater Catacombs", 157, Rarity.RARE, mage.cards.d.DarkwaterCatacombs.class)); cards.add(new SetCardInfo("Daybreak Coronet", 65, Rarity.RARE, mage.cards.d.DaybreakCoronet.class)); @@ -57,12 +66,15 @@ public final class WildsOfEldraineCommander extends ExpansionSet { cards.add(new SetCardInfo("Distant Melody", 89, Rarity.COMMON, mage.cards.d.DistantMelody.class)); cards.add(new SetCardInfo("Eidolon of Blossoms", 124, Rarity.RARE, mage.cards.e.EidolonOfBlossoms.class)); cards.add(new SetCardInfo("Eidolon of Countless Battles", 66, Rarity.RARE, mage.cards.e.EidolonOfCountlessBattles.class)); - cards.add(new SetCardInfo("Ellivere of the Wild Court", 2, Rarity.MYTHIC, mage.cards.e.EllivereOfTheWildCourt.class)); + cards.add(new SetCardInfo("Ellivere of the Wild Court", 2, Rarity.MYTHIC, mage.cards.e.EllivereOfTheWildCourt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ellivere of the Wild Court", 36, Rarity.MYTHIC, mage.cards.e.EllivereOfTheWildCourt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ellivere of the Wild Court", 57, Rarity.MYTHIC, mage.cards.e.EllivereOfTheWildCourt.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Enchantress's Presence", 125, Rarity.RARE, mage.cards.e.EnchantresssPresence.class)); cards.add(new SetCardInfo("Ethereal Armor", 67, Rarity.COMMON, mage.cards.e.EtherealArmor.class)); cards.add(new SetCardInfo("Exotic Orchard", 159, Rarity.RARE, mage.cards.e.ExoticOrchard.class)); cards.add(new SetCardInfo("Fact or Fiction", 90, Rarity.UNCOMMON, mage.cards.f.FactOrFiction.class)); - cards.add(new SetCardInfo("Faerie Bladecrafter", 14, Rarity.RARE, mage.cards.f.FaerieBladecrafter.class)); + cards.add(new SetCardInfo("Faerie Bladecrafter", 14, Rarity.RARE, mage.cards.f.FaerieBladecrafter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Faerie Bladecrafter", 50, Rarity.RARE, mage.cards.f.FaerieBladecrafter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Faerie Conclave", 160, Rarity.UNCOMMON, mage.cards.f.FaerieConclave.class)); cards.add(new SetCardInfo("Faerie Formation", 91, Rarity.RARE, mage.cards.f.FaerieFormation.class)); cards.add(new SetCardInfo("Faerie Seer", 92, Rarity.COMMON, mage.cards.f.FaerieSeer.class)); @@ -71,10 +83,12 @@ public final class WildsOfEldraineCommander extends ExpansionSet { cards.add(new SetCardInfo("Fortified Village", 161, Rarity.RARE, mage.cards.f.FortifiedVillage.class)); cards.add(new SetCardInfo("Frantic Search", 93, Rarity.COMMON, mage.cards.f.FranticSearch.class)); cards.add(new SetCardInfo("Generous Gift", 68, Rarity.UNCOMMON, mage.cards.g.GenerousGift.class)); - cards.add(new SetCardInfo("Giant Inheritance", 17, Rarity.RARE, mage.cards.g.GiantInheritance.class)); + cards.add(new SetCardInfo("Giant Inheritance", 17, Rarity.RARE, mage.cards.g.GiantInheritance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Giant Inheritance", 53, Rarity.RARE, mage.cards.g.GiantInheritance.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Glen Elendra Archmage", 94, Rarity.RARE, mage.cards.g.GlenElendraArchmage.class)); cards.add(new SetCardInfo("Glen Elendra Liege", 138, Rarity.RARE, mage.cards.g.GlenElendraLiege.class)); - cards.add(new SetCardInfo("Gylwain, Casting Director", 4, Rarity.MYTHIC, mage.cards.g.GylwainCastingDirector.class)); + cards.add(new SetCardInfo("Gylwain, Casting Director", 4, Rarity.MYTHIC, mage.cards.g.GylwainCastingDirector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gylwain, Casting Director", 37, Rarity.MYTHIC, mage.cards.g.GylwainCastingDirector.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hall of Heliod's Generosity", 162, Rarity.RARE, mage.cards.h.HallOfHeliodsGenerosity.class)); cards.add(new SetCardInfo("Halo Forager", 139, Rarity.UNCOMMON, mage.cards.h.HaloForager.class)); cards.add(new SetCardInfo("Hullbreaker Horror", 95, Rarity.RARE, mage.cards.h.HullbreakerHorror.class)); @@ -85,25 +99,33 @@ public final class WildsOfEldraineCommander extends ExpansionSet { cards.add(new SetCardInfo("Keep Watch", 98, Rarity.COMMON, mage.cards.k.KeepWatch.class)); cards.add(new SetCardInfo("Kenrith's Transformation", 128, Rarity.UNCOMMON, mage.cards.k.KenrithsTransformation.class)); cards.add(new SetCardInfo("Kindred Dominance", 113, Rarity.RARE, mage.cards.k.KindredDominance.class)); - cards.add(new SetCardInfo("Knickknack Ouphe", 18, Rarity.RARE, mage.cards.k.KnickknackOuphe.class)); + cards.add(new SetCardInfo("Knickknack Ouphe", 18, Rarity.RARE, mage.cards.k.KnickknackOuphe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Knickknack Ouphe", 54, Rarity.RARE, mage.cards.k.KnickknackOuphe.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kor Spiritdancer", 69, Rarity.RARE, mage.cards.k.KorSpiritdancer.class)); - cards.add(new SetCardInfo("Korvold, Gleeful Glutton", 26, Rarity.MYTHIC, mage.cards.k.KorvoldGleefulGlutton.class)); + cards.add(new SetCardInfo("Korvold, Gleeful Glutton", 26, Rarity.MYTHIC, mage.cards.k.KorvoldGleefulGlutton.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Korvold, Gleeful Glutton", 38, Rarity.MYTHIC, mage.cards.k.KorvoldGleefulGlutton.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Krosan Verge", 163, Rarity.UNCOMMON, mage.cards.k.KrosanVerge.class)); - cards.add(new SetCardInfo("Liberated Livestock", 5, Rarity.RARE, mage.cards.l.LiberatedLivestock.class)); - cards.add(new SetCardInfo("Loamcrafter Faun", 19, Rarity.RARE, mage.cards.l.LoamcrafterFaun.class)); - cards.add(new SetCardInfo("Malleable Impostor", 10, Rarity.RARE, mage.cards.m.MalleableImpostor.class)); + cards.add(new SetCardInfo("Liberated Livestock", 5, Rarity.RARE, mage.cards.l.LiberatedLivestock.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Liberated Livestock", 41, Rarity.RARE, mage.cards.l.LiberatedLivestock.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Loamcrafter Faun", 19, Rarity.RARE, mage.cards.l.LoamcrafterFaun.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Loamcrafter Faun", 55, Rarity.RARE, mage.cards.l.LoamcrafterFaun.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Malleable Impostor", 10, Rarity.RARE, mage.cards.m.MalleableImpostor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Malleable Impostor", 46, Rarity.RARE, mage.cards.m.MalleableImpostor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mantle of the Ancients", 70, Rarity.RARE, mage.cards.m.MantleOfTheAncients.class)); cards.add(new SetCardInfo("Midnight Clock", 99, Rarity.RARE, mage.cards.m.MidnightClock.class)); cards.add(new SetCardInfo("Mind Stone", 148, Rarity.UNCOMMON, mage.cards.m.MindStone.class)); - cards.add(new SetCardInfo("Misleading Signpost", 11, Rarity.RARE, mage.cards.m.MisleadingSignpost.class)); + cards.add(new SetCardInfo("Misleading Signpost", 11, Rarity.RARE, mage.cards.m.MisleadingSignpost.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Misleading Signpost", 47, Rarity.RARE, mage.cards.m.MisleadingSignpost.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Myriad Landscape", 164, Rarity.UNCOMMON, mage.cards.m.MyriadLandscape.class)); - cards.add(new SetCardInfo("Nettling Nuisance", 15, Rarity.RARE, mage.cards.n.NettlingNuisance.class)); + cards.add(new SetCardInfo("Nettling Nuisance", 15, Rarity.RARE, mage.cards.n.NettlingNuisance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nettling Nuisance", 51, Rarity.RARE, mage.cards.n.NettlingNuisance.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nightmare Unmaking", 114, Rarity.RARE, mage.cards.n.NightmareUnmaking.class)); cards.add(new SetCardInfo("Nightveil Sprite", 100, Rarity.UNCOMMON, mage.cards.n.NightveilSprite.class)); cards.add(new SetCardInfo("Nymris, Oona's Trickster", 141, Rarity.RARE, mage.cards.n.NymrisOonasTrickster.class)); cards.add(new SetCardInfo("Oona, Queen of the Fae", 142, Rarity.RARE, mage.cards.o.OonaQueenOfTheFae.class)); cards.add(new SetCardInfo("Opt", 101, Rarity.COMMON, mage.cards.o.Opt.class)); - cards.add(new SetCardInfo("Ox Drover", 6, Rarity.RARE, mage.cards.o.OxDrover.class)); + cards.add(new SetCardInfo("Ox Drover", 6, Rarity.RARE, mage.cards.o.OxDrover.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ox Drover", 42, Rarity.RARE, mage.cards.o.OxDrover.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Paradise Druid", 129, Rarity.UNCOMMON, mage.cards.p.ParadiseDruid.class)); cards.add(new SetCardInfo("Path of Ancestry", 165, Rarity.COMMON, mage.cards.p.PathOfAncestry.class)); cards.add(new SetCardInfo("Perplexing Test", 102, Rarity.RARE, mage.cards.p.PerplexingTest.class)); @@ -125,13 +147,15 @@ public final class WildsOfEldraineCommander extends ExpansionSet { cards.add(new SetCardInfo("Scion of Oona", 109, Rarity.RARE, mage.cards.s.ScionOfOona.class)); cards.add(new SetCardInfo("Secluded Glen", 166, Rarity.RARE, mage.cards.s.SecludedGlen.class)); cards.add(new SetCardInfo("Setessan Champion", 132, Rarity.RARE, mage.cards.s.SetessanChampion.class)); - cards.add(new SetCardInfo("Shadow Puppeteers", 12, Rarity.RARE, mage.cards.s.ShadowPuppeteers.class)); + cards.add(new SetCardInfo("Shadow Puppeteers", 12, Rarity.RARE, mage.cards.s.ShadowPuppeteers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadow Puppeteers", 48, Rarity.RARE, mage.cards.s.ShadowPuppeteers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shalai, Voice of Plenty", 74, Rarity.RARE, mage.cards.s.ShalaiVoiceOfPlenty.class)); cards.add(new SetCardInfo("Siona, Captain of the Pyleas", 144, Rarity.UNCOMMON, mage.cards.s.SionaCaptainOfThePyleas.class)); cards.add(new SetCardInfo("Snake Umbra", 133, Rarity.UNCOMMON, mage.cards.s.SnakeUmbra.class)); cards.add(new SetCardInfo("Snap", 110, Rarity.COMMON, mage.cards.s.Snap.class)); cards.add(new SetCardInfo("Sol Ring", 149, Rarity.UNCOMMON, mage.cards.s.SolRing.class)); - cards.add(new SetCardInfo("Songbirds' Blessing", 7, Rarity.RARE, mage.cards.s.SongbirdsBlessing.class)); + cards.add(new SetCardInfo("Songbirds' Blessing", 7, Rarity.RARE, mage.cards.s.SongbirdsBlessing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Songbirds' Blessing", 43, Rarity.RARE, mage.cards.s.SongbirdsBlessing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sower of Temptation", 111, Rarity.RARE, mage.cards.s.SowerOfTemptation.class)); cards.add(new SetCardInfo("Spectral Steel", 75, Rarity.UNCOMMON, mage.cards.s.SpectralSteel.class)); cards.add(new SetCardInfo("Starfield Mystic", 76, Rarity.RARE, mage.cards.s.StarfieldMystic.class)); @@ -142,19 +166,26 @@ public final class WildsOfEldraineCommander extends ExpansionSet { cards.add(new SetCardInfo("Sylvan Ranger", 134, Rarity.COMMON, mage.cards.s.SylvanRanger.class)); cards.add(new SetCardInfo("Tainted Isle", 169, Rarity.UNCOMMON, mage.cards.t.TaintedIsle.class)); cards.add(new SetCardInfo("Talisman of Dominance", 150, Rarity.UNCOMMON, mage.cards.t.TalismanOfDominance.class)); - cards.add(new SetCardInfo("Tegwyll, Duke of Splendor", 1, Rarity.MYTHIC, mage.cards.t.TegwyllDukeOfSplendor.class)); + cards.add(new SetCardInfo("Tegwyll, Duke of Splendor", 1, Rarity.MYTHIC, mage.cards.t.TegwyllDukeOfSplendor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tegwyll, Duke of Splendor", 39, Rarity.MYTHIC, mage.cards.t.TegwyllDukeOfSplendor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tegwyll, Duke of Splendor", 58, Rarity.MYTHIC, mage.cards.t.TegwyllDukeOfSplendor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tegwyll's Scouring", 16, Rarity.RARE, mage.cards.t.TegwyllsScouring.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tegwyll's Scouring", 52, Rarity.RARE, mage.cards.t.TegwyllsScouring.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Temple of Deceit", 170, Rarity.RARE, mage.cards.t.TempleOfDeceit.class)); cards.add(new SetCardInfo("Temple of Plenty", 171, Rarity.RARE, mage.cards.t.TempleOfPlenty.class)); cards.add(new SetCardInfo("Temple of the False God", 172, Rarity.UNCOMMON, mage.cards.t.TempleOfTheFalseGod.class)); cards.add(new SetCardInfo("Theoretical Duplication", 112, Rarity.RARE, mage.cards.t.TheoreticalDuplication.class)); cards.add(new SetCardInfo("Thrilling Encore", 118, Rarity.RARE, mage.cards.t.ThrillingEncore.class)); - cards.add(new SetCardInfo("Throne of Eldraine", 28, Rarity.RARE, mage.cards.t.ThroneOfEldraine.class)); - cards.add(new SetCardInfo("Timber Paladin", 20, Rarity.RARE, mage.cards.t.TimberPaladin.class)); + cards.add(new SetCardInfo("Throne of Eldraine", 28, Rarity.RARE, mage.cards.t.ThroneOfEldraine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Throne of Eldraine", 40, Rarity.RARE, mage.cards.t.ThroneOfEldraine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Timber Paladin", 20, Rarity.RARE, mage.cards.t.TimberPaladin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Timber Paladin", 56, Rarity.RARE, mage.cards.t.TimberPaladin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Timely Ward", 79, Rarity.RARE, mage.cards.t.TimelyWard.class)); cards.add(new SetCardInfo("Tithe Taker", 80, Rarity.RARE, mage.cards.t.TitheTaker.class)); cards.add(new SetCardInfo("Transcendent Envoy", 81, Rarity.COMMON, mage.cards.t.TranscendentEnvoy.class)); cards.add(new SetCardInfo("Umbra Mystic", 82, Rarity.RARE, mage.cards.u.UmbraMystic.class)); - cards.add(new SetCardInfo("Unfinished Business", 8, Rarity.RARE, mage.cards.u.UnfinishedBusiness.class)); + cards.add(new SetCardInfo("Unfinished Business", 8, Rarity.RARE, mage.cards.u.UnfinishedBusiness.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Unfinished Business", 44, Rarity.RARE, mage.cards.u.UnfinishedBusiness.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Utopia Sprawl", 135, Rarity.COMMON, mage.cards.u.UtopiaSprawl.class)); cards.add(new SetCardInfo("Verdant Embrace", 136, Rarity.RARE, mage.cards.v.VerdantEmbrace.class)); cards.add(new SetCardInfo("Vitu-Ghazi, the City-Tree", 173, Rarity.UNCOMMON, mage.cards.v.VituGhaziTheCityTree.class)); diff --git a/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java b/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java index 7fc7d21b35c..0ba8afb443a 100644 --- a/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java @@ -1,6 +1,7 @@ package mage.abilities.common; import mage.abilities.SpellAbility; +import mage.abilities.costs.Cost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.cards.Card; @@ -14,14 +15,21 @@ import mage.util.CardUtil; */ public class PayMoreToCastAsThoughtItHadFlashAbility extends SpellAbility { - private final ManaCosts costsToAdd; + private final Cost costsToAdd; - public PayMoreToCastAsThoughtItHadFlashAbility(Card card, ManaCosts costsToAdd) { - super(card.getSpellAbility().getManaCosts().copy(), card.getName() + " as though it had flash", Zone.HAND, SpellAbilityType.BASE_ALTERNATE); + public PayMoreToCastAsThoughtItHadFlashAbility(Card card, Cost costsToAdd) { + super(card.getSpellAbility().getManaCosts().copy(), card.getName(), Zone.HAND, SpellAbilityType.BASE_ALTERNATE); this.costsToAdd = costsToAdd; this.timing = TimingRule.INSTANT; this.setRuleAtTheTop(true); - CardUtil.increaseCost(this, costsToAdd); + + if(costsToAdd instanceof ManaCosts) { + ManaCosts manaCosts = (ManaCosts) costsToAdd; + CardUtil.increaseCost(this, manaCosts); + } + else { + this.addCost(costsToAdd); + } } protected PayMoreToCastAsThoughtItHadFlashAbility(final PayMoreToCastAsThoughtItHadFlashAbility ability) { @@ -38,10 +46,13 @@ public class PayMoreToCastAsThoughtItHadFlashAbility extends SpellAbility { public String getRule(boolean all) { return getRule(); } - @Override public String getRule() { - return "You may cast {this} as though it had flash if you pay " + costsToAdd.getText() + " more to cast it. (You may cast it any time you could cast an instant.)"; + if (costsToAdd instanceof ManaCosts) { + return "You may cast {this} as though it had flash if you pay " + costsToAdd.getText() + " more to cast it. (You may cast it any time you could cast an instant.)"; + } else { + return "You may cast {this} as though it had flash by " + costsToAdd.getText() + " in addition to paying its other costs."; + } } -} +} \ No newline at end of file From 815feae74452a5def9419dd74df8d552f0978003 Mon Sep 17 00:00:00 2001 From: tiera3 <87589219+tiera3@users.noreply.github.com> Date: Sat, 28 Sep 2024 10:21:47 +1000 Subject: [PATCH 22/33] [ONE] booster collation closes #12921 --- .../src/mage/sets/PhyrexiaAllWillBeOne.java | 87 ++++++++++++++++++- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOne.java b/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOne.java index 9ec1ebe22b2..00b975ce555 100644 --- a/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOne.java +++ b/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOne.java @@ -3,7 +3,13 @@ package mage.sets; import mage.cards.ExpansionSet; import mage.constants.Rarity; import mage.constants.SetType; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; +import java.util.ArrayList; +import java.util.List; /** * @author TheElk801 */ @@ -508,8 +514,81 @@ public final class PhyrexiaAllWillBeOne extends ExpansionSet { cards.add(new SetCardInfo("Zopandrel, Hunger Dominus", 458, Rarity.MYTHIC, mage.cards.z.ZopandrelHungerDominus.class, NON_FULL_USE_VARIOUS)); } -// @Override -// public BoosterCollator createCollator() { -// return new PhyrexiaAllWillBeOneCollator(); -// } + @Override + public BoosterCollator createCollator() { + return new PhyrexiaAllWillBeOneCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/one.html +// Using Japanese collation for common, no collation for uncommons +// Using 2:1 for each rare:mythic which results in (1/7) 14.29% mythic packs, matching the advertised ~14% +class PhyrexiaAllWillBeOneCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "54", "251", "44", "77", "243", "49", "228", "45", "231", "55", "260", "58", "60", "238", "65", "187", "54", "253", "59", "252", "52", "44", "224", "62", "251", "50", "139", "48", "236", "77", "238", "59", "53", "253", "65", "50", "187", "60", "228", "48", "260", "55", "251", "53", "231", "58", "252", "49", "243", "45", "224", "52", "236", "54", "139", "62", "44", "252", "58", "48", "238", "55", "243", "59", "139", "52", "260", "62", "54", "231", "50", "228", "44", "253", "77", "224", "49", "251", "65", "60", "236", "53", "187", "45", "228", "55", "52", "253", "50", "260", "77", "236", "58", "238", "45", "252", "65", "139", "49", "187", "59", "243", "48", "62", "231", "60", "53", "224"); + private final CardRun commonB = new CardRun(true, "16", "94", "111", "15", "7", "97", "12", "96", "102", "20", "120", "110", "9", "25", "103", "6", "40", "94", "4", "247", "117", "13", "137", "88", "39", "225", "109", "22", "151", "116", "28", "226", "92", "21", "12", "97", "8", "20", "111", "261", "39", "89", "28", "36", "117", "21", "120", "116", "8", "247", "103", "13", "40", "110", "22", "16", "102", "225", "4", "88", "151", "9", "109", "226", "7", "96", "137", "25", "92", "36", "6", "116", "261", "15", "109", "21", "39", "110", "16", "13", "102", "22", "137", "94", "25", "15", "111", "247", "8", "89", "12", "225", "117", "9", "120", "97", "7", "261", "103", "36", "40", "88", "28", "20", "92", "226", "4", "96", "151", "6", "89"); + private final CardRun commonC = new CardRun(true, "81", "165", "147", "188", "286", "126", "162", "173", "130", "180", "66", "122", "164", "157", "123", "160", "85", "156", "174", "237", "121", "170", "130", "188", "123", "185", "290", "122", "181", "179", "133", "157", "81", "135", "180", "182", "148", "66", "170", "126", "32", "162", "156", "235", "173", "141", "183", "80", "147", "174", "160", "131", "165", "164", "135", "32", "177", "155", "289", "180", "121", "181", "80", "131", "179", "235", "133", "188", "81", "130", "170", "121", "174", "80", "123", "177", "148", "182", "292", "165", "86", "141", "160", "237", "155", "185", "173", "148", "235", "164", "122", "287", "162", "135", "86", "157", "156", "183", "141", "177", "237", "133", "179", "185", "126", "85", "295", "155", "182", "183", "131"); + private final CardRun uncommon = new CardRun(false, "1", "79", "2", "3", "158", "41", "223", "119", "83", "5", "197", "124", "161", "198", "199", "87", "127", "200", "46", "91", "167", "168", "129", "93", "51", "132", "134", "136", "14", "230", "171", "172", "17", "18", "140", "64", "234", "142", "99", "212", "100", "101", "176", "26", "143", "178", "29", "30", "239", "106", "144", "67", "31", "146", "240", "107", "215", "68", "108", "216", "35", "184", "217", "69", "70", "72", "73", "74", "190", "76", "191", "152", "112", "113", "37", "193", "194", "220", "221", "78"); + private final CardRun rare = new CardRun(false, "118", "82", "82", "222", "222", "196", "248", "248", "84", "84", "42", "42", "159", "159", "43", "43", "125", "163", "163", "249", "249", "250", "250", "128", "128", "90", "10", "47", "47", "166", "166", "201", "201", "95", "95", "202", "202", "229", "229", "169", "169", "56", "57", "203", "203", "204", "204", "98", "98", "205", "205", "19", "19", "206", "206", "138", "138", "207", "208", "208", "209", "209", "61", "61", "210", "210", "63", "63", "232", "232", "254", "254", "23", "233", "233", "211", "175", "24", "24", "213", "213", "104", "104", "105", "27", "257", "257", "145", "145", "214", "214", "258", "258", "33", "33", "34", "34", "149", "149", "150", "241", "241", "242", "244", "245", "245", "71", "11", "11", "227", "227", "255", "255", "256", "256", "259", "259", "186", "186", "189", "218", "218", "75", "75", "153", "153", "192", "192", "219", "219", "154", "154", "114", "114", "115", "38", "38", "246", "246", "195"); + private final CardRun land = new CardRun(false, "262", "263", "264", "265", "266", "267", "268", "269", "270", "271", "272", "273", "274", "275", "276", "365", "366", "367", "368", "369"); + + private final BoosterStructure AABBBBCCCC = new BoosterStructure( + commonA, commonA, + commonB, commonB, commonB, commonB, + commonC, commonC, commonC, commonC + ); + private final BoosterStructure AAABBBCCCC = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC, commonC, commonC, commonC + ); + private final BoosterStructure AAABBBBCCC = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, commonB, + commonC, commonC, commonC + ); + private final BoosterStructure U3 = new BoosterStructure(uncommon, uncommon, uncommon); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 2.67 A commons (270 / 101) + // 3.66 B commons (370 / 101) + // 3.66 C commons (370 / 101) + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, + AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, + AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, + AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, + AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, + AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, + AABBBBCCCC, AABBBBCCCC, AABBBBCCCC, + AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, + AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, + AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, + AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, + AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, + AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, + AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, AAABBBCCCC, + AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, + AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, + AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, + AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, + AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, + AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, + AAABBBBCCC, AAABBBBCCC, AAABBBBCCC, AAABBBBCCC + ); + + private final RarityConfiguration uncommonRuns = new RarityConfiguration(U3); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } From 34d789cdf2e96737a55758c5c1ae723ab6725688 Mon Sep 17 00:00:00 2001 From: tiera3 <87589219+tiera3@users.noreply.github.com> Date: Sat, 28 Sep 2024 22:59:18 +1000 Subject: [PATCH 23/33] [LCI] booster collation closes #12923 --- .../src/mage/sets/TheLostCavernsOfIxalan.java | 102 +++++++++++++++--- 1 file changed, 89 insertions(+), 13 deletions(-) diff --git a/Mage.Sets/src/mage/sets/TheLostCavernsOfIxalan.java b/Mage.Sets/src/mage/sets/TheLostCavernsOfIxalan.java index 59285b500f5..392cf4d1165 100644 --- a/Mage.Sets/src/mage/sets/TheLostCavernsOfIxalan.java +++ b/Mage.Sets/src/mage/sets/TheLostCavernsOfIxalan.java @@ -6,6 +6,10 @@ import mage.cards.repository.CardInfo; import mage.constants.Rarity; import mage.constants.SetType; import mage.util.RandomUtil; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import java.util.ArrayList; import java.util.List; @@ -509,20 +513,92 @@ public final class TheLostCavernsOfIxalan extends ExpansionSet { } @Override - protected List findSpecialCardsByRarity(Rarity rarity) { - if (rarity == Rarity.RARE || rarity == Rarity.MYTHIC) { - return new ArrayList<>(); // Rare/Mythic DFCs are not special cards here - } else { - return super.findSpecialCardsByRarity(rarity); - // this accounts for 7 caves in the land slot, as well as the common/uncommon DFCs - } + public BoosterCollator createCollator() { + return new TheLostCavernsOfIxalanCollator(); } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/lci.html +// Using Japanese collation +// Using 1:2 ratio for individual dfc uncommon:common - as per shown sheet - differs from text description +// Using 5:3 ratio for individual nonbasic:basic - matches both text desciption and shown sheet +class TheLostCavernsOfIxalanCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "174", "248", "175", "166", "192", "206", "199", "205", "177", "138", "203", "210", "200", "192", "175", "206", "201", "250", "177", "174", "200", "205", "203", "210", "136", "202", "214", "182", "138", "218", "190", "248", "199", "166", "210", "192", "214", "174", "218", "182", "259", "190", "199", "248", "200", "203", "138", "177", "250", "202", "205", "166", "175", "259", "201", "136", "206", "250", "192", "203", "177", "248", "199", "174", "136", "205", "214", "166", "206", "201", "218", "202", "138", "200", "175", "182", "259", "190", "210", "192", "218", "166", "200", "174", "199", "250", "205", "190", "210", "201", "136", "206", "182", "138", "202", "175", "248", "177", "259", "214", "203", "201", "250", "190", "182", "218", "259", "202", "136", "214"); + private final CardRun commonB = new CardRun(true, "46", "140", "49", "131", "77", "149", "209", "75", "167", "84", "169", "66", "172", "71", "73", "163", "253", "68", "144", "57", "82", "131", "246", "45", "132", "64", "207", "151", "85", "154", "49", "140", "46", "168", "72", "70", "142", "53", "159", "69", "160", "85", "167", "207", "64", "151", "46", "132", "73", "163", "71", "75", "154", "253", "70", "160", "49", "82", "131", "246", "84", "142", "77", "209", "168", "68", "172", "69", "140", "53", "149", "72", "66", "169", "45", "159", "57", "144", "82", "168", "209", "66", "149", "84", "142", "85", "172", "69", "77", "160", "246", "68", "154", "57", "73", "144", "253", "45", "132", "75", "207", "169", "70", "163", "71", "159", "72", "151", "53", "64", "167"); + private final CardRun commonC = new CardRun(true, "116", "2", "99", "15", "90", "13", "118", "18", "101", "40", "130", "31", "106", "37", "110", "9", "117", "28", "89", "24", "100", "11", "95", "35", "114", "38", "104", "4", "109", "3", "90", "11", "255", "105", "15", "89", "31", "110", "40", "118", "30", "119", "13", "99", "28", "117", "2", "100", "7", "106", "3", "116", "38", "101", "18", "112", "24", "130", "27", "109", "4", "104", "9", "114", "35", "95", "37", "118", "31", "255", "104", "28", "106", "3", "130", "9", "90", "4", "99", "40", "119", "18", "105", "38", "116", "27", "109", "7", "100", "2", "114", "35", "95", "11", "110", "24", "112", "30", "117", "13", "101", "15", "89", "37", "119", "27", "255", "105", "30", "112", "7"); + private final CardRun dfc = new CardRun(false, "6", "146", "108", "155", "155", "60", "60", "195", "197", "197", "62", "233", "29", "29", "164", "36", "262", "128", "128", "217", "129", "83"); + private final CardRun uncommonA = new CardRun(false, "65", "65", "65", "74", "74", "74", "76", "76", "76", "78", "78", "405", "79", "79", "79", "86", "86", "86"); + private final CardRun uncommonB = new CardRun(true, "254", "33", "48", "23", "58", "286", "96", "251", "25", "111", "51", "5", "125", "8", "124", "186", "22", "102", "261", "87", "272", "107", "59", "120", "278", "19", "103", "42", "93", "21", "247", "33", "48", "111", "10", "50", "96", "23", "54", "286", "58", "17", "254", "213", "124", "8", "252", "59", "102", "25", "247", "51", "5", "125", "251", "107", "42", "93", "19", "103", "186", "22", "87", "278", "21", "261", "120", "272", "254", "17", "54", "252", "50", "10", "213", "48", "33", "58", "286", "96", "23", "51", "251", "124", "8", "125", "5", "102", "25", "59", "120", "247", "107", "186", "22", "272", "87", "261", "103", "278", "19", "93", "21", "111", "42", "252", "17", "54", "213", "10", "50"); + private final CardRun uncommonC = new CardRun(true, "245", "141", "301", "180", "230", "150", "220", "147", "194", "227", "148", "178", "139", "184", "260", "322", "263", "150", "187", "303", "198", "232", "183", "139", "270", "97", "173", "16", "187", "263", "198", "141", "298", "162", "232", "91", "173", "230", "143", "180", "170", "184", "242", "226", "147", "215", "320", "224", "187", "260", "236", "263", "143", "184", "242", "270", "245", "183", "147", "216", "236", "133", "224", "215", "310", "152", "302", "143", "331", "97", "178", "306", "323", "304", "198", "162", "329", "133", "312", "148", "215", "91", "170", "260", "179", "226", "194", "165", "16", "216", "97", "227", "152", "270", "150", "179", "139", "91", "170", "194", "165", "16", "141", "180", "152", "179", "220", "178", "173", "325", "148"); + private final CardRun rare = new CardRun(false, "219", "219", "1", "1", "88", "44", "44", "221", "221", "223", "223", "176", "176", "92", "134", "47", "47", "135", "135", "137", "137", "94", "94", "269", "249", "98", "98", "181", "181", "228", "228", "52", "52", "145", "145", "271", "271", "12", "12", "14", "14", "185", "229", "188", "188", "153", "153", "189", "191", "191", "156", "156", "193", "193", "196", "196", "231", "231", "61", "61", "20", "20", "157", "157", "63", "63", "256", "256", "234", "234", "158", "204", "67", "26", "237", "237", "161", "161", "113", "113", "208", "208", "115", "115", "238", "32", "280", "280", "281", "281", "282", "282", "283", "283", "284", "284", "258", "258", "239", "34", "34", "211", "211", "121", "121", "240", "241", "241", "122", "122", "123", "123", "80", "80", "285", "285", "126", "126", "264", "264", "127", "127", "222", "225", "225", "55", "56", "56", "257", "235", "212", "39", "39", "265", "265", "266", "266", "81", "81", "267", "267", "171", "171", "41", "41", "243", "244", "244", "43", "43"); + private final CardRun land = new CardRun(false, "268", "268", "268", "268", "268", "273", "273", "273", "273", "273", "274", "274", "274", "274", "274", "275", "275", "275", "275", "275", "276", "276", "276", "276", "276", "277", "277", "277", "277", "277", "279", "279", "279", "279", "279", "287", "287", "287", "288", "288", "288", "289", "289", "289", "290", "290", "290", "291", "291", "291", "393", "393", "393", "394", "394", "394", "395", "395", "395", "396", "396", "396", "397", "397", "397", "398", "398", "398", "399", "399", "399", "400", "400", "400", "401", "401", "401", "402", "402", "402"); + + private final BoosterStructure AABBBCCCC = new BoosterStructure( + commonA, commonA, + commonB, commonB, commonB, + commonC, commonC, commonC, commonC + ); + private final BoosterStructure AABBBBCCC = new BoosterStructure( + commonA, commonA, + commonB, commonB, commonB, commonB, + commonC, commonC, commonC + ); + private final BoosterStructure AAABBBCCC = new BoosterStructure( + commonA, commonA, commonA, + commonB, commonB, commonB, + commonC, commonC, commonC + ); + private final BoosterStructure BCC = new BoosterStructure(uncommonB, uncommonC, uncommonC); + private final BoosterStructure BBC = new BoosterStructure(uncommonB, uncommonB, uncommonC); + private final BoosterStructure ABC = new BoosterStructure(uncommonA, uncommonB, uncommonC); + + private final BoosterStructure D1 = new BoosterStructure(dfc); + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 2.06250 A commons (66 / 32) + // 3.46875 B commons (111 / 32) + // 3.46875 C commons (111 / 32) + private final RarityConfiguration commonRuns = new RarityConfiguration( + AABBBCCCC, AABBBCCCC, AABBBCCCC, AABBBCCCC, AABBBCCCC, + AABBBCCCC, AABBBCCCC, AABBBCCCC, AABBBCCCC, AABBBCCCC, + AABBBCCCC, AABBBCCCC, AABBBCCCC, AABBBCCCC, AABBBCCCC, + AABBBBCCC, AABBBBCCC, AABBBBCCC, AABBBBCCC, AABBBBCCC, + AABBBBCCC, AABBBBCCC, AABBBBCCC, AABBBBCCC, AABBBBCCC, + AABBBBCCC, AABBBBCCC, AABBBBCCC, AABBBBCCC, AABBBBCCC, + AAABBBCCC, AAABBBCCC + ); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 0.2250 A uncommons (18 / 80) + // 1.3875 B uncommons (111 / 80) + // 1.3875 C uncommons (111 / 80) + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + BCC, BCC, BCC, BCC, BCC, BCC, BCC, BCC, BCC, BCC, + BCC, BCC, BCC, BCC, BCC, BCC, BCC, BCC, BCC, BCC, + BCC, BCC, BCC, BCC, BCC, BCC, BCC, BCC, BCC, BCC, + BCC, + BBC, BBC, BBC, BBC, BBC, BBC, BBC, BBC, BBC, BBC, + BBC, BBC, BBC, BBC, BBC, BBC, BBC, BBC, BBC, BBC, + BBC, BBC, BBC, BBC, BBC, BBC, BBC, BBC, BBC, BBC, + BBC, + ABC, ABC, ABC, ABC, ABC, ABC, ABC, ABC, ABC, ABC, + ABC, ABC, ABC, ABC, ABC, ABC, ABC, ABC + ); + + private final RarityConfiguration dfcRuns = new RarityConfiguration(D1); + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); @Override - protected void addDoubleFace(List booster) { - int rarityKey = RandomUtil.nextInt((5 * 3) + 12); // 5 C, 12 U - assume commons three times the frequency as uncommons - Rarity rarity = (rarityKey > 12) ? Rarity.COMMON : Rarity.UNCOMMON; - addToBooster(booster, getSpecialCardsByRarity(rarity)); + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(dfcRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; } - -} +} \ No newline at end of file From 3ed75e668e98e27062f6f7a81fcba05ac6033b41 Mon Sep 17 00:00:00 2001 From: Grath <1895280+Grath@users.noreply.github.com> Date: Sun, 29 Sep 2024 12:11:05 -0400 Subject: [PATCH 24/33] Fix Zimone's targeting to actually be chosen. --- Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java b/Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java index dc5c08d4de3..a71427986c6 100644 --- a/Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java +++ b/Mage.Sets/src/mage/cards/z/ZimoneMysteryUnraveler.java @@ -77,6 +77,7 @@ class ZimoneMysteryUnravelerEffect extends OneShotEffect { filter.add(FaceDownPredicate.instance); if (controller != null) { TargetControlledPermanent target = new TargetControlledPermanent(0, 1, filter, true); + controller.choose(Outcome.BoostCreature, target, source, game); Permanent permanent = game.getPermanent(target.getFirstTarget()); return permanent != null && permanent.turnFaceUp(source, game, source.getControllerId()); } From e5a351ff58c70b39ec0249f028346dfd5790a392 Mon Sep 17 00:00:00 2001 From: Grath <1895280+Grath@users.noreply.github.com> Date: Sun, 29 Sep 2024 13:12:31 -0400 Subject: [PATCH 25/33] [DSC] Implement The Lord of Pain. (#12940) Also adds "their" to the text that CardUtil.addArticle supports and fixes a range of influence bug in SpellCastAllTriggeredAbility. --- Mage.Sets/src/mage/cards/t/TheLordOfPain.java | 141 ++++++++++++++++++ .../sets/DuskmournHouseOfHorrorCommander.java | 1 + .../common/SpellCastAllTriggeredAbility.java | 3 +- Mage/src/main/java/mage/util/CardUtil.java | 1 + 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/cards/t/TheLordOfPain.java diff --git a/Mage.Sets/src/mage/cards/t/TheLordOfPain.java b/Mage.Sets/src/mage/cards/t/TheLordOfPain.java new file mode 100644 index 00000000000..470a4556562 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheLordOfPain.java @@ -0,0 +1,141 @@ +package mage.cards.t; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.CantGainLifeAllEffect; +import mage.constants.*; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.FilterPlayer; +import mage.filter.FilterSpell; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.target.targetpointer.FixedTarget; +import mage.watchers.common.SpellsCastWatcher; + +/** + * + * @author Grath + */ +public final class TheLordOfPain extends CardImpl { + public TheLordOfPain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ASSASSIN); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // Your opponents can't gain life. + this.addAbility(new SimpleStaticAbility( + new CantGainLifeAllEffect(Duration.WhileOnBattlefield, TargetController.OPPONENT) + )); + + // Whenever a player casts their first spell each turn, choose another target player. The Lord of Pain deals damage equal to that spell's mana value to the chosen player. + this.addAbility(new TheLordOfPainTriggeredAbility()); + } + + private TheLordOfPain(final TheLordOfPain card) { + super(card); + } + + @Override + public TheLordOfPain copy() { + return new TheLordOfPain(this); + } +} + +enum TheLordOfPainPredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject input, Game game) { + return game.getState() + .getWatcher(SpellsCastWatcher.class) + .getCount(input.getControllerId()) == 1; + } +} + +class TheLordOfPainTriggeredAbility extends SpellCastAllTriggeredAbility { + private static final FilterSpell filter = new FilterSpell("their first spell each turn"); + + static { + filter.add(TheLordOfPainPredicate.instance); + } + + public TheLordOfPainTriggeredAbility() { + super(new TheLordOfPainEffect(), filter, false, SetTargetPointer.PLAYER); + } + + protected TheLordOfPainTriggeredAbility(final TheLordOfPainTriggeredAbility ability) { + super(ability); + } + + @Override + public TheLordOfPainTriggeredAbility copy() { + return new TheLordOfPainTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (super.checkTrigger(event, game)) { + Player controller = game.getPlayer(getControllerId()); + Spell spell = (Spell)getEffects().get(0).getValue("spellCast"); + if (controller != null) { + FilterPlayer filter2 = new FilterPlayer("another target player"); + filter2.add(Predicates.not(new MageObjectReferencePredicate(spell.getControllerId(), game))); + TargetPlayer target = new TargetPlayer(1, 1, false, filter2); + controller.choose(Outcome.Damage, target, this, game); + getEffects().setTargetPointer(new FixedTarget(target.getFirstTarget())); + return true; + } + } + return false; + } +} + +class TheLordOfPainEffect extends OneShotEffect { + + TheLordOfPainEffect() { + super(Outcome.Benefit); + staticText = "choose another target player. {this} deals damage equal to that spell's mana value to the chosen player"; + } + + private TheLordOfPainEffect(final TheLordOfPainEffect effect) { + super(effect); + } + + @Override + public TheLordOfPainEffect copy() { + return new TheLordOfPainEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = (Spell)this.getValue("spellCast"); + if (spell != null) { + int cost = spell.getManaValue(); + Player target = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (target != null) { + target.damage(cost, source.getSourceId(), source, game); + return true; + } + } + return false; } +} diff --git a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java index 0c83af0054a..aefc2b6eddb 100644 --- a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java +++ b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java @@ -257,6 +257,7 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet { cards.add(new SetCardInfo("Temur War Shaman", 200, Rarity.RARE, mage.cards.t.TemurWarShaman.class)); cards.add(new SetCardInfo("Terminus", 70, Rarity.RARE, mage.cards.t.Terminus.class)); cards.add(new SetCardInfo("The Eldest Reborn", 139, Rarity.UNCOMMON, mage.cards.t.TheEldestReborn.class)); + cards.add(new SetCardInfo("The Lord of Pain", 3, Rarity.MYTHIC, mage.cards.t.TheLordOfPain.class)); cards.add(new SetCardInfo("Theater of Horrors", 236, Rarity.RARE, mage.cards.t.TheaterOfHorrors.class)); cards.add(new SetCardInfo("They Came from the Pipes", 14, Rarity.RARE, mage.cards.t.TheyCameFromThePipes.class)); cards.add(new SetCardInfo("Thirst for Meaning", 129, Rarity.COMMON, mage.cards.t.ThirstForMeaning.class)); diff --git a/Mage/src/main/java/mage/abilities/common/SpellCastAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SpellCastAllTriggeredAbility.java index b14269dd126..957b9d9038e 100644 --- a/Mage/src/main/java/mage/abilities/common/SpellCastAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SpellCastAllTriggeredAbility.java @@ -60,7 +60,8 @@ public class SpellCastAllTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (!filter.match(spell, getControllerId(), this, game)) { + if (!filter.match(spell, getControllerId(), this, game) + || !game.getState().getPlayersInRange(getControllerId(), game, false).contains(event.getPlayerId())) { return false; } getEffects().setValue("spellCast", spell); diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 49742ab086d..a8d3413762e 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1032,6 +1032,7 @@ public final class CardUtil { || text.startsWith("any ") || text.startsWith("{this} ") || text.startsWith("your ") + || text.startsWith("their ") || text.startsWith("one ")) { return text; } From f690ddd4de7afc8479e312e41766d4f2587c96d9 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Sun, 29 Sep 2024 14:02:23 -0400 Subject: [PATCH 26/33] fix #12937 (Enduring Courage) --- Mage.Sets/src/mage/cards/e/EnduringCourage.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/e/EnduringCourage.java b/Mage.Sets/src/mage/cards/e/EnduringCourage.java index be65c188e04..a447b90c9bd 100644 --- a/Mage.Sets/src/mage/cards/e/EnduringCourage.java +++ b/Mage.Sets/src/mage/cards/e/EnduringCourage.java @@ -10,7 +10,9 @@ import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; +import mage.constants.Zone; import mage.filter.StaticFilters; import java.util.UUID; @@ -29,9 +31,9 @@ public final class EnduringCourage extends CardImpl { this.toughness = new MageInt(3); // Whenever another creature you control enters, it gets +2/+0 and gains haste until end of turn. - Ability ability = new EntersBattlefieldAllTriggeredAbility( + Ability ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new BoostTargetEffect(2, 0).setText("it gets +2/+0"), - StaticFilters.FILTER_ANOTHER_CREATURE_YOU_CONTROL + StaticFilters.FILTER_ANOTHER_CREATURE_YOU_CONTROL, false, SetTargetPointer.PERMANENT ); ability.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance()) .setText("and gains haste until end of turn")); From 8bcac56550cdc6043353d9bf9db7febe910cc303 Mon Sep 17 00:00:00 2001 From: xenohedron Date: Sun, 29 Sep 2024 18:28:10 -0400 Subject: [PATCH 27/33] tests: improve booster generation summary output --- .../mage/test/sets/BoosterGenerationTest.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java b/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java index a1272a2f066..97c8b2acf2b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java @@ -514,8 +514,9 @@ public class BoosterGenerationTest extends MageTestPlayerBase { @Ignore // debug only: collect info about cards in boosters, see https://github.com/magefree/mage/issues/8081 @Test public void test_CollectBoosterStats() { - ExpansionSet setToAnalyse = ModernHorizons3.getInstance(); - int openBoosters = 10000; + ExpansionSet setToAnalyse = NewPhyrexia.getInstance(); + // Takes about a minute for 100,000 boosters + int openBoosters = 100000; Map resRatio = new HashMap<>(); int totalCards = 0; @@ -523,11 +524,23 @@ public class BoosterGenerationTest extends MageTestPlayerBase { List booster = setToAnalyse.createBooster(); totalCards += booster.size(); booster.forEach(card -> { - String code = String.format("%s %s %s", card.getExpansionSetCode(), card.getRarity().getCode(), card.getName()); + String code = String.format("%s %s %s", card.getExpansionSetCode(), card.getRarity().toString().charAt(0), card.getName()); resRatio.putIfAbsent(code, 0); resRatio.computeIfPresent(code, (u, count) -> count + 1); }); } + System.out.println(setToAnalyse.getName() + " - boosters opened: " + openBoosters + ". Found cards: " + totalCards + "\n"); + for (char rarity : Arrays.asList('C', 'U', 'R', 'M', 'S', 'L', 'B')) { + List rarityCounts = resRatio.entrySet().stream() + .filter(e -> e.getKey().charAt(4) == rarity) + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + if (!rarityCounts.isEmpty()) { + System.out.println(rarity + String.format(": %s unique, min %s, max %s, total %s", + rarityCounts.size(), Collections.min(rarityCounts), Collections.max(rarityCounts), + rarityCounts.stream().mapToInt(x -> x).sum())); + } + } List info = resRatio.entrySet().stream() .sorted((o1, o2) -> Integer.compare(o2.getValue(), o1.getValue())) .map(e -> String.format("%s: %d", @@ -535,8 +548,7 @@ public class BoosterGenerationTest extends MageTestPlayerBase { e.getValue() )) .collect(Collectors.toList()); - System.out.println(setToAnalyse.getName() + " - boosters opened: " + openBoosters + ". Found cards: " + totalCards + "\n" - + String.join("\n", info)); + System.out.println("\n" + String.join("\n", info)); } @Ignore // debug only From 3c6cb2e89661b214a964b064a32b60164bfe9a55 Mon Sep 17 00:00:00 2001 From: tiera3 <87589219+tiera3@users.noreply.github.com> Date: Sun, 29 Sep 2024 17:55:27 +1000 Subject: [PATCH 28/33] [NPH] New Phyrexia booster collation (closes #12933) --- Mage.Sets/src/mage/sets/NewPhyrexia.java | 65 ++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/Mage.Sets/src/mage/sets/NewPhyrexia.java b/Mage.Sets/src/mage/sets/NewPhyrexia.java index c5dc6ca2c8c..e056f13aeb0 100644 --- a/Mage.Sets/src/mage/sets/NewPhyrexia.java +++ b/Mage.Sets/src/mage/sets/NewPhyrexia.java @@ -3,6 +3,13 @@ package mage.sets; import mage.cards.ExpansionSet; import mage.constants.Rarity; import mage.constants.SetType; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; + +import java.util.ArrayList; +import java.util.List; public final class NewPhyrexia extends ExpansionSet { @@ -198,4 +205,62 @@ public final class NewPhyrexia extends ExpansionSet { cards.add(new SetCardInfo("Wing Splicer", 50, Rarity.UNCOMMON, mage.cards.w.WingSplicer.class)); cards.add(new SetCardInfo("Xenograft", 51, Rarity.RARE, mage.cards.x.Xenograft.class)); } + + @Override + public BoosterCollator createCollator() { + return new NewPhyrexiaCollator(); + } } + +// Booster collation info from https://www.lethe.xyz/mtg/collation/nph.html +class NewPhyrexiaCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "34", "99", "111", "131", "66", "30", "22", "89", "139", "113", "55", "2", "48", "131", "114", "93", "25", "76", "35", "138", "111", "83", "11", "55", "120", "99", "43", "150", "125", "22", "83", "75", "21", "34", "113", "140", "14", "60", "94", "11", "124", "48", "139", "120", "25", "75", "94", "30", "125", "140", "2", "145", "66", "89", "43", "114", "14", "138", "60", "35", "93", "124", "76", "21", "145", "150"); + private final CardRun commonB = new CardRun(true, "122", "45", "69", "84", "163", "26", "96", "110", "67", "40", "100", "24", "149", "36", "69", "116", "79", "61", "26", "151", "46", "92", "13", "108", "45", "63", "100", "67", "163", "24", "29", "79", "46", "96", "63", "19", "40", "52", "110", "149", "84", "136", "29", "13", "122", "61", "151", "108", "36", "92", "52", "116", "136", "19"); + private final CardRun uncommonA = new CardRun(true, "5", "56", "134", "126", "102", "28", "53", "8", "134", "107", "154", "56", "118", "156", "102", "12", "133", "107", "33", "165", "53", "154", "126", "91", "142", "8", "33", "112", "77", "90", "129", "15", "39", "152", "64", "90", "142", "12", "112", "32", "77", "129", "101", "117", "156", "72", "5", "101", "39", "117", "165", "3", "64", "133", "147", "91", "3", "32", "72", "147", "86", "15", "118", "28", "152", "86"); + private final CardRun uncommonB = new CardRun(true, "160", "105", "144", "123", "59", "27", "47", "103", "144", "97", "141", "157", "121", "38", "82", "57", "23", "157", "16", "49", "82", "58", "10", "85", "155", "103", "38", "153", "78", "59", "155", "27", "7", "160", "50", "58", "141", "78", "123", "50", "85", "57", "97", "23", "47", "70", "7", "105", "10", "49", "70", "153", "121", "16"); + private final CardRun rare = new CardRun(false, "130", "104", "104", "4", "4", "80", "80", "132", "132", "6", "6", "54", "54", "81", "81", "31", "31", "106", "106", "9", "135", "109", "109", "62", "62", "137", "137", "87", "87", "37", "128", "128", "1", "143", "143", "65", "65", "115", "115", "88", "88", "146", "146", "17", "17", "148", "148", "41", "41", "42", "42", "68", "119", "119", "18", "18", "71", "71", "44", "44", "20", "20", "73", "95", "95", "158", "158", "159", "159", "74", "74", "161", "162", "162", "164", "164", "98", "127", "51", "51"); + private final CardRun land = new CardRun(false, "166", "167", "168", "169", "170", "171", "172", "173", "174", "175"); + + private final BoosterStructure AAAAAABBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB + ); + private final BoosterStructure AAAAABBBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB, commonB + ); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + + private final BoosterStructure R1 = new BoosterStructure(rare); + private final BoosterStructure L1 = new BoosterStructure(land); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 5.5 A uncommons (11 / 2) + // 4.5 B uncommons (9 / 2) + private final RarityConfiguration commonRuns = new RarityConfiguration( + AAAAAABBBB, AAAAABBBBB + ); + + // In order for equal numbers of each common to exist, the average booster must contain: + // 1.65 A uncommons (33 / 20) + // 1.35 B uncommons (27 / 20) + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, AAB, + AAB, AAB, AAB, + ABB, ABB, ABB, ABB, ABB, ABB, ABB + ); + + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } +} \ No newline at end of file From d60680b3f4dcd6209cc3c7b79e038fc17774b9b6 Mon Sep 17 00:00:00 2001 From: tiera3 <87589219+tiera3@users.noreply.github.com> Date: Sun, 29 Sep 2024 17:59:16 +1000 Subject: [PATCH 29/33] [LRW] Lorwyn booster collation (closes #12934) --- Mage.Sets/src/mage/sets/Lorwyn.java | 65 +++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/Mage.Sets/src/mage/sets/Lorwyn.java b/Mage.Sets/src/mage/sets/Lorwyn.java index a40bb52b2b0..072258c03e7 100644 --- a/Mage.Sets/src/mage/sets/Lorwyn.java +++ b/Mage.Sets/src/mage/sets/Lorwyn.java @@ -3,6 +3,13 @@ package mage.sets; import mage.cards.ExpansionSet; import mage.constants.Rarity; import mage.constants.SetType; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; + +import java.util.ArrayList; +import java.util.List; /** * @author North @@ -328,4 +335,62 @@ public final class Lorwyn extends ExpansionSet { cards.add(new SetCardInfo("Wydwen, the Biting Gale", 253, Rarity.RARE, mage.cards.w.WydwenTheBitingGale.class)); cards.add(new SetCardInfo("Zephyr Net", 98, Rarity.COMMON, mage.cards.z.ZephyrNet.class)); } + + @Override + public BoosterCollator createCollator() { + return new LorwynCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/lrw.html +// order not known for uncommon runs, but card division is +// pack distribution for uncommons not specified, assuming AAB ABB as in Onslaught +class LorwynCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "91", "191", "128", "197", "177", "35", "77", "108", "260", "231", "32", "84", "189", "214", "126", "23", "181", "134", "77", "231", "258", "156", "33", "189", "90", "128", "211", "126", "35", "96", "227", "148", "48", "91", "161", "103", "229", "260", "181", "32", "78", "227", "156", "143", "23", "96", "103", "148", "211", "258", "20", "84", "191", "108", "197", "33", "78", "177", "134", "229", "161", "48", "90", "143", "214", "20"); + private final CardRun commonB = new CardRun(true, "118", "57", "17", "212", "110", "190", "68", "201", "34", "265", "118", "151", "242", "25", "115", "76", "184", "228", "147", "27", "57", "183", "223", "257", "4", "146", "184", "212", "50", "137", "43", "201", "194", "265", "87", "4", "198", "110", "185", "261", "232", "17", "68", "146", "190", "223", "43", "76", "147", "185", "242", "261", "27", "115", "151", "228", "87", "34", "137", "198", "50", "194", "257", "25", "232", "183"); + private final CardRun commonC = new CardRun(true, "16", "62", "132", "152", "206", "12", "54", "129", "163", "234", "16", "97", "166", "111", "241", "152", "8", "81", "99", "273", "205", "170", "29", "89", "141", "166", "93", "234", "45", "132", "163", "62", "241", "29", "141", "154", "81", "218", "45", "129", "157", "93", "206", "54", "8", "154", "111", "218", "170", "12", "89", "99", "205", "97", "157"); + private final CardRun commonD = new CardRun(true, "22", "225", "153", "109", "79", "217", "140", "39", "164", "204", "51", "24", "138", "225", "41", "79", "100", "80", "164", "140", "52", "24", "236", "153", "47", "215", "101", "98", "100", "22", "70", "186", "40", "204", "47", "52", "127", "98", "138", "215", "80", "180", "51", "236", "109", "39", "127", "186", "41", "217", "70", "101", "180", "40", "273"); + private final CardRun uncommonA = new CardRun(false, "5", "102", "155", "7", "55", "11", "162", "59", "207", "208", "13", "112", "64", "165", "167", "169", "67", "18", "174", "117", "119", "26", "224", "226", "122", "72", "73", "130", "136", "188", "82", "42", "237", "239", "94", "46", "275", "277", "279", "243"); + private final CardRun uncommonB = new CardRun(false, "53", "199", "158", "160", "9", "200", "10", "58", "60", "61", "63", "113", "168", "114", "116", "171", "172", "216", "19", "220", "221", "182", "222", "28", "124", "125", "74", "36", "235", "38", "139", "86", "142", "193", "144", "195", "276", "278", "49", "245"); + private final CardRun rare = new CardRun(false, "1", "266", "2", "150", "149", "267", "3", "104", "6", "246", "105", "159", "202", "106", "254", "56", "203", "255", "256", "247", "107", "209", "210", "65", "14", "66", "248", "15", "213", "268", "69", "173", "175", "21", "249", "176", "269", "219", "178", "179", "71", "120", "121", "123", "230", "30", "31", "75", "270", "250", "131", "187", "133", "233", "135", "37", "259", "83", "271", "85", "272", "88", "274", "192", "92", "251", "262", "145", "44", "263", "238", "264", "240", "280", "95", "196", "281", "252", "244", "253"); + + private final BoosterStructure AAAAAACCCCC = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, commonA, + commonC, commonC, commonC, commonC, commonC + ); + private final BoosterStructure AAAAAADDDDD = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, commonA, + commonD, commonD, commonD, commonD, commonD + ); + private final BoosterStructure BBBBBBCCCCC = new BoosterStructure( + commonB, commonB, commonB, commonB, commonB, commonB, + commonC, commonC, commonC, commonC, commonC + ); + private final BoosterStructure BBBBBBDDDDD = new BoosterStructure( + commonB, commonB, commonB, commonB, commonB, commonB, + commonD, commonD, commonD, commonD, commonD + ); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + + private final BoosterStructure R1 = new BoosterStructure(rare); + + private final RarityConfiguration commonRuns = new RarityConfiguration( + AAAAAACCCCC, AAAAAADDDDD, BBBBBBCCCCC, BBBBBBDDDDD + ); + + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + AAB, ABB + ); + + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + return booster; + } } From 7bd680669d485f469988c77866d788226f083e15 Mon Sep 17 00:00:00 2001 From: tiera3 <87589219+tiera3@users.noreply.github.com> Date: Sun, 29 Sep 2024 18:44:11 +1000 Subject: [PATCH 30/33] [SHM] Shadowmoor booster collation (closes #12935) --- Mage.Sets/src/mage/sets/Shadowmoor.java | 65 +++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/Mage.Sets/src/mage/sets/Shadowmoor.java b/Mage.Sets/src/mage/sets/Shadowmoor.java index 4b4f24c5d4c..9ce4d513486 100644 --- a/Mage.Sets/src/mage/sets/Shadowmoor.java +++ b/Mage.Sets/src/mage/sets/Shadowmoor.java @@ -3,6 +3,13 @@ package mage.sets; import mage.cards.ExpansionSet; import mage.constants.Rarity; import mage.constants.SetType; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; + +import java.util.ArrayList; +import java.util.List; /** * @author North @@ -328,4 +335,62 @@ public final class Shadowmoor extends ExpansionSet { cards.add(new SetCardInfo("Wound Reflection", 81, Rarity.RARE, mage.cards.w.WoundReflection.class)); cards.add(new SetCardInfo("Zealous Guardian", 157, Rarity.COMMON, mage.cards.z.ZealousGuardian.class)); } + + @Override + public BoosterCollator createCollator() { + return new ShadowmoorCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/shm.html +// order not known for uncommon runs, but card division is +// pack distribution for uncommons not specified, assuming AAB ABB as in Onslaught +class ShadowmoorCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "238", "263", "230", "39", "133", "67", "102", "212", "249", "11", "30", "196", "82", "125", "242", "41", "182", "100", "113", "11", "178", "196", "22", "102", "238", "270", "232", "39", "173", "86", "133", "60", "148", "178", "194", "85", "125", "30", "22", "32", "182", "82", "212", "270", "242", "44", "67", "85", "235", "215", "230", "41", "60", "86", "113", "263", "232", "44", "194", "100", "235", "249", "148", "32", "173", "215"); + private final CardRun commonB = new CardRun(true, "20", "121", "218", "77", "177", "247", "149", "109", "136", "164", "195", "150", "239", "124", "213", "80", "177", "268", "138", "216", "66", "200", "139", "149", "259", "110", "152", "187", "166", "165", "20", "216", "101", "64", "45", "268", "3", "224", "195", "66", "139", "166", "152", "121", "101", "164", "150", "259", "138", "110", "218", "64", "124", "136", "239", "224", "213", "77", "45", "247", "3", "109", "200", "165", "80", "187"); + private final CardRun commonC = new CardRun(true, "265", "13", "52", "95", "115", "79", "27", "46", "88", "132", "59", "13", "246", "52", "105", "55", "10", "161", "193", "116", "56", "1", "54", "130", "265", "105", "240", "55", "27", "170", "207", "246", "185", "116", "10", "46", "95", "240", "79", "4", "170", "193", "115", "258", "59", "1", "161", "88", "130", "185", "4", "54", "207", "132", "56"); + private final CardRun commonD = new CardRun(true, "107", "78", "262", "24", "36", "175", "96", "51", "8", "118", "191", "217", "226", "6", "157", "71", "91", "15", "210", "36", "258", "84", "211", "155", "175", "250", "31", "91", "78", "118", "51", "6", "107", "120", "71", "262", "217", "76", "8", "48", "191", "211", "31", "24", "120", "96", "250", "15", "157", "76", "84", "210", "48", "155", "226"); + private final CardRun uncommonA = new CardRun(false, "28", "57", "83", "58", "61", "63", "34", "225", "35", "160", "112", "38", "206", "208", "253", "69", "94", "168", "9", "273", "255", "256", "274", "231", "171", "14", "275", "276", "17", "103", "127", "236", "19", "261", "128", "279", "106", "197", "267", "108"); + private final CardRun uncommonB = new CardRun(false, "2", "180", "29", "203", "62", "37", "205", "92", "114", "141", "117", "251", "189", "119", "254", "70", "167", "97", "229", "190", "43", "144", "192", "145", "18", "146", "147", "174", "241", "23", "264", "219", "220", "154", "199", "131", "266", "179", "269", "244"); + private final CardRun rare = new CardRun(false, "181", "137", "202", "5", "248", "158", "33", "87", "89", "183", "204", "184", "159", "111", "65", "90", "140", "186", "271", "227", "188", "93", "162", "163", "142", "272", "7", "252", "228", "68", "209", "40", "42", "98", "99", "122", "12", "169", "72", "143", "123", "277", "172", "233", "16", "234", "257", "73", "74", "126", "47", "75", "104", "260", "278", "237", "49", "214", "21", "50", "129", "198", "280", "151", "176", "153", "53", "25", "201", "221", "222", "243", "245", "26", "134", "281", "135", "156", "223", "81"); + + private final BoosterStructure AAAAAACCCCC = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, commonA, + commonC, commonC, commonC, commonC, commonC + ); + private final BoosterStructure AAAAAADDDDD = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, commonA, + commonD, commonD, commonD, commonD, commonD + ); + private final BoosterStructure BBBBBBCCCCC = new BoosterStructure( + commonB, commonB, commonB, commonB, commonB, commonB, + commonC, commonC, commonC, commonC, commonC + ); + private final BoosterStructure BBBBBBDDDDD = new BoosterStructure( + commonB, commonB, commonB, commonB, commonB, commonB, + commonD, commonD, commonD, commonD, commonD + ); + private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB); + private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB); + + private final BoosterStructure R1 = new BoosterStructure(rare); + + private final RarityConfiguration commonRuns = new RarityConfiguration( + AAAAAACCCCC, AAAAAADDDDD, BBBBBBCCCCC, BBBBBBDDDDD + ); + + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + AAB, ABB + ); + + private final RarityConfiguration rareRuns = new RarityConfiguration(R1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + return booster; + } } From 112f6705b167500e77783a2d8caa0e29ed165772 Mon Sep 17 00:00:00 2001 From: tiera3 <87589219+tiera3@users.noreply.github.com> Date: Sun, 29 Sep 2024 22:19:38 +1000 Subject: [PATCH 31/33] [DMR] Dominaria Remastered booster collation (closes #12936) --- .../src/mage/sets/DominariaRemastered.java | 82 ++++++++++++++++++- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/DominariaRemastered.java b/Mage.Sets/src/mage/sets/DominariaRemastered.java index cf6c84c2f6e..9fa8df9d84f 100644 --- a/Mage.Sets/src/mage/sets/DominariaRemastered.java +++ b/Mage.Sets/src/mage/sets/DominariaRemastered.java @@ -3,6 +3,13 @@ package mage.sets; import mage.cards.ExpansionSet; import mage.constants.Rarity; import mage.constants.SetType; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; + +import java.util.ArrayList; +import java.util.List; /** * @author TheElk801 @@ -484,8 +491,75 @@ public class DominariaRemastered extends ExpansionSet { cards.add(new SetCardInfo("Zur the Enchanter", 374, Rarity.RARE, mage.cards.z.ZurTheEnchanter.class, NON_FULL_USE_VARIOUS)); } -// @Override -// public BoosterCollator createCollator() { -// return new DominariaRemasteredCollator(); -// } + @Override + public BoosterCollator createCollator() { + return new DominariaRemasteredCollator(); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/dmr.html +// 1/4 of packs contain an old frame rare/mythic +// 6% of packs contain a borderles C/U and 17% of packs contain a borderless card +// using borderless for 1/3 of the printings of R/M with borderless variations works with those numbers +class DominariaRemasteredCollator implements BoosterCollator { + private final CardRun commonA = new CardRun(true, "56", "29", "226", "89", "150", "134", "45", "19", "98", "167", "114", "43", "23", "104", "173", "128", "52", "1", "97", "168", "143", "57", "17", "87", "160", "141", "55", "15", "83", "184", "125", "66", "30", "102", "156", "146", "63", "35", "94", "176", "130", "58", "21", "76", "174", "124", "38", "3", "106", "182", "117", "65", "29", "109", "173", "118", "53", "24", "107", "158", "112", "56", "9", "89", "150", "134", "43", "15", "98", "167", "141", "45", "1", "104", "180", "128", "52", "23", "87", "168", "143", "57", "17", "97", "160", "114", "63", "19", "76", "184", "125", "66", "21", "102", "156", "146", "55", "30", "94", "176", "124", "58", "35", "83", "174", "130", "38", "3", "106", "182", "112", "65", "9", "109", "158", "118", "53", "24", "107", "180", "117"); + private final CardRun commonB = new CardRun(true, "226", "152", "140", "41", "22", "228", "257", "88", "178", "131", "61", "18", "256", "232", "81", "172", "137", "20", "254", "85", "74", "163", "72", "16", "245", "90", "152", "145", "33", "46", "222", "259", "183", "99", "123", "233", "61", "140", "253", "18", "169", "103", "131", "41", "22", "257", "228", "88", "178", "51", "11", "113", "256", "232", "81", "172", "137", "74", "254", "85", "163", "20", "145", "72", "16", "245", "90", "152", "46", "33", "123", "233", "259", "99", "183", "113", "51", "11", "222", "253", "103", "169", "131", "41", "22", "228", "257", "88", "178", "140", "61", "18", "254", "232", "81", "172", "137", "46", "20", "256", "85", "163", "16", "72", "245", "90", "169", "145", "74", "33", "233", "253", "99", "183", "123", "51", "11", "222", "113", "259", "103"); + private final CardRun uncommonA = new CardRun(true, "251", "164", "44", "207", "129", "170", "80", "122", "221", "14", "157", "8", "93", "234", "213", "119", "2", "54", "260", "220", "120", "154", "49", "215", "10", "231", "153", "42", "252", "219", "84", "216", "70", "225", "214", "79", "255", "64", "210", "142", "251", "44", "207", "164", "129", "170", "80", "221", "122", "157", "14", "93", "8", "213", "119", "234", "54", "2", "260", "49", "120", "154", "220", "215", "10", "42", "153", "231", "252", "219", "84", "216", "70", "225", "214", "79", "255", "64", "210", "142", "251", "44", "164", "207", "129", "170", "80", "221", "122", "14", "157", "8", "93", "213", "234", "119", "54", "2", "260", "220", "215", "154", "49", "120", "10", "231", "153", "42", "219", "252", "84", "216", "70", "225", "214", "79", "255", "64", "210", "142"); + private final CardRun uncommonB = new CardRun(true, "246", "48", "155", "209", "40", "86", "28", "208", "115", "212", "34", "196", "190", "240", "199", "105", "197", "25", "162", "242", "116", "189", "243", "138", "181", "111", "202", "78", "68", "31", "147", "235", "204", "100", "203", "177", "4", "211", "192", "246", "195", "48", "209", "40", "155", "212", "28", "208", "115", "196", "86", "34", "190", "240", "199", "105", "162", "25", "197", "243", "189", "116", "242", "138", "181", "111", "202", "68", "78", "203", "147", "235", "204", "100", "31", "177", "211", "4", "192", "195", "246", "48", "155", "209", "40", "86", "28", "208", "115", "212", "34", "196", "190", "240", "199", "105", "162", "25", "197", "243", "189", "116", "242", "138", "181", "111", "202", "68", "78", "203", "147", "235", "204", "100", "31", "177", "4", "211", "192", "195"); + private final CardRun uncommonC = new CardRun(false, "425", "432", "419", "446", "429"); + private final CardRun rare = new CardRun(false, "186", "186", "186", "186", "443", "443", "149", "149", "149", "149", "438", "438", "187", "187", "187", "187", "187", "187", "39", "39", "39", "39", "39", "39", "151", "151", "151", "151", "439", "439", "75", "75", "75", "75", "75", "75", "77", "77", "77", "77", "424", "424", "241", "241", "241", "241", "241", "241", "217", "217", "217", "217", "217", "217", "218", "218", "218", "218", "218", "218", "244", "244", "454", "188", "188", "188", "188", "444", "444", "47", "47", "47", "47", "417", "417", "5", "5", "5", "5", "5", "5", "6", "6", "6", "6", "412", "412", "82", "82", "82", "82", "426", "426", "159", "159", "159", "159", "159", "159", "50", "50", "418", "161", "161", "161", "161", "161", "161", "121", "121", "121", "121", "433", "433", "223", "223", "447", "247", "247", "247", "247", "455", "455", "7", "7", "7", "7", "7", "7", "126", "126", "126", "126", "434", "434", "224", "224", "224", "224", "448", "448", "248", "248", "248", "248", "248", "248", "191", "191", "445", "249", "249", "249", "249", "249", "249", "227", "227", "227", "227", "449", "449", "165", "165", "165", "165", "165", "165", "166", "166", "166", "127", "127", "435", "229", "229", "450", "12", "12", "12", "12", "12", "12", "230", "230", "230", "230", "451", "451", "13", "13", "413", "250", "250", "250", "250", "456", "456", "91", "91", "91", "91", "91", "91", "60", "60", "60", "60", "421", "421", "59", "59", "59", "59", "420", "420", "92", "92", "92", "92", "92", "92", "95", "95", "427", "171", "171", "440", "62", "62", "62", "62", "62", "62", "132", "132", "132", "132", "132", "132", "96", "96", "96", "96", "428", "428", "133", "133", "133", "133", "133", "133", "193", "193", "193", "193", "193", "193", "194", "194", "194", "194", "194", "194", "198", "198", "198", "198", "198", "198", "101", "101", "101", "101", "101", "101", "175", "175", "175", "175", "175", "175", "26", "26", "26", "27", "27", "27", "27", "27", "27", "135", "135", "135", "135", "135", "135", "136", "136", "136", "136", "436", "436", "139", "139", "139", "200", "200", "200", "200", "200", "200", "201", "201", "201", "201", "201", "201", "67", "67", "67", "67", "67", "67", "258", "258", "258", "258", "258", "258", "144", "144", "144", "144", "144", "144", "179", "179", "441", "32", "32", "414", "69", "69", "422", "236", "236", "236", "236", "452", "452", "237", "237", "237", "237", "237", "237", "71", "71", "423", "238", "238", "238", "238", "238", "238", "239", "239", "453", "108", "108", "430", "73", "73", "73", "73", "73", "73", "36", "36", "36", "36", "415", "415", "261", "261", "261", "261", "261", "261", "148", "148", "437", "185", "185", "185", "185", "442", "442", "37", "37", "37", "37", "416", "416", "205", "205", "205", "205", "205", "205", "110", "110", "431", "206", "206", "206", "206", "206", "206"); + private final CardRun rareOld = new CardRun(false, "354", "354", "335", "335", "355", "355", "280", "280", "336", "336", "298", "298", "299", "299", "393", "393", "375", "375", "376", "376", "394", "356", "356", "282", "282", "262", "262", "263", "263", "304", "304", "339", "339", "284", "341", "341", "321", "321", "378", "395", "395", "264", "264", "324", "324", "379", "379", "396", "396", "359", "397", "397", "381", "381", "343", "343", "344", "325", "383", "265", "265", "384", "384", "266", "398", "398", "305", "305", "289", "289", "288", "288", "306", "306", "308", "347", "290", "290", "327", "327", "309", "309", "328", "328", "361", "361", "362", "362", "366", "366", "310", "310", "348", "348", "272", "273", "273", "329", "329", "330", "330", "331", "368", "368", "369", "369", "293", "293", "400", "400", "332", "332", "350", "276", "294", "389", "389", "390", "390", "296", "391", "391", "392", "314", "297", "297", "278", "278", "401", "401", "334", "353", "353", "279", "279", "373", "373", "315", "374", "374"); + private final CardRun retro = new CardRun(false, "300", "316", "281", "377", "301", "337", "317", "357", "302", "303", "338", "318", "340", "283", "319", "320", "410", "285", "322", "358", "323", "286", "380", "287", "342", "404", "382", "345", "267", "385", "399", "326", "268", "408", "360", "346", "307", "386", "291", "292", "402", "363", "364", "365", "269", "270", "367", "271", "274", "370", "349", "311", "406", "275", "371", "312", "387", "372", "388", "295", "313", "333", "277", "351", "352"); + private final CardRun common = new CardRun(false, "38", "1", "112", "41", "150", "152", "76", "113", "3", "43", "114", "45", "46", "222", "245", "81", "156", "117", "158", "118", "83", "160", "85", "51", "163", "52", "123", "124", "87", "125", "53", "55", "88", "89", "9", "90", "56", "226", "228", "167", "11", "168", "57", "128", "169", "130", "58", "232", "131", "15", "16", "94", "17", "61", "18", "233", "63", "19", "172", "65", "20", "97", "98", "99", "253", "173", "21", "22", "254", "23", "134", "174", "24", "176", "137", "256", "257", "66", "140", "141", "29", "178", "102", "143", "30", "145", "180", "259", "103", "104", "146", "106", "107", "72", "33", "182", "35", "183", "184", "74", "109"); + private final CardRun land = new CardRun(false, "402", "403", "404", "405", "406", "407", "408", "409", "410", "411"); + + private final BoosterStructure AAAAAABBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB + ); + private final BoosterStructure AAAAABBBB = new BoosterStructure( + commonA, commonA, commonA, commonA, commonA, + commonB, commonB, commonB, commonB + ); + private final BoosterStructure AAA = new BoosterStructure(uncommonA, uncommonA, uncommonA); + private final BoosterStructure BBB = new BoosterStructure(uncommonB, uncommonB, uncommonB); + private final BoosterStructure AAC = new BoosterStructure(uncommonA, uncommonA, uncommonC); + private final BoosterStructure BBC = new BoosterStructure(uncommonB, uncommonB, uncommonC); + + private final BoosterStructure OR = new BoosterStructure(retro,rare); + private final BoosterStructure CO = new BoosterStructure(common,rareOld); + private final BoosterStructure L1 = new BoosterStructure(land); + + private final RarityConfiguration commonRuns = new RarityConfiguration( + AAAAAABBB, AAAAAABBB, + AAAAABBBB, AAAAABBBB, AAAAABBBB + ); + + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, + AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, + AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, + AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, + AAA, AAA, AAA, AAA, AAA, AAA, AAA, + BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, + BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, + BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, + BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, + BBB, BBB, BBB, BBB, BBB, BBB, BBB, + AAC, AAC, AAC, + BBC, BBC, BBC + ); + + private final RarityConfiguration rareRuns = new RarityConfiguration(OR,OR,OR,CO); + private final RarityConfiguration landRuns = new RarityConfiguration(L1); + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(landRuns.getNext().makeRun()); + return booster; + } } From 56f0dd8460398b26da67dacf29c2a5eae11c912f Mon Sep 17 00:00:00 2001 From: xenohedron Date: Sun, 29 Sep 2024 19:04:10 -0400 Subject: [PATCH 32/33] remove outdated comment --- Mage.Sets/src/mage/sets/DominariaRemastered.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/DominariaRemastered.java b/Mage.Sets/src/mage/sets/DominariaRemastered.java index 9fa8df9d84f..847cc35dad3 100644 --- a/Mage.Sets/src/mage/sets/DominariaRemastered.java +++ b/Mage.Sets/src/mage/sets/DominariaRemastered.java @@ -27,7 +27,7 @@ public class DominariaRemastered extends ExpansionSet { this.hasBoosters = true; this.hasBasicLands = true; this.maxCardNumberInBooster = 261; - this.numBoosterCommon = 10; // Frame/art variants not yet implemented for booster generation + this.numBoosterCommon = 10; this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 7; // 60 rare, 20 mythic From 4a432b61f96bea275861ae4720208413c330a4cb Mon Sep 17 00:00:00 2001 From: Grath <1895280+Grath@users.noreply.github.com> Date: Mon, 30 Sep 2024 01:31:32 -0400 Subject: [PATCH 33/33] [DSK] Implement Niko, Light of Hope (#12942) Add UntilTheNextEndstep duration for 'until the beginning of the next end step' on the copy effect. --- .../src/mage/cards/n/NikoLightOfHope.java | 115 ++++++++++++++++++ .../src/mage/sets/DuskmournHouseOfHorror.java | 1 + .../abilities/effects/ContinuousEffect.java | 2 + .../effects/ContinuousEffectImpl.java | 22 +++- .../effects/ContinuousEffectsList.java | 1 + .../main/java/mage/constants/Duration.java | 1 + 6 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/n/NikoLightOfHope.java diff --git a/Mage.Sets/src/mage/cards/n/NikoLightOfHope.java b/Mage.Sets/src/mage/cards/n/NikoLightOfHope.java new file mode 100644 index 00000000000..6cecc1acc09 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NikoLightOfHope.java @@ -0,0 +1,115 @@ +package mage.cards.n; + +import java.util.UUID; + +import mage.abilities.Ability; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; +import mage.cards.Card; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.ShardToken; +import mage.players.Player; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; +import mage.util.functions.EmptyCopyApplier; + +/** + * + * @author Grath + */ +public final class NikoLightOfHope extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("nonlegendary creature you control"); + + static { + filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate())); + } + + public NikoLightOfHope(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // When Niko, Light of Hope enters, create two Shard tokens. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ShardToken(), 2))); + + // {2}, {T}: Exile target nonlegendary creature you control. Shards you control become copies of it until the beginning of the next end step. Return it to the battlefield under its owner's control at the beginning of the next end step. + Ability ability = new SimpleActivatedAbility(new NikoLightOfHopeEffect(), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetControlledCreaturePermanent(1, 1, filter, false)); + this.addAbility(ability); + } + + private NikoLightOfHope(final NikoLightOfHope card) { + super(card); + } + + @Override + public NikoLightOfHope copy() { + return new NikoLightOfHope(this); + } +} + +class NikoLightOfHopeEffect extends OneShotEffect { + + NikoLightOfHopeEffect() { + super(Outcome.Benefit); + staticText = "Exile target nonlegendary creature you control. Shards you control become copies of it until the beginning of the next end step. Return it to the battlefield under its owner's control at the beginning of the next end step."; + } + + private NikoLightOfHopeEffect(final NikoLightOfHopeEffect effect) { + super(effect); + } + + @Override + public NikoLightOfHopeEffect copy() { + return new NikoLightOfHopeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null && controller != null) { + if (permanent.moveToExile(source.getSourceId(), "Niko, Light of Hope", source, game)) { + FilterPermanent filter = new FilterPermanent("shards"); + filter.add(SubType.SHARD.getPredicate()); + for (Permanent copyTo : game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game)) { + game.copyPermanent(Duration.UntilTheNextEndStep, permanent, copyTo.getId(), source, new EmptyCopyApplier()); + } + ExileZone exile = game.getExile().getExileZone(source.getSourceId()); + if (exile != null && !exile.isEmpty()) { + Card card = game.getCard(permanent.getId()); + if (card != null) { + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false); + effect.setTargetPointer(new FixedTarget(card.getId())); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); + } + } + return true; + } + } + return false; + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java index 1b304b8f2f6..941be07fedd 100644 --- a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java +++ b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java @@ -149,6 +149,7 @@ public final class DuskmournHouseOfHorror extends ExpansionSet { cards.add(new SetCardInfo("Murky Sewer", 263, Rarity.COMMON, mage.cards.m.MurkySewer.class)); cards.add(new SetCardInfo("Nashi, Searcher in the Dark", 223, Rarity.RARE, mage.cards.n.NashiSearcherInTheDark.class)); cards.add(new SetCardInfo("Neglected Manor", 264, Rarity.COMMON, mage.cards.n.NeglectedManor.class)); + cards.add(new SetCardInfo("Niko, Light of Hope", 224, Rarity.MYTHIC, mage.cards.n.NikoLightOfHope.class)); cards.add(new SetCardInfo("Norin, Swift Survivalist", 145, Rarity.UNCOMMON, mage.cards.n.NorinSwiftSurvivalist.class)); cards.add(new SetCardInfo("Oblivious Bookworm", 225, Rarity.UNCOMMON, mage.cards.o.ObliviousBookworm.class)); cards.add(new SetCardInfo("Optimistic Scavenger", 21, Rarity.UNCOMMON, mage.cards.o.OptimisticScavenger.class)); diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java index c1456292c37..3b0cddcbf60 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java @@ -74,6 +74,8 @@ public interface ContinuousEffect extends Effect { boolean isYourNextEndStep(Game game); + boolean isTheNextEndStep(Game game); + boolean isYourNextUpkeepStep(Game game); @Override diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java index 156e9400251..901dc44d102 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java @@ -56,9 +56,11 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu // until your next turn or until end of your next turn private UUID startingControllerId; // player to check for turn duration (can't different with real controller ability) + private UUID activePlayerId; // Player whose turn the effect started on private boolean startingTurnWasActive; // effect started during related players turn and related players turn was already active private int effectStartingOnTurn = 0; // turn the effect started - private int effectStartingEndStep = 0; + private int effectControllerStartingEndStep = 0; + private int effectActivePlayerStartingEndStep = 0; private int nextTurnNumber = Integer.MAX_VALUE; // effect is waiting for a step during your next turn, we store it if found. // set to the turn number on your next turn. private int effectStartingStepNum = 0; // Some continuous are waiting for the next step of a kind. @@ -93,7 +95,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu this.startingControllerId = effect.startingControllerId; this.startingTurnWasActive = effect.startingTurnWasActive; this.effectStartingOnTurn = effect.effectStartingOnTurn; - this.effectStartingEndStep = effect.effectStartingEndStep; + this.effectControllerStartingEndStep = effect.effectControllerStartingEndStep; this.dependencyTypes = effect.dependencyTypes; this.dependendToTypes = effect.dependendToTypes; this.characterDefining = effect.characterDefining; @@ -251,10 +253,12 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu @Override public void setStartingControllerAndTurnNum(Game game, UUID startingController, UUID activePlayerId) { this.startingControllerId = startingController; + this.activePlayerId = activePlayerId; this.startingTurnWasActive = activePlayerId != null && activePlayerId.equals(startingController); // you can't use "game" for active player cause it's called from tests/cheat too this.effectStartingOnTurn = game.getTurnNum(); - this.effectStartingEndStep = EndStepCountWatcher.getCount(startingController, game); + this.effectControllerStartingEndStep = EndStepCountWatcher.getCount(startingController, game); + this.effectActivePlayerStartingEndStep = EndStepCountWatcher.getCount(activePlayerId, game); this.effectStartingStepNum = game.getState().getStepNum(); } @@ -266,7 +270,12 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu @Override public boolean isYourNextEndStep(Game game) { - return EndStepCountWatcher.getCount(startingControllerId, game) > effectStartingEndStep; + return EndStepCountWatcher.getCount(startingControllerId, game) > effectControllerStartingEndStep; + } + + @Override + public boolean isTheNextEndStep(Game game) { + return EndStepCountWatcher.getCount(activePlayerId, game) > effectActivePlayerStartingEndStep; } public boolean isEndCombatOfYourNextTurn(Game game) { @@ -298,6 +307,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu case UntilYourNextTurn: case UntilEndOfYourNextTurn: case UntilYourNextEndStep: + case UntilTheNextEndStep: case UntilEndCombatOfYourNextTurn: case UntilYourNextUpkeepStep: break; @@ -342,6 +352,10 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu return this.isYourNextEndStep(game); } break; + case UntilTheNextEndStep: + if (player != null && player.isInGame()) { + return this.isTheNextEndStep(game); + } case UntilEndCombatOfYourNextTurn: if (player != null && player.isInGame()) { return this.isEndCombatOfYourNextTurn(game); diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java index c8707a7289f..a75c476971b 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java @@ -156,6 +156,7 @@ public class ContinuousEffectsList extends ArrayList case UntilEndOfYourNextTurn: case UntilEndCombatOfYourNextTurn: case UntilYourNextEndStep: + case UntilTheNextEndStep: case UntilYourNextUpkeepStep: // until your turn effects continue until real turn reached, their used it's own inactive method // 514.2 Second, the following actions happen simultaneously: all damage marked on permanents diff --git a/Mage/src/main/java/mage/constants/Duration.java b/Mage/src/main/java/mage/constants/Duration.java index dd14431a6b5..da37dd1e973 100644 --- a/Mage/src/main/java/mage/constants/Duration.java +++ b/Mage/src/main/java/mage/constants/Duration.java @@ -13,6 +13,7 @@ public enum Duration { EndOfTurn("until end of turn", true, true), UntilYourNextTurn("until your next turn", true, true), UntilYourNextEndStep("until your next end step", true, true), + UntilTheNextEndStep("until your next end step", true, true), UntilEndCombatOfYourNextTurn("until end of combat on your next turn", true, true), UntilYourNextUpkeepStep("until your next upkeep", true, true), UntilEndOfYourNextTurn("until the end of your next turn", true, true),