idsList = new ArrayList<>();
+ for (UUID id : oathbreakersToSearch) {
+ idsList.add(new PermanentIdPredicate(id));
+ }
+ this.filter.add(Predicates.or(idsList));
+ } else {
+ // random id to disable condition
+ this.filter.add(new PermanentIdPredicate(UUID.randomUUID()));
+ }
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ // source.getSourceId() is null for commander's effects
+ int permanentsOnBattlefield = game.getBattlefield().count(this.filter, source.getSourceId(), playerId, game);
+ return permanentsOnBattlefield > 0;
+ }
+
+ @Override
+ public String toString() {
+ return filter.getMessage();
+ }
+}
diff --git a/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java b/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java
index fb0d3beb18b..7a7e10b7406 100644
--- a/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java
@@ -5,6 +5,7 @@ import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
+import mage.constants.CommanderCardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
@@ -83,7 +84,7 @@ class CommanderStormEffect extends OneShotEffect {
if (player == null) {
return false;
}
- stormCount = player.getCommandersIds().stream()
+ stormCount = game.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER).stream()
.map((commanderId) -> game.getState().getWatcher(CommanderPlaysCountWatcher.class).getPlaysCount(commanderId))
.reduce(stormCount, Integer::sum);
if (stormCount == 0) {
diff --git a/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java b/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java
index ebbc8c89015..6b4b95043ef 100644
--- a/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java
@@ -1,30 +1,27 @@
package mage.abilities.keyword;
-import java.util.Iterator;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility;
-import mage.abilities.costs.Cost;
-import mage.abilities.costs.Costs;
-import mage.abilities.costs.OptionalAdditionalCost;
-import mage.abilities.costs.OptionalAdditionalCostImpl;
-import mage.abilities.costs.OptionalAdditionalModeSourceCosts;
+import mage.abilities.costs.*;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
+import java.util.Iterator;
+
/**
* 702.40. Entwine
- *
+ *
* 702.40a Entwine is a static ability of modal spells (see rule 700.2) that
* functions while the spell is on the stack. "Entwine [cost]" means "You may
* choose all modes of this spell instead of just one. If you do, you pay an
* additional [cost]." Using the entwine ability follows the rules for choosing
* modes and paying additional costs in rules 601.2b and 601.2e-g.
- *
+ *
* 702.40b If the entwine cost was paid, follow the text of each of the modes in
* the order written on the card when the spell resolves.
*
@@ -33,15 +30,18 @@ import mage.players.Player;
public class EntwineAbility extends StaticAbility implements OptionalAdditionalModeSourceCosts {
private static final String keywordText = "Entwine";
- private static final String reminderText = "Choose both if you pay the entwine cost.";
protected OptionalAdditionalCost additionalCost;
public EntwineAbility(String manaString) {
super(Zone.STACK, null);
- this.additionalCost = new OptionalAdditionalCostImpl(keywordText, reminderText, new ManaCostsImpl(manaString));
+ this.additionalCost = new OptionalAdditionalCostImpl(keywordText, "Choose both if you pay the entwine cost.", new ManaCostsImpl(manaString));
}
public EntwineAbility(Cost cost) {
+ this(cost, "Choose both if you pay the entwine cost.");
+ }
+
+ public EntwineAbility(Cost cost, String reminderText) {
super(Zone.STACK, null);
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderText, cost);
setRuleAtTheTop(true);
@@ -80,28 +80,32 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
@Override
public void changeModes(Ability ability, Game game) {
- if (ability instanceof SpellAbility) {
- Player player = game.getPlayer(controllerId);
- if (player != null) {
- this.resetCosts();
- if (additionalCost != null) {
- if (additionalCost.canPay(ability, ability.getSourceId(), ability.getControllerId(), game)
- && player.chooseUse(Outcome.Benefit, "Pay " + additionalCost.getText(false) + " ?", ability, game)) {
+ if (!(ability instanceof SpellAbility)) {
+ return;
+ }
+ Player player = game.getPlayer(controllerId);
+ if (player == null) {
+ return;
+ }
+ this.resetCosts();
+ if (additionalCost == null) {
+ return;
+ }
+ if (additionalCost.canPay(ability, ability.getSourceId(), ability.getControllerId(), game)
+ && player.chooseUse(Outcome.Benefit, "Pay " + additionalCost.getText(false) + " ?", ability, game)) {
- additionalCost.activate();
- ability.getModes().setAdditionalCost(this);
- ability.getModes().setMinModes(2);
- ability.getModes().setMaxModes(2);
- }
- }
- }
+ additionalCost.activate();
+ int modeCount = ability.getModes().size();
+ ability.getModes().setAdditionalCost(this);
+ ability.getModes().setMinModes(modeCount);
+ ability.getModes().setMaxModes(modeCount);
}
}
@Override
public void addOptionalAdditionalModeCosts(Ability ability, Game game) {
if (additionalCost.isActivated()) {
- for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) {
+ for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext(); ) {
Cost cost = (Cost) it.next();
if (cost instanceof ManaCostsImpl) {
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
diff --git a/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java b/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java
index e63ae4d5a2d..157a173354b 100644
--- a/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/NinjutsuAbility.java
@@ -1,6 +1,5 @@
package mage.abilities.keyword;
-import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.costs.Cost;
@@ -21,24 +20,25 @@ import mage.players.Player;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.common.TargetControlledPermanent;
+import java.util.UUID;
+
/**
* 702.47. Ninjutsu
- *
+ *
* 702.47a Ninjutsu is an activated ability that functions only while the card
* with ninjutsu is in a player's hand. "Ninjutsu [cost]" means "[Cost], Reveal
* this card from your hand, Return an unblocked attacking creature you control
* to its owner's hand: Put this card onto the battlefield from your hand tapped
* and attacking."
- *
+ *
* 702.47b The card with ninjutsu remains revealed from the time the ability is
* announced until the ability leaves the stack.
- *
+ *
* 702.47c A ninjutsu ability may be activated only while a creature on the
* battlefield is unblocked (see rule 509.1h). The creature with ninjutsu is put
* onto the battlefield unblocked. It will be attacking the same player or
* planeswalker as the creature that was returned to its owner's hand.
*
- *
* @author LevelX2
*/
public class NinjutsuAbility extends ActivatedAbilityImpl {
@@ -51,7 +51,6 @@ public class NinjutsuAbility extends ActivatedAbilityImpl {
}
/**
- *
* @param manaCost ninjutsu mana cost
*/
public NinjutsuAbility(ManaCost manaCost) {
@@ -195,7 +194,7 @@ class RevealNinjutsuCardCost extends CostImpl {
Card card = player.getHand().get(ability.getSourceId(), game);
if (card == null && commander
- && player.getCommandersIds().contains(ability.getSourceId())) {
+ && game.getCommandersIds(player).contains(ability.getSourceId())) {
for (CommandObject coj : game.getState().getCommand()) {
if (coj != null && coj.getId().equals(ability.getSourceId())) {
card = game.getCard(ability.getSourceId());
diff --git a/Mage/src/main/java/mage/abilities/mana/CommanderColorIdentityManaAbility.java b/Mage/src/main/java/mage/abilities/mana/CommanderColorIdentityManaAbility.java
index 44ad62ff232..47f7d1dea56 100644
--- a/Mage/src/main/java/mage/abilities/mana/CommanderColorIdentityManaAbility.java
+++ b/Mage/src/main/java/mage/abilities/mana/CommanderColorIdentityManaAbility.java
@@ -1,8 +1,5 @@
package mage.abilities.mana;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
@@ -17,8 +14,11 @@ import mage.filter.FilterMana;
import mage.game.Game;
import mage.players.Player;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
/**
- *
* @author LevelX2
*/
public class CommanderColorIdentityManaAbility extends ActivatedManaAbilityImpl {
@@ -46,7 +46,7 @@ public class CommanderColorIdentityManaAbility extends ActivatedManaAbilityImpl
if (netMana.isEmpty() && game != null) {
Player controller = game.getPlayer(getControllerId());
if (controller != null) {
- for (UUID commanderId : controller.getCommandersIds()) {
+ for (UUID commanderId : game.getCommandersIds(controller)) {
Card commander = game.getCard(commanderId);
if (commander != null) {
FilterMana commanderMana = commander.getColorIdentity();
@@ -114,7 +114,7 @@ class CommanderIdentityManaEffect extends ManaEffect {
if (controller != null) {
Choice choice = new ChoiceImpl();
choice.setMessage("Pick a mana color");
- for (UUID commanderId : controller.getCommandersIds()) {
+ for (UUID commanderId : game.getCommandersIds(controller)) {
Card commander = game.getCard(commanderId);
if (commander != null) {
FilterMana commanderMana = commander.getColorIdentity();
diff --git a/Mage/src/main/java/mage/cards/ExpansionSet.java b/Mage/src/main/java/mage/cards/ExpansionSet.java
index ba8ac8073f4..3a362676025 100644
--- a/Mage/src/main/java/mage/cards/ExpansionSet.java
+++ b/Mage/src/main/java/mage/cards/ExpansionSet.java
@@ -24,7 +24,7 @@ public abstract class ExpansionSet implements Serializable {
private static final Logger logger = Logger.getLogger(ExpansionSet.class);
public static final CardGraphicInfo NON_FULL_USE_VARIOUS = new CardGraphicInfo(null, true);
public static final CardGraphicInfo FULL_ART_BFZ_VARIOUS = new CardGraphicInfo(FrameStyle.BFZ_FULL_ART_BASIC, true);
-
+ public static final CardGraphicInfo FULL_ART_ZEN_VARIOUS = new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true);
public class SetCardInfo implements Serializable {
diff --git a/Mage/src/main/java/mage/constants/CommanderCardType.java b/Mage/src/main/java/mage/constants/CommanderCardType.java
new file mode 100644
index 00000000000..a4c3e007d26
--- /dev/null
+++ b/Mage/src/main/java/mage/constants/CommanderCardType.java
@@ -0,0 +1,10 @@
+package mage.constants;
+
+/**
+ * @author JayDi85
+ */
+public enum CommanderCardType {
+ ANY,
+ COMMANDER_OR_OATHBREAKER,
+ SIGNATURE_SPELL
+}
diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/CommanderPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/CommanderPredicate.java
index 58bc4269b22..ada3bcbaeaa 100644
--- a/Mage/src/main/java/mage/filter/predicate/permanent/CommanderPredicate.java
+++ b/Mage/src/main/java/mage/filter/predicate/permanent/CommanderPredicate.java
@@ -20,7 +20,7 @@ public enum CommanderPredicate implements Predicate {
public boolean apply(Permanent input, Game game) {
Player owner = game.getPlayer(input.getOwnerId());
return owner != null
- && owner.getCommandersIds().contains(input.getId());
+ && game.getCommandersIds(owner).contains(input.getId());
}
@Override
diff --git a/Mage/src/main/java/mage/game/Game.java b/Mage/src/main/java/mage/game/Game.java
index 30638c63a1c..af5ce5a1ead 100644
--- a/Mage/src/main/java/mage/game/Game.java
+++ b/Mage/src/main/java/mage/game/Game.java
@@ -476,4 +476,9 @@ public interface Game extends MageItem, Serializable {
Mulligan getMulligan();
+ Set getCommandersIds(Player player, CommanderCardType commanderCardType);
+
+ default Set getCommandersIds(Player player) {
+ return getCommandersIds(player, CommanderCardType.ANY);
+ }
}
diff --git a/Mage/src/main/java/mage/game/GameCommanderImpl.java b/Mage/src/main/java/mage/game/GameCommanderImpl.java
index eb5094f735b..1430103aca7 100644
--- a/Mage/src/main/java/mage/game/GameCommanderImpl.java
+++ b/Mage/src/main/java/mage/game/GameCommanderImpl.java
@@ -41,6 +41,7 @@ public abstract class GameCommanderImpl extends GameImpl {
@Override
protected void init(UUID choosingPlayerId) {
+ // Karn Liberated calls it to restart game, all data and commanders must be re-initialized
// plays watcher
state.addWatcher(new CommanderPlaysCountWatcher());
@@ -49,20 +50,19 @@ public abstract class GameCommanderImpl extends GameImpl {
for (UUID playerId : state.getPlayerList(startingPlayerId)) {
Player player = getPlayer(playerId);
if (player != null) {
- if (player.getSideboard().isEmpty()) { // needed for restart game of e.g. Karn Liberated
- for (UUID commanderId : player.getCommandersIds()) {
- Card commander = this.getCard(commanderId);
- if (commander != null) {
- initCommander(commander, player);
- }
+ // add new commanders
+ for (UUID id : player.getSideboard()) {
+ Card commander = this.getCard(id);
+ if (commander != null) {
+ addCommander(commander, player);
}
- } else {
- while (!player.getSideboard().isEmpty()) {
- Card commander = this.getCard(player.getSideboard().iterator().next());
- if (commander != null) {
- player.addCommanderId(commander.getId());
- initCommander(commander, player);
- }
+ }
+
+ // init commanders
+ for (UUID commanderId : this.getCommandersIds(player)) {
+ Card commander = this.getCard(commanderId);
+ if (commander != null) {
+ initCommander(commander, player);
}
}
}
@@ -75,17 +75,27 @@ public abstract class GameCommanderImpl extends GameImpl {
}
public void initCommander(Card commander, Player player) {
- Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects"));
commander.moveToZone(Zone.COMMAND, null, this, true);
commander.getAbilities().setControllerId(player.getId());
- ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary));
- ability.addEffect(new CommanderCostModification(commander.getId()));
- CommanderInfoWatcher watcher = new CommanderInfoWatcher(commander.getId(), checkCommanderDamage);
+
+ Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects"));
+ initCommanderEffects(commander, player, ability);
+ CommanderInfoWatcher watcher = initCommanderWatcher(commander, checkCommanderDamage);
getState().addWatcher(watcher);
watcher.addCardInfoToCommander(this);
this.getState().addAbility(ability, null);
}
+ public CommanderInfoWatcher initCommanderWatcher(Card commander, boolean checkCommanderDamage) {
+ return new CommanderInfoWatcher("Commander", commander.getId(), checkCommanderDamage);
+ }
+
+ public void initCommanderEffects(Card commander, Player player, Ability commanderAbility) {
+ // all commander effects must be independent from sourceId or controllerId
+ commanderAbility.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary));
+ commanderAbility.addEffect(new CommanderCostModification(commander.getId()));
+ }
+
//20130711
/*903.8. The Commander variant uses an alternate mulligan rule.
* Each time a player takes a mulligan, rather than shuffling their entire hand of cards into their library, that player exiles any number of cards from their hand face down.
@@ -173,7 +183,7 @@ public abstract class GameCommanderImpl extends GameImpl {
@Override
protected boolean checkStateBasedActions() {
for (Player player : getPlayers().values()) {
- for (UUID commanderId : player.getCommandersIds()) {
+ for (UUID commanderId : this.getCommandersIds(player)) {
CommanderInfoWatcher damageWatcher = getState().getWatcher(CommanderInfoWatcher.class, commanderId);
if (damageWatcher == null) {
continue;
@@ -207,4 +217,8 @@ public abstract class GameCommanderImpl extends GameImpl {
this.checkCommanderDamage = checkCommanderDamage;
}
+ public void addCommander(Card card, Player player) {
+ player.addCommanderId(card.getId());
+ }
+
}
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index a2d57f858c5..f3f48ac1436 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -2871,7 +2871,7 @@ public abstract class GameImpl implements Game, Serializable {
// as commander (only commander games, look at init code in GameCommanderImpl)
if (this instanceof GameCommanderImpl) {
for (Card card : command) {
- player.addCommanderId(card.getId());
+ ((GameCommanderImpl) this).addCommander(card, player);
// no needs in initCommander call -- it's uses on game startup (init)
}
} else if (!command.isEmpty()) {
@@ -3193,4 +3193,9 @@ public abstract class GameImpl implements Game, Serializable {
return mulligan;
}
+ @Override
+ public Set getCommandersIds(Player player, CommanderCardType commanderCardType) {
+ return player.getCommandersIds();
+ }
+
}
diff --git a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java
index f89c542c844..83f17e0126b 100644
--- a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java
+++ b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java
@@ -62,7 +62,7 @@ public abstract class GameTinyLeadersImpl extends GameImpl {
ability.addEffect(new CommanderCostModification(commander.getId()));
// Commander rule #4 was removed Jan. 18, 2016
// ability.addEffect(new CommanderManaReplacementEffect(player.getId(), CardUtil.getColorIdentity(commander)));
- CommanderInfoWatcher watcher = new CommanderInfoWatcher(commander.getId(), false);
+ CommanderInfoWatcher watcher = new CommanderInfoWatcher("Commander", commander.getId(), false);
getState().addWatcher(watcher);
watcher.addCardInfoToCommander(this);
this.getState().addAbility(ability, null);
diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java
index 6dc09c8f9dd..d6f8bb8bfc2 100644
--- a/Mage/src/main/java/mage/players/Player.java
+++ b/Mage/src/main/java/mage/players/Player.java
@@ -696,9 +696,11 @@ public interface Player extends MageItem, Copyable {
/**
* Get the commanderIds of the player
+ * Deprecated, use game.getCommandersIds(xxx) instead
*
* @return
*/
+ @Deprecated
Set getCommandersIds();
/**
@@ -853,7 +855,9 @@ public interface Player extends MageItem, Copyable {
void revokePermissionToSeeHandCards();
- boolean isRequestToShowHandCardsAllowed();
+ boolean isPlayerAllowedToRequestHand(UUID gameId, UUID requesterPlayerId);
+
+ void addPlayerToRequestedHandList(UUID gameId, UUID requesterPlayerId);
Set getUsersAllowedToSeeHandCards();
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index c39eff82b06..6dd3cbbe366 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -302,7 +302,7 @@ public abstract class PlayerImpl implements Player, Serializable {
this.sideboard = player.getSideboard().copy();
this.hand = player.getHand().copy();
this.graveyard = player.getGraveyard().copy();
- this.commandersIds = player.getCommandersIds();
+ this.commandersIds = new HashSet<>(player.getCommandersIds());
this.abilities = player.getAbilities().copy();
this.counters = player.getCounters().copy();
@@ -2284,6 +2284,7 @@ public abstract class PlayerImpl implements Player, Serializable {
break;
case PERMISSION_REQUESTS_ALLOWED_ON:
userData.setAllowRequestShowHandCards(true);
+ userData.resetRequestedHandPlayersList(game.getId()); // users can send request again
break;
}
logger.trace("PASS Priority: " + playerAction.toString());
@@ -3969,8 +3970,13 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
- public boolean isRequestToShowHandCardsAllowed() {
- return userData.isAllowRequestShowHandCards();
+ public boolean isPlayerAllowedToRequestHand(UUID gameId, UUID requesterPlayerId) {
+ return userData.isAllowRequestHandToPlayer(gameId, requesterPlayerId);
+ }
+
+ @Override
+ public void addPlayerToRequestedHandList(UUID gameId, UUID requesterPlayerId) {
+ userData.addPlayerToRequestedHandList(gameId, requesterPlayerId);
}
@Override
diff --git a/Mage/src/main/java/mage/players/net/UserData.java b/Mage/src/main/java/mage/players/net/UserData.java
index fec7fbcba2c..06f2aff9224 100644
--- a/Mage/src/main/java/mage/players/net/UserData.java
+++ b/Mage/src/main/java/mage/players/net/UserData.java
@@ -1,6 +1,7 @@
package mage.players.net;
import java.io.Serializable;
+import java.util.*;
/**
* User data that is passed during connection to the server.
@@ -24,6 +25,7 @@ public class UserData implements Serializable {
protected boolean autoOrderTrigger;
protected boolean useFirstManaAbility = false;
private String userIdStr;
+ protected Map> requestedHandPlayersList; // game -> players list
protected String matchHistory;
protected int matchQuitRatio;
@@ -35,9 +37,9 @@ public class UserData implements Serializable {
private int limitedRating;
public UserData(UserGroup userGroup, int avatarId, boolean showAbilityPickerForced,
- boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps,
- String flagName, boolean askMoveToGraveOrder, boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted,
- boolean passPriorityCast, boolean passPriorityActivation, boolean autoOrderTrigger, boolean useFirstManaAbility, String userIdStr) {
+ boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps,
+ String flagName, boolean askMoveToGraveOrder, boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted,
+ boolean passPriorityCast, boolean passPriorityActivation, boolean autoOrderTrigger, boolean useFirstManaAbility, String userIdStr) {
this.groupId = userGroup.getGroupId();
this.avatarId = avatarId;
this.showAbilityPickerForced = showAbilityPickerForced;
@@ -57,6 +59,7 @@ public class UserData implements Serializable {
this.tourneyHistory = "";
this.tourneyQuitRatio = 0;
this.userIdStr = userIdStr;
+ this.requestedHandPlayersList = new HashMap<>();
}
public void update(UserData userData) {
@@ -106,14 +109,35 @@ public class UserData implements Serializable {
this.showAbilityPickerForced = showAbilityPickerForced;
}
- public boolean isAllowRequestShowHandCards() {
+ public boolean isAllowRequestHandToAll() {
return allowRequestShowHandCards;
}
+ public boolean isAllowRequestHandToPlayer(UUID gameId, UUID requesterPlayerId) {
+ // once per game
+ boolean allowToPlayer = true;
+ if (requestedHandPlayersList.containsKey(gameId) && requestedHandPlayersList.get(gameId).contains(requesterPlayerId)) {
+ allowToPlayer = false;
+ }
+ return isAllowRequestHandToAll() && allowToPlayer;
+ }
+
+ public void addPlayerToRequestedHandList(UUID gameId, UUID requesterPlayerId) {
+ if (!requestedHandPlayersList.containsKey(gameId)) {
+ requestedHandPlayersList.put(gameId, new HashSet<>());
+ }
+ Set requestedPlayers = requestedHandPlayersList.get(gameId);
+ requestedPlayers.add(requesterPlayerId);
+ }
+
public void setAllowRequestShowHandCards(boolean allowRequestShowHandCards) {
this.allowRequestShowHandCards = allowRequestShowHandCards;
}
+ public void resetRequestedHandPlayersList(UUID gameId) {
+ this.requestedHandPlayersList.remove(gameId);
+ }
+
public UserSkipPrioritySteps getUserSkipPrioritySteps() {
return userSkipPrioritySteps;
}
diff --git a/Mage/src/main/java/mage/watchers/common/CommanderInfoWatcher.java b/Mage/src/main/java/mage/watchers/common/CommanderInfoWatcher.java
index d72635b4006..0cfc776fc30 100644
--- a/Mage/src/main/java/mage/watchers/common/CommanderInfoWatcher.java
+++ b/Mage/src/main/java/mage/watchers/common/CommanderInfoWatcher.java
@@ -26,17 +26,20 @@ public class CommanderInfoWatcher extends Watcher {
private final Map damageToPlayer = new HashMap<>();
private final boolean checkCommanderDamage;
+ private final String commanderTypeName;
- public CommanderInfoWatcher(UUID commander, boolean checkCommanderDamage) {
+ public CommanderInfoWatcher(String commanderTypeName, UUID commander, boolean checkCommanderDamage) {
super(WatcherScope.CARD);
this.sourceId = commander;
this.checkCommanderDamage = checkCommanderDamage;
+ this.commanderTypeName = commanderTypeName;
}
public CommanderInfoWatcher(final CommanderInfoWatcher watcher) {
super(watcher);
this.damageToPlayer.putAll(watcher.damageToPlayer);
this.checkCommanderDamage = watcher.checkCommanderDamage;
+ this.commanderTypeName = watcher.commanderTypeName;
}
@Override
@@ -78,7 +81,7 @@ public class CommanderInfoWatcher extends Watcher {
}
if (object != null) {
StringBuilder sb = new StringBuilder();
- sb.append("Commander");
+ sb.append("" + commanderTypeName + "");
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
int playsCount = watcher.getPlaysCount(sourceId);
if (playsCount > 0) {
@@ -89,9 +92,9 @@ public class CommanderInfoWatcher extends Watcher {
if (checkCommanderDamage) {
for (Map.Entry entry : damageToPlayer.entrySet()) {
Player damagedPlayer = game.getPlayer(entry.getKey());
- sb.append("Commander did ").append(entry.getValue()).append(" combat damage to player ").append(damagedPlayer.getLogName()).append('.');
+ sb.append("" + commanderTypeName + " did ").append(entry.getValue()).append(" combat damage to player ").append(damagedPlayer.getLogName()).append('.');
this.addInfo(object, "Commander" + entry.getKey(),
- "Commander did " + entry.getValue() + " combat damage to player " + damagedPlayer.getLogName() + '.', game);
+ "" + commanderTypeName + " did " + entry.getValue() + " combat damage to player " + damagedPlayer.getLogName() + '.', game);
}
}
}
diff --git a/Mage/src/main/java/mage/watchers/common/CommanderPlaysCountWatcher.java b/Mage/src/main/java/mage/watchers/common/CommanderPlaysCountWatcher.java
index 789cb2d2c84..43c7a19b8d8 100644
--- a/Mage/src/main/java/mage/watchers/common/CommanderPlaysCountWatcher.java
+++ b/Mage/src/main/java/mage/watchers/common/CommanderPlaysCountWatcher.java
@@ -43,7 +43,7 @@ public class CommanderPlaysCountWatcher extends Watcher {
UUID possibleCommanderId = event.getSourceId();
boolean isCommanderObject = false;
for (Player player : game.getPlayers().values()) {
- if (player.getCommandersIds().contains(possibleCommanderId)) {
+ if (game.getCommandersIds(player).contains(possibleCommanderId)) {
isCommanderObject = true;
break;
}
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 783e3ce4813..50af935a091 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -35118,6 +35118,7 @@ Morophon, the Boundless|Modern Horizons|1|M|{7}|Legendary Creature - Shapeshifte
Astral Drift|Modern Horizons|3|R|{2}{W}|Enchantment|||Whenever you cycle Astral Drift or cycle another card while Astral Drift is on the battlefield, you may exile target creature. If you do, return that card to the battlefield under its owner's control at the beginning of the next end step.$Cycling {2}{W}|
Battle Screech|Modern Horizons|4|U|{2}{W}{W}|Sorcery|||Create two 1/1 white Bird creature tokens with flying.$Flashback—Tap three untapped white creatures you control.|
Dismantling Blow|Modern Horizons|5|U|{2}{W}|Instant|||Kicker {2}{U}$Destroy target artifact or enchantment. If this spell was kicked, draw two cards.|
+Enduring Sliver|Modern Horizons|8|C|{1}{W}|Creature - Sliver|2|2|Outlast {2}$Other sliver creatures you control have outlast {2}.|
Force of Virtue|Modern Horizons|10|R|{2}{W}{W}|Enchantment|||If it's not your turn, you may exile a white card from your hand rather than pay this spell's mana cost.$Flash$Creatures you control get +1/+1.|
Generous Gift|Modern Horizons|11|U|{2}{W}|Instant|||Destroy target permanent. Its controller creates a 3/3 green Elephant creature token.|
Giver of Runes|Modern Horizons|13|R|{W}|Creature - Kor Cleric|1|2|{T}: Another target creature you control gains protection from colorless or from the color of your choice until end of turn.|
@@ -35125,9 +35126,11 @@ Impostor of the Sixth Pride|Modern Horizons|14|C|{1}{W}|Creature - Shapeshifter|
King of the Pride|Modern Horizons|16|U|{2}{W}|Creature - Cat|2|1|Other Cats you control get +2/+1.|
Martyr's Soul|Modern Horizons|19|C|{2}{W}|Creature - Spirit Soldier|3|2|Convoke$When Martyr's Soul enters the battlefield, if you control no tapped lands, put two +1/+1 counters on it.|
Ranger-Captain of Eos|Modern Horizons|21|M|{1}{W}{W}|Creature - Human Soldier|3|3|When Ranger-Captain of Eos enters the battlefield, you may search your library for a creature card with converted mana cost 1 or less, reveal it, put it into your hand, then shuffle your library.$Sacrifice Ranger-Captain of Eos: Your opponents can't cast noncreature spells this turn.|
+Segovian Angel|Modern Horizons|25|C|{W}|Creature - Angel|1|1|Flying, vigilance|
Serra the Benevolent|Modern Horizons|26|M|{2}{W}{W}|Legendary Planeswalker - Serra|4|+2: Creatures you control with flying get +1/+1 until end of turn.$-3: Create a 4/4 white Angel creature token with flying and vigilance.$-6: You get an emblem with "If you control a creature, damage that would reduce your life total to less than 1 reduces it to 1 instead."|
Sisay, Weatherlight Captain|Modern Horizons|29|R|{2}{W}|Legendary Creature - Human Soldier|2|2|Sisay, Weatherlight Captain gets +1/+1 for each color among other legendary permanents you control.${W}{U}{B}{R}{G}: Search your library for a legendary permanent card with converted mana cost less than Sisay's power, put that card onto the battlefield, then shuffle your library.|
Splicer's Skill|Modern Horizons|31|U|{2}{W}|Sorcery|||Create a 3/3 colorless Golem artifact creature token.$Splice onto instant or sorcery {3}{W}|
+Valiant Changeling|Modern Horizons|34|U|{5}{W}{W}|Creature - Shapeshifter|3|3|This spell costs {1} less to cast for each creature type among creatures you control. This effect can't reduce the amount of mana this spell costs by more than {5}.$Changeling$Double strike|
Wall of One Thousand Cuts|Modern Horizons|36|C|{3}{W}{W}|Creature - Wall|3|5|Defender, flying${W}: Wall of One Thousand Cuts can attack this turn as though it didn't have defender.|
Winds of Abandon|Modern Horizons|37|R|{1}{W}|Sorcery|||Exile target creature you don't control. For each creature exiled this way, its controller searches their library for a basic land card. Those players put those cards onto the battlefield tapped, then shuffle their libraries.$Overload {4}{W}{W}|
Wing Shards|Modern Horizons|38|U|{1}{W}{W}|Instant|||Target player sacrifices an attacking creature.$Storm|
@@ -35144,7 +35147,9 @@ Pondering Mage|Modern Horizons|63|C|{3}{U}{U}|Creature - Human Wizard|3|4|When P
Prohibit|Modern Horizons|64|C|{1}{U}|Instant|||Kicker {2}$Counter target spell if its converted mana cost is 2 or less. If this spell was kicked, counter that spell if its converted mana cost is 4 or less instead.|
Scour All Possibilities|Modern Horizons|67|C|{1}{U}|Sorcery|||Scry 2, then draw a card.$Flashback {4}{U}|
Scuttling Sliver|Modern Horizons|68|U|{2}{U}|Creature - Sliver Trilobite|2|2|Sliver creatures you control have "{2}: Untap this creature."|
+Spell Snuff|Modern Horizons|70|C|{1}{U}{U}|Instant|||Counter target spell.$Fateful hour — If you have 5 or less life, draw a card.|
Stream of Thought|Modern Horizons|71|C|{U}|Sorcery|||Target player puts the top four cards of their library into their graveyard. You shuffle up to four cards from your graveyard into your library.$Replicate {2}{U}{U}|
+String of Disappearances|Modern Horizons|72|C|{U}|Instant|||Return target creature to its owner's hand. Then that creature's controller may pay {U}{U}. If the player does, they may copy this spell and may choose a new target for that copy.|
Urza, Lord High Artificer|Modern Horizons|75|M|{2}{U}{U}|Legendary Creature - Human Artificer|1|4|When Urza, Lord High Artificer enters the battlefield, create a 0/0 colorless Construct artifact creature token with "This creature gets +1/+1 for each artifact you control."$Tap an untapped artifact you control: Add {U}.${5}: Shuffle your library, then exile the top card. Until end of turn, you may play that card without paying its mana cost.|
Cabal Therapist|Modern Horizons|80|R|{B}|Creature - Horror|1|1|Menace$At the beginning of your precombat main phase, you may sacrifice a creature. When you do, choose a nonland card name, then target player reveals their hand and discards all cards with that name.|
Changeling Outcast|Modern Horizons|82|C|{B}|Creature - Shapeshifter|1|1|Changeling$Changeling Outcast can't block and can't be blocked.|
@@ -35155,17 +35160,21 @@ Feaster of Fools|Modern Horizons|90|U|{4}{B}{B}|Creature - Demon|3|3|Convoke$Fly
Force of Despair|Modern Horizons|92|R|{1}{B}{B}|Instant|||If it's not your turn, you may exile a black card from your hand rather than pay this spell's mana cost.$Destroy all creatures that entered the battlefield this turn.|
Headless Specter|Modern Horizons|95|C|{1}{B}{B}|Creature - Specter|2|2|Flying$Hellbent — Whenever Headless Specter deals combat damage to a player, if you have no cards in hand, that player discards a card at random.|
Plague Engineer|Modern Horizons|100|R|{2}{B}|Creature - Carrier|2|2|Deathtouch$As Plague Engineer enters the battlefield, choose a creature type.$Creatures of the chosen type your opponents control get -1/-1.|
-Sling-Gang Lieutenant|Modern Horizons|108|U|{3}{B}|Creature - Goblin|||When Sling-Gang Lieutenant enters the battlefield, create two 1/1 red Goblin creature tokens.$Sacrifice a Goblin: Target player loses 1 life and you gain 1 life.|
+Sling-Gang Lieutenant|Modern Horizons|108|U|{3}{B}|Creature - Goblin|1|1|When Sling-Gang Lieutenant enters the battlefield, create two 1/1 red Goblin creature tokens.$Sacrifice a Goblin: Target player loses 1 life and you gain 1 life.|
Umezawa's Charm|Modern Horizons|111|C|{1}{B}|Instant|||Choose one—$• Target creature gets +2/+2 until end of turn.$• Target creature gets -1/-1 until end of turn.$• You gain 2 life.|
Undead Augur|Modern Horizons|112|U|{B}{B}|Creature - Zombie Wizard|2|2|Whenever Undead Augur or another Zombie you control dies, you draw a card and you lose 1 life.|
Venomous Changeling|Modern Horizons|114|C|{2}{B}|Creature - Shapeshifter|1|3|Changeling$Deathtouch|
+Yawgmoth, Thran Physician|Modern Horizons|116|M|{2}{B}{B}|Legendary Creature - Human Cleric|2|4|Protection from Humans$Pay 1 life, Sacrifice another creature: Put a -1/-1 counter on up to one target creature and draw a card.${B}{B}, Discard a card: Proliferate.|
Aria of Flame|Modern Horizons|118|R|{2}{R}|Enchantment|||When Aria of Flame enters the battlefield, each opponent gains 10 life.$Whenever you cast an instant or sorcery spell, put a verse counter on Aria of Flame, then it deals damage equal to the number of verse counters on it to target player or planeswalker.|
+Bladeback Sliver|Modern Horizons|119|C|{1}{R}|Creature - Sliver|2|2|Hellbent — As long as you have no cards in hand, Sliver creatures you control have "{T}: This creature deals 1 damage to target player or planeswalker."|
Firebolt|Modern Horizons|122|U|{R}|Sorcery|||Firebolt deals 2 damage to any target.$Flashback {4}{R}|
Fists of Flame|Modern Horizons|123|C|{1}{R}|Instant|||Draw a card. Until end of turn, target creature gains trample and gets +1/+0 for each card you've drawn this turn.|
Force of Rage|Modern Horizons|124|R|{1}{R}{R}|Instant|||If it's not your turn, you may exile a red card from your hand rather than pay this spell's mana cost.$Create two 3/1 red Elemental creature tokens with trample and haste. Sacrifice those tokens at the beginning of your next upkeep.|
Goatnap|Modern Horizons|126|C|{2}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. If that creature is a Goat, it also gets +3/+0 until end of turn.|
+Goblin Champion|Modern Horizons|127|C|{R}|Creature - Goblin Warrior|0|1|Haste$Exalted|
Goblin Engineer|Modern Horizons|128|R|{1}{R}|Creature - Goblin Artificer|1|2|When Goblin Engineer enters the battlefield, you may search your library for an artifact card, put it into your graveyard, then shuffle your library.${R}, {T}, Sacrifice an artifact: Return target artifact card with converted mana cost 3 or less from your graveyard to the battlefield.|
Goblin Matron|Modern Horizons|129|U|{2}{R}|Creature - Goblin|1|1|When Goblin Matron enters the battlefield, you may search your library for a Goblin card, reveal that card, and put it into your hand. If you do, shuffle your library.|
+Goblin Oriflamme|Modern Horizons|130|U|{1}{R}|Enchantment|||Attacking creatures you control get +1/+0.|
Goblin War Party|Modern Horizons|131|C|{3}{R}|Sorcery|||Choose one —$• Create three 1/1 red Goblin creature tokens.$• Creatures you control get +1/+1 and gain haste until end of turn.$Entwine {2}{R}|
Hollowhead Sliver|Modern Horizons|132|U|{2}{R}|Creature - Sliver|2|2|Sliver creatures you control have "{T}, Discard a card: Draw a card."|
Lava Dart|Modern Horizons|134|C|{R}|Instant|||Lava Dart deals 1 damage to any target.$Flashback—Sacrifice a Mountain.|
@@ -35177,6 +35186,8 @@ Ravenous Giant|Modern Horizons|143|U|{2}{R}{R}|Creature - Giant|5|5|At the begin
Seasoned Pyromancer|Modern Horizons|145|M|{1}{R}{R}|Creature - Human Shaman|2|2|When Seasoned Pyromancer enters the battlefield, discard two cards, then draw two cards. For each nonland card discarded this way, create a 1/1 red Elemental creature token.${3}{R}{R}, Exile Seasoned Pyromancer from your graveyard: Create two 1/1 red Elemental creature tokens.|
Ayula, Queen Among Bears|Modern Horizons|155|R|{1}{G}|Legendary Creature - Bear|2|2|Whenever another Bear enters the battlefield under your control, choose one —$• Put two +1/+1 counters on target Bear.$• Target Bear you control fights target creature you don't control.|
Ayula's Influence|Modern Horizons|156|R|{G}{G}{G}|Enchantment|||Discard a land card: Create a 2/2 green Bear creature token.|
+Collector Ouphe|Modern Horizons|158|R|{1}{G}|Creature - Ouphe|2|2|Activated abilities of artifacts can't be activated.|
+Crashing Footfalls|Modern Horizons|160|R||Sorcery|||Suspend 4—{G}$Create two 4/4 green Rhino creature tokens with trample.|
Deep Forest Hermit|Modern Horizons|161|R|{3}{G}{G}|Creature - Elf Druid|1|1|Vanishing 3$When Deep Forest Hermit enters the battlefield, create four 1/1 green Squirrel creature tokens.$Squirrels you control get +1/+1.|
Elvish Fury|Modern Horizons|162|C|{G}|Instant|||Buyback {4}$Target creature gets +2/+2 until end of turn.|
Force of Vigor|Modern Horizons|164|R|{2}{G}{G}|Instant|||If it's not your turn, you may exile a green card from your hand rather than pay this spell's mana cost.$Destroy up to two target artifacts and/or enchantments.|
@@ -35184,6 +35195,7 @@ Genesis|Modern Horizons|166|R|{4}{G}|Creature - Incarnation|4|4|At the beginning
Glacial Revelation|Modern Horizons|167|U|{2}{G}|Sorcery|||Reveal the top six cards of your library. You may put any number of snow permanent cards from among them into your hand. Put the rest into your graveyard.|
Hexdrinker|Modern Horizons|168|M|{G}|Creature - Snake|2|1|Level up {1}$LEVEL 3-7$4/4$Protection from instants$LEVEL 8+$6/6$Protection from everything|
Mother Bear|Modern Horizons|171|C|{1}{G}|Creature - Bear|2|2|{3}{G}{G}, Exile Mother Bear from your graveyard: Create two 2/2 green Bear creature tokens. Activate this ability only any time you could cast a sorcery.|
+Nantuko Cultivator|Modern Horizons|173|U|{3}{G}|Creature - Insect Druid|2|2|When Nantuko Cultivator enters the battlefield, you may discard any number of land cards. Put that many +1/+1 counters on Nantuko Cultivator and draw that many cards.|
Nimble Mongoose|Modern Horizons|174|C|{G}|Creature - Mongoose|1|1|Shroud$Threshold — Nimble Mongoose gets +2/+2 as long as seven or more cards are in your graveyard.|
Regrowth|Modern Horizons|175|U|{1}{G}|Sorcery|||Return target card from your graveyard to your hand.|
Savage Swipe|Modern Horizons|178|C|{G}|Sorcery|||Target creature you control gets +2/+2 until end of turn if its power is 2. Then it fights target creature you don't control.|
@@ -35193,14 +35205,20 @@ Springbloom Druid|Modern Horizons|181|C|{2}{G}|Creature - Elf Druid|1|1|When Spr
Squirrel Nest|Modern Horizons|182|U|{1}{G}{G}|Enchantment - Aura|||Enchant land$Enchanted land has "{T}: Create a 1/1 green Squirrel creature token."|
Tempered Sliver|Modern Horizons|183|U|{2}{G}|Creature - Sliver|2|2|Sliver creatures you control have "Whenever this creature deals combat damage to a player, put a +1/+1 counter on it."|
Unbound Flourishing|Modern Horizons|189|M|{2}{G}|Enchantment|||Whenever you cast a permanent spell with a mana cost that contains {X}, double the value of X.$Whenever you cast an instant or sorcery spell or activate an ability, if that spell's mana cost or that ability's activation cost contains {X}, copy that spell or ability. You may choose new targets for the copy.|
+Weather the Storm|Modern Horizons|191|C|{1}{G}|Instant|||You gain 3 life.$Storm|
+Webweaver Changeling|Modern Horizons|192|U|{3}{G}{G}|Creature - Shapeshifter|3|5|Changeling$Reach$When Webweaver Changeling enters the battlefield, if there are three or more creature cards in your graveyard, you gain 5 life.|
Abominable Treefolk|Modern Horizons|194|U|{2}{G}{U}|Snow Creature - Treefolk|*|*|Trample$Abominable Treefolk's power and toughness are each equal to the number of snow permanents you control.$When Abominable Treefolk enters the battlefield, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step.|
Cloudshredder Sliver|Modern Horizons|195|R|{R}{W}|Creature - Sliver|1|1|Sliver creatures you control have flying and haste.|
Collected Conjuring|Modern Horizons|196|R|{2}{U}{R}|Sorcery|||Exile the top six cards of your library. You may cast up to two sorcery cards with converted mana costs 3 or less from among them without paying their mana cost. Put the exiled cards not cast this way on the bottom of your library in a random order.|
+Eladamri's Call|Modern Horizons|197|R|{G}{W}|Instant|||Search your library for a creature card, reveal that card, put it into your hand, then shuffle your library.|
Etchings of the Chosen|Modern Horizons|198|U|{1}{W}{B}|Enchantment|||As Etchings of the Chosen enters the battlefield, choose a creature type.$Creatures you control of the chosen type get +1/+1.${1}, Sacrifice a creature of the chosen type: Target creature you control gains indestructible until end of turn.|
Fallen Shinobi|Modern Horizons|199|R|{3}{U}{B}|Creature - Zombie Ninja|5|4|Ninjutsu {2}{U}{B}$Whenever Fallen Shinobi deals combat damage to a player, that player exiles the top two cards of their library. Until end of turn, you may play those cards without paying their mana cost.|
The First Sliver|Modern Horizons|200|M|{W}{U}{B}{R}{G}|Legendary Creature - Sliver|7|7|Cascade$Sliver spells you cast have cascade.|
Good-Fortune Unicorn|Modern Horizons|201|U|{1}{G}{W}|Creature - Unicorn|2|2|Whenever another creature enters the battlefield under your control, put a +1/+1 counter on that creature.|
Ice-Fang Coatl|Modern Horizons|203|R|{G}{U}|Snow Creature - Snake|1|1|Flash$Flying$When Ice-Fang Coatl enters the battlefield, draw a card.$Ice-Fang Coatl has deathtouch as long as you control at least three other snow permanents.|
+Ingenious Infiltrator|Modern Horizons|204|U|{2}{U}{B}|Creature - Vedalken Ninja|2|3|Ninjutsu {U}{B}$Whenever a Ninja you control deals combat damage to a player, draw a card.|
+Kaya's Guile|Modern Horizons|205|R|{1}{W}{B}|Instant|||Choose two —$• Each opponent sacrifices a creature.$• Exile all cards from each opponent's graveyard.$• Create a 1/1 white and black Spirit creature token with flying.$• You gain 4 life.$Entwine {3}|
+Kess, Dissident Mage|Modern Horizons|206|M|{1}{U}{B}{R}|Legendary Creature - Human Wizard|3|4|Flying$During each of your turns, you may cast an instant or sorcery card from your graveyard. If a card cast this way would be put into your graveyard, exile it instead.|
Lavabelly Sliver|Modern Horizons|207|U|{1}{R}{W}|Creature - Sliver|2|2|Sliver creatures you control have "When this creature enters the battlefield, it deals 1 damage to target player or planeswalker and you gain 1 life."|
Lightning Skelemental|Modern Horizons|208|R|{B}{R}{R}|Creature - Elemental Skeleton|6|1|Trample, haste$Whenever Lightning Skelemental deals combat damage to a player, that player discards two cards.$At the beginning of the end step, sacrifice Lightning Skelemental.|
Munitions Expert|Modern Horizons|209|U|{B}{R}|Creature - Goblin|1|1|Flash$When Munitions Expert enters the battlefield, you may have it deal damage to target creature or planeswalker equal to the number of Goblins you control.|
@@ -35208,12 +35226,18 @@ Nature's Chant|Modern Horizons|210|C|{1}{G/W}|Instant|||Destroy target artifact
Thundering Djinn|Modern Horizons|215|U|{3}{U}{R}|Creature - Djinn|3|4|Flying$Whenever Thundering Djinn attacks, it deals damage to any target equal to the number of cards you've drawn this turn.|
Wrenn and Six|Modern Horizons|217|M|{R}{G}|Legendary Planeswalker - Wrenn|3|+1: Return up to one target land card from your graveyard to your hand.$-1: Wrenn and Six deals 1 damage to any target.$-7: You get an emblem with "Instant and sorcery cards in your graveyard have retrace."|
Altar of Dementia|Modern Horizons|218|R|{2}|Artifact|||Sacrifice a creature: Target player puts a number of cards equal to the sacrificed creature's power from the top of their library into their graveyard.|
+Arcum's Astrolabe|Modern Horizons|220|C|{S}|Snow Artifact|||({S} can be paid with one mana from a snow permanent.)$When Arcum's Astrolabe enters the battlefield, draw a card.${1}, {T}: Add one mana of any color.|
Farmstead Gleaner|Modern Horizons|222|U|{3}|Artifact Creature - Scarecrow|2|2|Farmstead Gleaner doesn't untap during your untap step.${2}, {Q}: Put a +1/+1 counter on Farmstead Gleaner.|
Lesser Masticore|Modern Horizons|225|U|{2}|Artifact Creature - Masticore|2|2|As an additional cost to cast this spell, discard a card.${4}: Lesser Masticore deals 1 damage to target creature.$Persist|
Mox Tantalite|Modern Horizons|226|M||Artifact|||Suspend 3—{0}${T}: Add one mana of any color.|
Scrapyard Recombiner|Modern Horizons|227|R|{3}|Artifact Creature - Construct|0|0|Modular 2${T}, Sacrifice an artifact: Search your library for a Construct card, reveal it, put it into your hand, then shuffle your library.|
Sword of Sinew and Steel|Modern Horizons|228|M|{3}|Artifact - Equipment|||Equipped creature gets +2/+2 and has protection from black and from red.$Whenever equipped creature deals combat damage to a player, destroy up to one target planeswalker and up to one target artifact.$Equip {2}|
Sword of Truth and Justice|Modern Horizons|229|M|{3}|Artifact - Equipment|||Equipped creature gets +2/+2 and has protection from white and from blue.$Whenever equipped creature deals combat damage to a player, put a +1/+1 counter on a creature you control, then proliferate.$Equip {2}|
+Talisman of Conviction|Modern Horizons|230|U|{2}|Artifact|||{T}: Add {C}.${T}: Add {R} or {W}. Talisman of Conviction deals 1 damage to you.|
+Talisman of Creativity|Modern Horizons|231|U|{2}|Artifact|||{T}: Add {C}.${T}: Add {U} or {R}. Talisman of Creativity deals 1 damage to you.|
+Talisman of Curiosity|Modern Horizons|232|U|{2}|Artifact|||{T}: Add {C}.${T}: Add {G} or {U}. Talisman of Curiosity deals 1 damage to you.|
+Talisman of Hierarchy|Modern Horizons|233|U|{2}|Artifact|||{T}: Add {C}.${T}: Add {W} or {B}. Talisman of Hierarchy deals 1 damage to you.|
+Talisman of Resilience|Modern Horizons|234|U|{2}|Artifact|||{T}: Add {C}.${T}: Add {B} or {G}. Talisman of Resilience deals 1 damage to you.|
Fiery Islet|Modern Horizons|238|R||Land|||{T}, Pay 1 life: Add {U} or {R}.${1}, {T}, Sacrifice Fiery Islet: Draw a card.|
Frostwalk Bastion|Modern Horizons|240|U||Snow Land|||{T}: Add {C}.${1}{S}: Until end of turn, Frostwalk Bastion becomes a 2/3 Construct artifact creature. It's still a land.$Whenever Frostwalk Bastion deals combat damage to a creature, tap that creature and it doesn't untap during its controller's next untap step.|
Nurturing Peatland|Modern Horizons|243|R||Land|||{T}, Pay 1 life: Add {B} or {G}.${1}, {T}, Sacrifice Nurturing Peatland: Draw a card.|