Merge branch 'master' into Attractions

# Conflicts:
#	forge-game/src/main/java/forge/game/player/Player.java
This commit is contained in:
Jetz
2024-06-24 20:06:29 -04:00
24 changed files with 133 additions and 161 deletions

View File

@@ -69,7 +69,6 @@ public enum SpellApiToAi {
.put(ApiType.DayTime, DayTimeAi.class)
.put(ApiType.DealDamage, DamageDealAi.class)
.put(ApiType.Debuff, DebuffAi.class)
.put(ApiType.DeclareCombatants, CannotPlayAi.class)
.put(ApiType.DelayedTrigger, DelayedTriggerAi.class)
.put(ApiType.Destroy, DestroyAi.class)
.put(ApiType.DestroyAll, DestroyAllAi.class)

View File

@@ -1,42 +0,0 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.game;
/**
* The Enum GlobalRuleChange.
*/
public enum GlobalRuleChange {
attackerChoosesBlockers ("The attacking player chooses how each creature blocks each combat.");
private final String ruleText;
GlobalRuleChange(String text) {
ruleText = text;
}
public static GlobalRuleChange fromString(String text) {
for (final GlobalRuleChange v : GlobalRuleChange.values()) {
if (v.ruleText.compareToIgnoreCase(text) == 0) {
return v;
}
}
throw new RuntimeException("Element " + text + " not found in GlobalRuleChange enum");
}
}

View File

@@ -198,6 +198,9 @@ public class StaticEffect {
p.removeAdditionalVote(getTimestamp());
p.removeAdditionalOptionalVote(getTimestamp());
p.removeAdditionalVillainousChoices(getTimestamp());
p.removeDeclaresAttackers(getTimestamp());
p.removeDeclaresBlockers(getTimestamp());
}
// modify the affected card

View File

@@ -17,7 +17,6 @@
*/
package forge.game;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
@@ -39,12 +38,8 @@ public class StaticEffects {
// **************** StaticAbility system **************************
private final Map<StaticAbility, StaticEffect> staticEffects = Maps.newHashMap();
//Global rule changes
private final Set<GlobalRuleChange> ruleChanges = EnumSet.noneOf(GlobalRuleChange.class);
public final void clearStaticEffects(final Set<Card> affectedCards) {
ruleChanges.clear();
// remove all static effects
for (final StaticEffect se : staticEffects.values()) {
Iterables.addAll(affectedCards, se.remove());
@@ -52,14 +47,6 @@ public class StaticEffects {
this.staticEffects.clear();
}
public void setGlobalRuleChange(final GlobalRuleChange change) {
this.ruleChanges.add(change);
}
public boolean getGlobalRuleChange(final GlobalRuleChange change) {
return this.ruleChanges.contains(change);
}
/**
* Add a static effect to the list of static effects.
*

View File

@@ -67,7 +67,6 @@ public enum ApiType {
DealDamage (DamageDealEffect.class),
DayTime (DayTimeEffect.class),
Debuff (DebuffEffect.class),
DeclareCombatants (DeclareCombatantsEffect.class),
DelayedTrigger (DelayedTriggerEffect.class),
Destroy (DestroyEffect.class),
DestroyAll (DestroyAllEffect.class),

View File

@@ -1,65 +0,0 @@
package forge.game.ability.effects;
import java.util.List;
import forge.GameCommand;
import forge.game.ability.SpellAbilityEffect;
import forge.game.phase.PhaseHandler;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.util.Lang;
import forge.util.TextUtil;
public class DeclareCombatantsEffect extends SpellAbilityEffect {
@Override
protected String getStackDescription(SpellAbility sa) {
List<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa);
boolean attackers = sa.hasParam("DeclareAttackers");
boolean blockers = sa.hasParam("DeclareBlockers");
String what = Lang.joinHomogenous(
attackers
? "which creatures attack"
: null,
blockers
? "which creatures block this turn and how those creatures block"
: null
);
String duration = "EndOfTurn".equals(sa.getParam("Until")) ? "turn" : "combat";
return TextUtil.concatWithSpace(Lang.joinHomogenous(tgtPlayers),Lang.joinVerb(tgtPlayers, "choose"),what,"this",TextUtil.addSuffix(duration,"."));
}
@Override
public void resolve(SpellAbility sa) {
List<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa);
final boolean attackers = sa.hasParam("DeclareAttackers");
final boolean blockers = sa.hasParam("DeclareBlockers");
String until = sa.getParam("Until");
boolean untilEoT = "EndOfTurn".equals(until);
for (Player p : tgtPlayers) { // Obviously the last player will be applied
final PhaseHandler ph = p.getGame().getPhaseHandler();
if (attackers) ph.setPlayerDeclaresAttackers(p);
if (blockers) ph.setPlayerDeclaresBlockers(p);
GameCommand removeOverrides = new GameCommand() {
private static final long serialVersionUID = -8064627517852651016L;
@Override
public void run() {
if (attackers) ph.setPlayerDeclaresAttackers(null);
if (blockers) ph.setPlayerDeclaresBlockers(null);
}
};
if (untilEoT)
p.getGame().getEndOfTurn().addUntil(removeOverrides);
else
p.getGame().getEndOfCombat().addUntil(removeOverrides);
}
}
}

View File

@@ -44,8 +44,6 @@ public class RestartGameEffect extends SpellAbilityEffect {
trigHandler.suppressMode(TriggerType.Shuffled);
game.getPhaseHandler().restart();
game.getPhaseHandler().setPlayerDeclaresAttackers(null);
game.getPhaseHandler().setPlayerDeclaresBlockers(null);
game.getUntap().clearCommands();
game.getUpkeep().clearCommands();
game.getEndOfCombat().clearCommands();

View File

@@ -42,6 +42,8 @@ import forge.util.CollectionSuppliers;
import forge.util.TextUtil;
import forge.util.maps.HashMapOfLists;
import forge.util.maps.MapOfLists;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.time.StopWatch;
import java.util.*;
@@ -81,9 +83,6 @@ public class PhaseHandler implements java.io.Serializable {
private transient Combat combat = null;
private boolean bRepeatCleanup = false;
private transient Player playerDeclaresBlockers = null;
private transient Player playerDeclaresAttackers = null;
/** The need to next phase. */
private boolean givePriorityToPlayer = false;
@@ -524,9 +523,7 @@ public class PhaseHandler implements java.io.Serializable {
}
private void declareAttackersTurnBasedAction() {
final Player whoDeclares = playerDeclaresAttackers == null || playerDeclaresAttackers.hasLost()
? playerTurn
: playerDeclaresAttackers;
final Player whoDeclares = ObjectUtils.firstNonNull(playerTurn.getDeclaresAttackers(), playerTurn);
if (CombatUtil.canAttack(playerTurn)) {
boolean success = false;
@@ -654,10 +651,7 @@ public class PhaseHandler implements java.io.Serializable {
do {
p = game.getNextPlayerAfter(p);
// Apply Odric's effect here
Player whoDeclaresBlockers = playerDeclaresBlockers == null || playerDeclaresBlockers.hasLost() ? p : playerDeclaresBlockers;
if (game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.attackerChoosesBlockers)) {
whoDeclaresBlockers = combat.getAttackingPlayer();
}
Player whoDeclaresBlockers = ObjectUtils.firstNonNull(p.getDeclaresBlockers(), p);
if (combat.isPlayerAttacked(p)) {
if (CombatUtil.canBlock(p, combat)) {
// Replacement effects (for Camouflage)
@@ -1236,14 +1230,6 @@ public class PhaseHandler implements java.io.Serializable {
givePriorityToPlayer = true;
}
public final void setPlayerDeclaresAttackers(Player player) {
playerDeclaresAttackers = player;
}
public final void setPlayerDeclaresBlockers(Player player) {
playerDeclaresBlockers = player;
}
public void endCombat() {
game.getEndOfCombat().executeUntil();
game.getEndOfCombat().executeUntilEndOfPhase(playerTurn);

View File

@@ -196,6 +196,9 @@ public class Player extends GameEntity implements Comparable<Player> {
private SortedSet<Long> controlVotes = Sets.newTreeSet();
private Map<Long, Integer> additionalVillainousChoices = Maps.newHashMap();
private NavigableMap<Long, Player> declaresAttackers = Maps.newTreeMap();
private NavigableMap<Long, Player> declaresBlockers = Maps.newTreeMap();
private final AchievementTracker achievementTracker = new AchievementTracker();
private final PlayerView view;
@@ -3894,4 +3897,30 @@ public class Player extends GameEntity implements Comparable<Player> {
this.visitAttractions(total);
}
public void addDeclaresAttackers(long ts, Player p) {
this.declaresAttackers.put(ts, p);
}
public void removeDeclaresAttackers(long ts) {
this.declaresAttackers.remove(ts);
}
public Player getDeclaresAttackers() {
Map.Entry<Long, Player> e = declaresAttackers.lastEntry();
return e == null ? null : e.getValue();
}
public void addDeclaresBlockers(long ts, Player p) {
this.declaresBlockers.put(ts, p);
}
public void removeDeclaresBlockers(long ts) {
this.declaresBlockers.remove(ts);
}
public Player getDeclaresBlockers() {
Map.Entry<Long, Player> e = declaresBlockers.lastEntry();
return e == null ? null : e.getValue();
}
}

View File

@@ -175,6 +175,10 @@ public class PlayerProperty {
if (!source.getDamageHistory().hasAttackedThisTurn(player)) {
return false;
}
} else if (property.equals("Attacking")) {
if (game.getCombat() == null || !player.equals(game.getCombat().getAttackingPlayer())) {
return false;
}
} else if (property.equals("Defending")) {
if (game.getCombat() == null || !game.getCombat().getAttackersAndDefenders().values().contains(player)) {
return false;

View File

@@ -165,7 +165,8 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
if (hasParam("AddHiddenKeyword") || hasParam("MayPlay")
|| hasParam("IgnoreEffectCost") || hasParam("Goad") || hasParam("CanBlockAny") || hasParam("CanBlockAmount")
|| hasParam("AdjustLandPlays") || hasParam("ControlVote") || hasParam("AdditionalVote") || hasParam("AdditionalOptionalVote")) {
|| hasParam("AdjustLandPlays") || hasParam("ControlVote") || hasParam("AdditionalVote") || hasParam("AdditionalOptionalVote")
|| hasParam("DeclaresAttackers") || hasParam("DeclaresBlockers")) {
layers.add(StaticAbilityLayer.RULES);
}

View File

@@ -26,7 +26,6 @@ import com.google.common.collect.Maps;
import forge.GameCommand;
import forge.card.*;
import forge.game.Game;
import forge.game.GlobalRuleChange;
import forge.game.StaticEffect;
import forge.game.StaticEffects;
import forge.game.ability.AbilityUtils;
@@ -142,11 +141,6 @@ public final class StaticAbilityContinuous {
boolean mayPlayGrantZonePermissions = true;
Integer mayPlayLimit = null;
//Global rules changes
if (layer == StaticAbilityLayer.RULES && params.containsKey("GlobalRule")) {
effects.setGlobalRuleChange(GlobalRuleChange.fromString(params.get("GlobalRule")));
}
if (layer == StaticAbilityLayer.SETPT || layer == StaticAbilityLayer.CHARACTERISTIC) {
if (params.containsKey("SetPower")) {
setP = params.get("SetPower");
@@ -599,6 +593,17 @@ public final class StaticAbilityContinuous {
int add = AbilityUtils.calculateAmount(hostCard, mhs, stAb);
p.addAdditionalVillainousChoices(se.getTimestamp(), add);
}
if (params.containsKey("DeclaresAttackers")) {
PlayerCollection players = AbilityUtils.getDefinedPlayers(hostCard, params.get("DeclaresAttackers"), stAb);
if (!players.isEmpty())
p.addDeclaresAttackers(se.getTimestamp(), players.getFirst());
}
if (params.containsKey("DeclaresBlockers")) {
PlayerCollection players = AbilityUtils.getDefinedPlayers(hostCard, params.get("DeclaresBlockers"), stAb);
if (!players.isEmpty())
p.addDeclaresBlockers(se.getTimestamp(), players.getFirst());
}
}
}

View File

@@ -32,7 +32,7 @@
<objectgroup id="4" name="Objects">
<object id="66" template="../../obj/door_up.tx" x="543" y="638">
<properties>
<property name="teleport" value="maps/map/cave/cave_kobold.tmx"/>
<property name="teleport" value="../common/maps/map/cave/cave_kobold.tmx"/>
<property name="teleportObjectId" value="67"/>
</properties>
</object>

View File

@@ -36,6 +36,7 @@
<objectgroup id="4" name="Objects">
<object id="38" template="../../obj/entry_up.tx" x="450.334" y="239.188">
<properties>
<property name="direction" value="down"/>
<property name="teleport" value="../common/maps/map/fort/fort_colorless_7_multilevel4.tmx"/>
<property name="teleportObjectId" value="38"/>
</properties>

View File

@@ -6,5 +6,6 @@ SVar:MustBlock:DB$ ChooseCard | Choices$ Creature | Amount$ X | MinAmount$ 0 | C
SVar:DBEffect:DB$ Effect | StaticAbilities$ StaticBlock
SVar:StaticBlock:Mode$ MustBlock | ValidCreature$ Card.ChosenCardStrict | Description$ The chosen creatures block this turn if able.
SVar:X:Count$Valid Creature
SVar:ChooseBlock:DB$ DeclareCombatants | DeclareBlockers$ True | Until$ EndOfTurn | SpellDescription$ 15—20 VERT You choose which creatures block this turn and how those creatures block.
SVar:ChooseBlock:DB$ Effect | StaticAbilities$ DeclareCombatants | SpellDescription$ 15—20 VERT You choose which creatures block this turn and how those creatures block.
SVar:DeclareCombatants:Mode$ Continuous | Affected$ Player | DeclaresBlockers$ You | Description$ You choose which creatures block this combat and how those creatures block.
Oracle:Cast this spell only before combat or during combat before blockers are declared.\nRoll two d20 and ignore the lower roll.\n1—14 | Choose any number of creatures. They block this turn if able.\n15—20 | You choose which creatures block this turn and how those creatures block.

View File

@@ -5,9 +5,9 @@ PT:3/3
T:Mode$ Attacks | ValidCard$ Creature.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever a creature you control attacks, defending player loses 1 life and you gain 1 life.
SVar:TrigLoseLife:DB$ LoseLife | Defined$ TriggeredDefendingPlayer | LifeAmount$ 1 | SubAbility$ DBGainLife
SVar:DBGainLife:DB$ GainLife | LifeAmount$ 1
A:AB$ Effect | Cost$ 3 RW RW | StaticAbilities$ STBlock | SubAbility$ DBDeclareCombatants | SpellDescription$ Creatures your opponents control block this turn if able, and you choose how those creatures block.
A:AB$ Effect | Cost$ 3 RW RW | StaticAbilities$ STBlock,DeclareCombatants | SpellDescription$ Creatures your opponents control block this turn if able, and you choose how those creatures block.
SVar:STBlock:Mode$ MustBlock | ValidCreature$ Creature.OppCtrl | Description$ Creatures your opponents control block this turn if able, and you choose how those creatures block.
SVar:DBDeclareCombatants:DB$ DeclareCombatants | DeclareBlockers$ True | Until$ EndOfTurn
SVar:DeclareCombatants:Mode$ Continuous | Affected$ Player.Opponent | DeclaresBlockers$ You | Description$ You choose how those creatures block.
SVar:HasAttackEffect:TRUE
DeckHas:Ability$LifeGain
Oracle:Whenever a creature you control attacks, defending player loses 1 life and you gain 1 life.\n{3}{R/W}{R/W}: Creatures your opponents control block this turn if able, and you choose how those creatures block.

View File

@@ -2,7 +2,7 @@ Name:Invasion Plans
ManaCost:2 R
Types:Enchantment
S:Mode$ MustBlock | ValidCreature$ Creature | Description$ All creatures block each combat if able.
S:Mode$ Continuous | GlobalRule$ The attacking player chooses how each creature blocks each combat. | Description$ The attacking player chooses how each creature blocks each combat.
S:Mode$ Continuous | Affected$ Player | DeclaresBlockers$ AttackingPlayer | Description$ The attacking player chooses how each creature blocks each combat.
SVar:NonStackingEffect:True
AI:RemoveDeck:Random
Oracle:All creatures block each combat if able.\nThe attacking player chooses how each creature blocks each combat.

View File

@@ -1,6 +1,7 @@
Name:Master Warcraft
ManaCost:2 RW RW
Types:Instant
A:SP$ DeclareCombatants | DeclareAttackers$ True | DeclareBlockers$ True | ActivationPhases$ Upkeep->BeginCombat | ActivationFirstCombat$ True | Until$ EndOfTurn | SpellDescription$ Cast this spell only before attackers are declared. You choose which creatures attack this turn. You choose which creatures block this turn and how those creatures block.
A:SP$ Effect | StaticAbilities$ DeclareCombatants | ActivationPhases$ Upkeep->BeginCombat | ActivationFirstCombat$ True | SpellDescription$ Cast this spell only before attackers are declared. You choose which creatures attack this turn. You choose which creatures block this turn and how those creatures block.
SVar:DeclareCombatants:Mode$ Continuous | Affected$ Player | DeclaresAttackers$ You | DeclaresBlockers$ You | Description$ You choose which creatures attack this turn. You choose which creatures block this turn and how those creatures block.
AI:RemoveDeck:All
Oracle:Cast this spell only before attackers are declared.\nYou choose which creatures attack this turn.\nYou choose which creatures block this turn and how those creatures block.

View File

@@ -1,8 +1,8 @@
Name:Melee
ManaCost:4 R
Types:Instant
A:SP$ DeclareCombatants | DeclareBlockers$ True | PlayerTurn$ True | ActivationPhases$ BeginCombat->Declare Attackers | SubAbility$ DBEffect | SpellDescription$ Cast this spell only during your turn and only during combat before blockers are declared. You choose which creatures block this combat and how those creatures block. Whenever a creature attacks and isn't blocked this combat, untap it and remove it from combat.
SVar:DBEffect:DB$ Effect | Triggers$ TrigAttack | Duration$ UntilEndOfCombat
A:SP$ Effect | StaticAbilities$ DeclareCombatants | Triggers$ TrigAttack | Duration$ UntilEndOfCombat | PlayerTurn$ True | ActivationPhases$ BeginCombat->Declare Attackers | SpellDescription$ Cast this spell only during your turn and only during combat before blockers are declared. You choose which creatures block this combat and how those creatures block. Whenever a creature attacks and isn't blocked this combat, untap it and remove it from combat.
SVar:DeclareCombatants:Mode$ Continuous | Affected$ Player | DeclaresBlockers$ You | Description$ You choose which creatures block this combat and how those creatures block.
SVar:TrigAttack:Mode$ AttackerUnblocked | ValidCard$ Creature | Execute$ TrigUntap | TriggerZones$ Command | TriggerDescription$ Whenever a creature attacks and isn't blocked this combat, untap it and remove it from combat.
SVar:TrigUntap:DB$ Untap | Defined$ TriggeredAttackerLKICopy | SubAbility$ RemCombat
SVar:RemCombat:DB$ RemoveFromCombat | Defined$ TriggeredAttackerLKICopy

View File

@@ -4,6 +4,7 @@ Types:Legendary Creature Human Soldier
PT:3/4
K:First Strike
T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | IsPresent$ Creature.attacking+Other | PresentCompare$ GE3 | NoResolvingCheck$ True | Execute$ TrigOdricEffect | TriggerDescription$ Whenever CARDNAME and at least three other creatures attack, you choose which creatures block this combat and how those creatures block.
SVar:TrigOdricEffect:DB$ DeclareCombatants | DeclareBlockers$ True
SVar:TrigOdricEffect:DB$ Effect | StaticAbilities$ DeclareCombatants | Duration$ UntilEndOfCombat
SVar:DeclareCombatants:Mode$ Continuous | Affected$ Player | DeclaresBlockers$ You | Description$ You choose which creatures block this combat and how those creatures block.
AI:RemoveDeck:All
Oracle:First strike (This creature deals combat damage before creatures without first strike.)\nWhenever Odric, Master Tactician and at least three other creatures attack, you choose which creatures block this combat and how those creatures block.

View File

@@ -1,4 +1,5 @@
Name:Desmond Miles
ManaCost:1 B
Types:Legendary Creature Human Assassin
PT:1/3
K:Menace

View File

@@ -1,4 +1,5 @@
Name:Evie Frye
ManaCost:1 B
Types:Legendary Creature Human Assassin
PT:2/1
K:Partner:Jacob Frye

View File

@@ -4,7 +4,7 @@ Types:Enchantment Saga
K:Chapter:3:DBToken,DBVote,DBVote
SVar:DBToken:DB$ Token | TokenAmount$ OneEach | TokenScript$ w_1_1_human_soldier
SVar:OneEach:PlayerCountOpponents$Amount
SVar:DBVote:DB$ Vote | Defined$ Player | Secretly$ True | VoteCard$ Creature | VoteMessage$ for a creature | UpTo$ True | VoteSubAbility$ DBDestroyAll | SubAbility$ DBBranch | SpellDescription$ Each player secretly votes for up to one creature, then those votes are revealed. If no creature got votes, each player draws a card. Otherwise, destroy each creature with the most votes or tied for most votes.
SVar:DBVote:DB$ Vote | Defined$ Player | Secretly$ True | VoteCard$ Creature | VoteMessage$ for a creature | UpTo$ True | VoteSubAbility$ DBDestroyAll | SpellDescription$ Each player secretly votes for up to one creature, then those votes are revealed. If no creature got votes, each player draws a card. Otherwise, destroy each creature with the most votes or tied for most votes.
SVar:DBDestroyAll:DB$ DestroyAll | ValidCards$ Creature.IsRemembered | SubAbility$ DBDraw
SVar:DBDraw:DB$ Draw | Defined$ Player | NumCards$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ0
SVar:X:Remembered$Amount

View File

@@ -3,6 +3,12 @@ Code=MH3
Date=2024-06-07
Name=Modern Horizons 3
Type=Draft
Booster=6 Common, 3 Uncommon, 1 RareMythic, 1 fromSheet("MH3 new to modern"), 1 Land, 1 Any, 1 Any+
# This isn't the right ratios yet. But it's a start
ChanceReplaceCommonWith=.0156F fromsheet("MH3 special guests")
BoosterBox=36
BoosterCovers=3
ChaosDraftThemes=MASTERS_SET
ScryfallCode=MH3
[cards]
@@ -536,6 +542,62 @@ ScryfallCode=MH3
495 R Powerbalance @Liiga Smilshkalne
496 R Flusterstorm @Andrew Mar
[new to modern]
1 Angel of the Ruins|MH3
1 Decree of Justice|MH3
1 Distinguished Conjurer|MH3
1 Orim's Chant|MH3
1 Recruiter of the Guard|MH3
1 Sevinne's Reclamation|MH3
1 Deep Analysis|MH3
1 Estrid's Invocation|MH3
1 Kappa Cannoneer|MH3
1 Reef Worm|MH3
1 Shrieking Drake|MH3
1 Buried Alive|MH3
1 K'rrik, Son of Yawgmoth|MH3
1 Nadier's Nightblade|MH3
1 Ophiomancer|MH3
1 Toxic Deluge|MH3
1 Victimize|MH3
1 Cursed Mirror|MH3
1 Fledgling Dragon|MH3
1 Laelia, the Blade Reforged|MH3
1 Meltdown|MH3
1 Meteoric Mace|MH3
1 Annoyed Altisaur|MH3
1 Branching Evolution|MH3
1 Priest of Titania|MH3
1 Sylvan Safekeeper|MH3
1 Wirewood Symbiote|MH3
1 Breya, Etherium Shaper|MH3
1 Kaalia of the Vast|MH3
1 Emerald Medallion|MH3
1 Jet Medallion|MH3
1 Junk Diver|MH3
1 Pearl Medallion|MH3
1 Ruby Medallion|MH3
1 Sapphire Medallion|MH3
1 Urza's Incubator|MH3
1 Worn Powerstone|MH3
1 Barbarian Ring|MH3
1 Cephalid Coliseum|MH3
1 Deserted Temple|MH3
1 Nesting Grounds|MH3
1 Phyrexian Tower|MH3
[special guests]
1 Thought-Knot Seer|SPG
1 Prismatic Ending|SPG
1 Dismember|SPG
1 Persist|SPG
1 Expressive Iteration|SPG
1 Solitude|SPG
1 Subtlety|SPG
1 Grief|SPG
1 Fury|SPG
1 Endurance|SPG
[tokens]
b_0_0_phyrexian_germ
b_0_0_zombie_army