diff --git a/Mage.Sets/src/mage/cards/a/AnalyzeThePollen.java b/Mage.Sets/src/mage/cards/a/AnalyzeThePollen.java
new file mode 100644
index 00000000000..bf651e6d329
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/AnalyzeThePollen.java
@@ -0,0 +1,44 @@
+package mage.cards.a;
+
+import mage.abilities.condition.common.CollectedEvidenceCondition;
+import mage.abilities.decorator.ConditionalOneShotEffect;
+import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
+import mage.abilities.keyword.CollectEvidenceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetCardInLibrary;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class AnalyzeThePollen extends CardImpl {
+
+ public AnalyzeThePollen(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}");
+
+ // As an additional cost to cast this spell, you may collect evidence 8.
+ this.addAbility(new CollectEvidenceAbility(8));
+
+ // Search your library for a basic land card. If evidence was collected, instead search your library for a creature or land card. Reveal that card, put it into your hand, then shuffle.
+ this.getSpellAbility().addEffect(new ConditionalOneShotEffect(
+ new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_CREATURE_OR_LAND), true),
+ new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true),
+ CollectedEvidenceCondition.instance, "search your library for a basic land card. " +
+ "If evidence was collected, instead search your library for a creature or land card. " +
+ "Reveal that card, put it into your hand, then shuffle"
+ ));
+ }
+
+ private AnalyzeThePollen(final AnalyzeThePollen card) {
+ super(card);
+ }
+
+ @Override
+ public AnalyzeThePollen copy() {
+ return new AnalyzeThePollen(this);
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/a/AxebaneFerox.java b/Mage.Sets/src/mage/cards/a/AxebaneFerox.java
new file mode 100644
index 00000000000..8af2f813505
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/AxebaneFerox.java
@@ -0,0 +1,45 @@
+package mage.cards.a;
+
+import mage.MageInt;
+import mage.abilities.costs.common.CollectEvidenceCost;
+import mage.abilities.keyword.DeathtouchAbility;
+import mage.abilities.keyword.HasteAbility;
+import mage.abilities.keyword.WardAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class AxebaneFerox extends CardImpl {
+
+ public AxebaneFerox(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}");
+
+ this.subtype.add(SubType.BEAST);
+ this.power = new MageInt(4);
+ this.toughness = new MageInt(4);
+
+ // Deathtouch
+ this.addAbility(DeathtouchAbility.getInstance());
+
+ // Haste
+ this.addAbility(HasteAbility.getInstance());
+
+ // Ward--Collect evidence 4.
+ this.addAbility(new WardAbility(new CollectEvidenceCost(4)));
+ }
+
+ private AxebaneFerox(final AxebaneFerox card) {
+ super(card);
+ }
+
+ @Override
+ public AxebaneFerox copy() {
+ return new AxebaneFerox(this);
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/c/CrimestopperSprite.java b/Mage.Sets/src/mage/cards/c/CrimestopperSprite.java
new file mode 100644
index 00000000000..b43455b924a
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/CrimestopperSprite.java
@@ -0,0 +1,58 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.condition.common.CollectedEvidenceCondition;
+import mage.abilities.decorator.ConditionalOneShotEffect;
+import mage.abilities.effects.common.TapTargetEffect;
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.abilities.keyword.CollectEvidenceAbility;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class CrimestopperSprite extends CardImpl {
+
+ public CrimestopperSprite(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
+
+ this.subtype.add(SubType.FAERIE);
+ this.subtype.add(SubType.DETECTIVE);
+ this.power = new MageInt(2);
+ this.toughness = new MageInt(2);
+
+ // As an additional cost to cast this spell, you may collect evidence 6.
+ this.addAbility(new CollectEvidenceAbility(6));
+
+ // Flying
+ this.addAbility(FlyingAbility.getInstance());
+
+ // When Crimestopper Sprite enters the battlefield, tap target creature. If evidence was collected, put a stun counter on it.
+ Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect());
+ ability.addTarget(new TargetCreaturePermanent());
+ ability.addEffect(new ConditionalOneShotEffect(
+ new AddCountersTargetEffect(CounterType.STUN.createInstance()),
+ CollectedEvidenceCondition.instance, "If evidence was collected, put a stun counter on it"
+ ));
+ this.addAbility(ability);
+ }
+
+ private CrimestopperSprite(final CrimestopperSprite card) {
+ super(card);
+ }
+
+ @Override
+ public CrimestopperSprite copy() {
+ return new CrimestopperSprite(this);
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/e/EvidenceExaminer.java b/Mage.Sets/src/mage/cards/e/EvidenceExaminer.java
new file mode 100644
index 00000000000..218b53802fb
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/e/EvidenceExaminer.java
@@ -0,0 +1,48 @@
+package mage.cards.e;
+
+import mage.MageInt;
+import mage.abilities.common.BeginningOfCombatTriggeredAbility;
+import mage.abilities.common.CollectEvidenceTriggeredAbility;
+import mage.abilities.costs.common.CollectEvidenceCost;
+import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.keyword.InvestigateEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class EvidenceExaminer extends CardImpl {
+
+ public EvidenceExaminer(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}");
+
+ this.subtype.add(SubType.MERFOLK);
+ this.subtype.add(SubType.DETECTIVE);
+ this.power = new MageInt(2);
+ this.toughness = new MageInt(2);
+
+ // At the beginning of combat on your turn, you may collect evidence 4.
+ this.addAbility(new BeginningOfCombatTriggeredAbility(
+ new DoIfCostPaid(null, new CollectEvidenceCost(4)),
+ TargetController.YOU, false
+ ));
+
+ // Whenever you collect evidence, investigate.
+ this.addAbility(new CollectEvidenceTriggeredAbility(new InvestigateEffect(), false));
+ }
+
+ private EvidenceExaminer(final EvidenceExaminer card) {
+ super(card);
+ }
+
+ @Override
+ public EvidenceExaminer copy() {
+ return new EvidenceExaminer(this);
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/f/ForensicResearcher.java b/Mage.Sets/src/mage/cards/f/ForensicResearcher.java
new file mode 100644
index 00000000000..6f5be4559b9
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/f/ForensicResearcher.java
@@ -0,0 +1,52 @@
+package mage.cards.f;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.CollectEvidenceCost;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.effects.common.TapTargetEffect;
+import mage.abilities.effects.common.UntapTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.StaticFilters;
+import mage.target.TargetPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ForensicResearcher extends CardImpl {
+
+ public ForensicResearcher(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
+
+ this.subtype.add(SubType.MERFOLK);
+ this.subtype.add(SubType.DETECTIVE);
+ this.power = new MageInt(1);
+ this.toughness = new MageInt(3);
+
+ // {T}: Untap another target permanent you control.
+ Ability ability = new SimpleActivatedAbility(new UntapTargetEffect(), new TapSourceCost());
+ ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_ANOTHER_TARGET_PERMANENT));
+ this.addAbility(ability);
+
+ // {T}, Collect evidence 3: Tap target creature you don't control.
+ ability = new SimpleActivatedAbility(new TapTargetEffect(), new TapSourceCost());
+ ability.addCost(new CollectEvidenceCost(3));
+ ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL));
+ this.addAbility(ability);
+ }
+
+ private ForensicResearcher(final ForensicResearcher card) {
+ super(card);
+ }
+
+ @Override
+ public ForensicResearcher copy() {
+ return new ForensicResearcher(this);
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/i/IzoniCenterOfTheWeb.java b/Mage.Sets/src/mage/cards/i/IzoniCenterOfTheWeb.java
new file mode 100644
index 00000000000..87bf630d97c
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/i/IzoniCenterOfTheWeb.java
@@ -0,0 +1,66 @@
+package mage.cards.i;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.CollectEvidenceCost;
+import mage.abilities.costs.common.SacrificeTargetCost;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.effects.keyword.SurveilEffect;
+import mage.abilities.keyword.MenaceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.filter.StaticFilters;
+import mage.game.permanent.token.IzoniSpiderToken;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class IzoniCenterOfTheWeb extends CardImpl {
+
+ public IzoniCenterOfTheWeb(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{G}");
+
+ this.supertype.add(SuperType.LEGENDARY);
+ this.subtype.add(SubType.ELF);
+ this.subtype.add(SubType.DETECTIVE);
+ this.power = new MageInt(5);
+ this.toughness = new MageInt(4);
+
+ // Menace
+ this.addAbility(new MenaceAbility());
+
+ // Whenever Izoni, Center of the Web enters the battlefield or attacks, you may collect evidence 4. If you do, create two 2/1 black and green Spider creature tokens with menace and reach.
+ this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new DoIfCostPaid(
+ new CreateTokenEffect(new IzoniSpiderToken(), 2),
+ new CollectEvidenceCost(4)
+ )));
+
+ // Sacrifice four tokens: Surveil 2, then draw two cards. You gain 2 life.
+ Ability ability = new SimpleActivatedAbility(
+ new SurveilEffect(2, false),
+ new SacrificeTargetCost(4, StaticFilters.FILTER_PERMANENT_TOKEN)
+ );
+ ability.addEffect(new DrawCardSourceControllerEffect(2).concatBy(", then"));
+ ability.addEffect(new GainLifeEffect(2));
+ this.addAbility(ability);
+ }
+
+ private IzoniCenterOfTheWeb(final IzoniCenterOfTheWeb card) {
+ super(card);
+ }
+
+ @Override
+ public IzoniCenterOfTheWeb copy() {
+ return new IzoniCenterOfTheWeb(this);
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/s/SampleCollector.java b/Mage.Sets/src/mage/cards/s/SampleCollector.java
new file mode 100644
index 00000000000..6c84497632e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SampleCollector.java
@@ -0,0 +1,49 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
+import mage.abilities.costs.common.CollectEvidenceCost;
+import mage.abilities.effects.common.DoWhenCostPaid;
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.target.common.TargetControlledCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SampleCollector extends CardImpl {
+
+ public SampleCollector(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
+
+ this.subtype.add(SubType.TROLL);
+ this.subtype.add(SubType.DETECTIVE);
+ this.power = new MageInt(2);
+ this.toughness = new MageInt(3);
+
+ // Whenever Sample Collector attacks, you may collect evidence 3. When you do, put a +1/+1 counter on target creature you control.
+ ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+ new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false
+ );
+ ability.addTarget(new TargetControlledCreaturePermanent());
+ this.addAbility(new AttacksTriggeredAbility(new DoWhenCostPaid(
+ ability, new CollectEvidenceCost(3), "Collect evidence 3?"
+ )));
+ }
+
+ private SampleCollector(final SampleCollector card) {
+ super(card);
+ }
+
+ @Override
+ public SampleCollector copy() {
+ return new SampleCollector(this);
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/s/SurveillanceMonitor.java b/Mage.Sets/src/mage/cards/s/SurveillanceMonitor.java
new file mode 100644
index 00000000000..28ee7e57891
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SurveillanceMonitor.java
@@ -0,0 +1,49 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.common.CollectEvidenceTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.costs.common.CollectEvidenceCost;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.DoIfCostPaid;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.game.permanent.token.ThopterColorlessToken;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SurveillanceMonitor extends CardImpl {
+
+ public SurveillanceMonitor(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}");
+
+ this.subtype.add(SubType.VEDALKEN);
+ this.subtype.add(SubType.DETECTIVE);
+ this.power = new MageInt(3);
+ this.toughness = new MageInt(3);
+
+ // When Surveillance Monitor enters the battlefield, you may collect evidence 4.
+ this.addAbility(new EntersBattlefieldTriggeredAbility(
+ new DoIfCostPaid(null, new CollectEvidenceCost(4))
+ ));
+
+ // Whenever you collect evidence, create a 1/1 colorless Thopter artifact creature token with flying.
+ this.addAbility(new CollectEvidenceTriggeredAbility(
+ new CreateTokenEffect(new ThopterColorlessToken()), false
+ ));
+ }
+
+ private SurveillanceMonitor(final SurveillanceMonitor card) {
+ super(card);
+ }
+
+ @Override
+ public SurveillanceMonitor copy() {
+ return new SurveillanceMonitor(this);
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/u/UrgentNecropsy.java b/Mage.Sets/src/mage/cards/u/UrgentNecropsy.java
new file mode 100644
index 00000000000..7358884883b
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/u/UrgentNecropsy.java
@@ -0,0 +1,76 @@
+package mage.cards.u;
+
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.costs.CostAdjuster;
+import mage.abilities.costs.common.CollectEvidenceCost;
+import mage.abilities.effects.common.DestroyTargetEffect;
+import mage.abilities.effects.common.InfoEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.game.Game;
+import mage.target.Target;
+import mage.target.common.TargetArtifactPermanent;
+import mage.target.common.TargetCreaturePermanent;
+import mage.target.common.TargetEnchantmentPermanent;
+import mage.target.common.TargetPlaneswalkerPermanent;
+import mage.target.targetpointer.EachTargetPointer;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class UrgentNecropsy extends CardImpl {
+
+ public UrgentNecropsy(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}{G}");
+
+ // As an additional cost to cast this spell, collect evidence X, where X is the total mana value of the permanents this spell targets.
+ this.getSpellAbility().addEffect(new InfoEffect(
+ "As an additional cost to cast this spell, collect evidence X, " +
+ "where X is the total mana value of the permanents this spell targets."
+ ));
+ this.getSpellAbility().setCostAdjuster(UrgentNecropsyAdjuster.instance);
+
+ // Destroy up to one target artifact, up to one target creature, up to one target enchantment, and up to one target planeswalker.
+ this.getSpellAbility().addEffect(new DestroyTargetEffect(
+ "
Destroy up to one target artifact, up to one target creature, " +
+ "up to one target enchantment, and up to one target planeswalker"
+ ).setTargetPointer(new EachTargetPointer()));
+ this.getSpellAbility().addTarget(new TargetArtifactPermanent(0, 1));
+ this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 1));
+ this.getSpellAbility().addTarget(new TargetEnchantmentPermanent(0, 1));
+ this.getSpellAbility().addTarget(new TargetPlaneswalkerPermanent(0, 1));
+ }
+
+ private UrgentNecropsy(final UrgentNecropsy card) {
+ super(card);
+ }
+
+ @Override
+ public UrgentNecropsy copy() {
+ return new UrgentNecropsy(this);
+ }
+}
+
+enum UrgentNecropsyAdjuster implements CostAdjuster {
+ instance;
+
+ @Override
+ public void adjustCosts(Ability ability, Game game) {
+ int xValue = ability
+ .getTargets()
+ .stream()
+ .map(Target::getTargets)
+ .flatMap(Collection::stream)
+ .map(game::getPermanent)
+ .filter(Objects::nonNull)
+ .mapToInt(MageObject::getManaValue)
+ .sum();
+ ability.addCost(new CollectEvidenceCost(xValue));
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/v/VituGhaziInspector.java b/Mage.Sets/src/mage/cards/v/VituGhaziInspector.java
new file mode 100644
index 00000000000..b24c87c26d7
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/v/VituGhaziInspector.java
@@ -0,0 +1,59 @@
+package mage.cards.v;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.condition.common.CollectedEvidenceCondition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.abilities.keyword.CollectEvidenceAbility;
+import mage.abilities.keyword.ReachAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class VituGhaziInspector extends CardImpl {
+
+ public VituGhaziInspector(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}");
+
+ this.subtype.add(SubType.ELF);
+ this.subtype.add(SubType.DETECTIVE);
+ this.power = new MageInt(1);
+ this.toughness = new MageInt(3);
+
+ // As an additional cost to cast this spell, you may collect evidence 6.
+ this.addAbility(new CollectEvidenceAbility(6));
+
+ // Reach
+ this.addAbility(ReachAbility.getInstance());
+
+ // When Vitu-Ghazi Inspector enters the battlefield, if evidence was collected, put a +1/+1 counter on target creature and you gain 2 life.
+ Ability ability = new ConditionalInterveningIfTriggeredAbility(
+ new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())),
+ CollectedEvidenceCondition.instance, "When {this} enters the battlefield, if evidence was " +
+ "collected, put a +1/+1 counter on target creature and you gain 2 life."
+ );
+ ability.addEffect(new GainLifeEffect(2));
+ ability.addTarget(new TargetCreaturePermanent());
+ this.addAbility(ability);
+ }
+
+ private VituGhaziInspector(final VituGhaziInspector card) {
+ super(card);
+ }
+
+ @Override
+ public VituGhaziInspector copy() {
+ return new VituGhaziInspector(this);
+ }
+}
diff --git a/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java b/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java
index 175ba3c7492..3cf00e2bbc2 100644
--- a/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java
+++ b/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java
@@ -33,11 +33,13 @@ public final class MurdersAtKarlovManor extends ExpansionSet {
cards.add(new SetCardInfo("Airtight Alibi", 149, Rarity.COMMON, mage.cards.a.AirtightAlibi.class));
cards.add(new SetCardInfo("Alley Assailant", 76, Rarity.COMMON, mage.cards.a.AlleyAssailant.class));
cards.add(new SetCardInfo("Alquist Proft, Master Sleuth", 185, Rarity.MYTHIC, mage.cards.a.AlquistProftMasterSleuth.class));
+ cards.add(new SetCardInfo("Analyze the Pollen", 150, Rarity.RARE, mage.cards.a.AnalyzeThePollen.class));
cards.add(new SetCardInfo("Anzrag, the Quake-Mole", 186, Rarity.MYTHIC, mage.cards.a.AnzragTheQuakeMole.class));
cards.add(new SetCardInfo("Assassin's Trophy", 187, Rarity.RARE, mage.cards.a.AssassinsTrophy.class));
cards.add(new SetCardInfo("Audience with Trostani", 152, Rarity.RARE, mage.cards.a.AudienceWithTrostani.class));
cards.add(new SetCardInfo("Aurelia, the Law Above", 188, Rarity.RARE, mage.cards.a.AureliaTheLawAbove.class));
cards.add(new SetCardInfo("Auspicious Arrival", 5, Rarity.COMMON, mage.cards.a.AuspiciousArrival.class));
+ cards.add(new SetCardInfo("Axebane Ferox", 153, Rarity.RARE, mage.cards.a.AxebaneFerox.class));
cards.add(new SetCardInfo("Barbed Servitor", 77, Rarity.RARE, mage.cards.b.BarbedServitor.class));
cards.add(new SetCardInfo("Basilica Stalker", 78, Rarity.COMMON, mage.cards.b.BasilicaStalker.class));
cards.add(new SetCardInfo("Benthic Criminologists", 40, Rarity.COMMON, mage.cards.b.BenthicCriminologists.class));
@@ -54,6 +56,7 @@ public final class MurdersAtKarlovManor extends ExpansionSet {
cards.add(new SetCardInfo("Concealed Weapon", 117, Rarity.UNCOMMON, mage.cards.c.ConcealedWeapon.class));
cards.add(new SetCardInfo("Convenient Target", 119, Rarity.UNCOMMON, mage.cards.c.ConvenientTarget.class));
cards.add(new SetCardInfo("Crime Novelist", 121, Rarity.UNCOMMON, mage.cards.c.CrimeNovelist.class));
+ cards.add(new SetCardInfo("Crimestopper Sprite", 49, Rarity.COMMON, mage.cards.c.CrimestopperSprite.class));
cards.add(new SetCardInfo("Culvert Ambusher", 158, Rarity.UNCOMMON, mage.cards.c.CulvertAmbusher.class));
cards.add(new SetCardInfo("Curious Cadaver", 194, Rarity.UNCOMMON, mage.cards.c.CuriousCadaver.class));
cards.add(new SetCardInfo("Curious Inquiry", 51, Rarity.UNCOMMON, mage.cards.c.CuriousInquiry.class));
@@ -71,12 +74,14 @@ public final class MurdersAtKarlovManor extends ExpansionSet {
cards.add(new SetCardInfo("Eliminate the Impossible", 54, Rarity.UNCOMMON, mage.cards.e.EliminateTheImpossible.class));
cards.add(new SetCardInfo("Escape Tunnel", 261, Rarity.COMMON, mage.cards.e.EscapeTunnel.class));
cards.add(new SetCardInfo("Essence of Antiquity", 15, Rarity.UNCOMMON, mage.cards.e.EssenceOfAntiquity.class));
+ cards.add(new SetCardInfo("Evidence Examiner", 201, Rarity.UNCOMMON, mage.cards.e.EvidenceExaminer.class));
cards.add(new SetCardInfo("Exit Specialist", 55, Rarity.UNCOMMON, mage.cards.e.ExitSpecialist.class));
cards.add(new SetCardInfo("Ezrim, Agency Chief", 202, Rarity.RARE, mage.cards.e.EzrimAgencyChief.class));
cards.add(new SetCardInfo("Fae Flight", 56, Rarity.UNCOMMON, mage.cards.f.FaeFlight.class));
cards.add(new SetCardInfo("Faerie Snoop", 203, Rarity.COMMON, mage.cards.f.FaerieSnoop.class));
cards.add(new SetCardInfo("Fanatical Strength", 159, Rarity.COMMON, mage.cards.f.FanaticalStrength.class));
cards.add(new SetCardInfo("Festerleech", 85, Rarity.UNCOMMON, mage.cards.f.Festerleech.class));
+ cards.add(new SetCardInfo("Forensic Researcher", 58, Rarity.UNCOMMON, mage.cards.f.ForensicResearcher.class));
cards.add(new SetCardInfo("Forest", 276, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS));
cards.add(new SetCardInfo("Forum Familiar", 16, Rarity.UNCOMMON, mage.cards.f.ForumFamiliar.class));
cards.add(new SetCardInfo("Furtive Courier", 59, Rarity.UNCOMMON, mage.cards.f.FurtiveCourier.class));
@@ -103,6 +108,7 @@ public final class MurdersAtKarlovManor extends ExpansionSet {
cards.add(new SetCardInfo("Inside Source", 19, Rarity.COMMON, mage.cards.i.InsideSource.class));
cards.add(new SetCardInfo("Insidious Roots", 208, Rarity.UNCOMMON, mage.cards.i.InsidiousRoots.class));
cards.add(new SetCardInfo("Island", 273, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS));
+ cards.add(new SetCardInfo("Izoni, Center of the Web", 209, Rarity.RARE, mage.cards.i.IzoniCenterOfTheWeb.class));
cards.add(new SetCardInfo("Jaded Analyst", 62, Rarity.COMMON, mage.cards.j.JadedAnalyst.class));
cards.add(new SetCardInfo("Knife", 134, Rarity.UNCOMMON, mage.cards.k.Knife.class));
cards.add(new SetCardInfo("Kraul Whipcracker", 213, Rarity.UNCOMMON, mage.cards.k.KraulWhipcracker.class));
@@ -159,6 +165,7 @@ public final class MurdersAtKarlovManor extends ExpansionSet {
cards.add(new SetCardInfo("Rubblebelt Braggart", 143, Rarity.COMMON, mage.cards.r.RubblebeltBraggart.class));
cards.add(new SetCardInfo("Rubblebelt Maverick", 174, Rarity.COMMON, mage.cards.r.RubblebeltMaverick.class));
cards.add(new SetCardInfo("Rune-Brand Juggler", 229, Rarity.UNCOMMON, mage.cards.r.RuneBrandJuggler.class));
+ cards.add(new SetCardInfo("Sample Collector", 175, Rarity.UNCOMMON, mage.cards.s.SampleCollector.class));
cards.add(new SetCardInfo("Sanctuary Wall", 32, Rarity.UNCOMMON, mage.cards.s.SanctuaryWall.class));
cards.add(new SetCardInfo("Sanguine Savior", 230, Rarity.COMMON, mage.cards.s.SanguineSavior.class));
cards.add(new SetCardInfo("Sanitation Automaton", 256, Rarity.COMMON, mage.cards.s.SanitationAutomaton.class));
@@ -176,6 +183,7 @@ public final class MurdersAtKarlovManor extends ExpansionSet {
cards.add(new SetCardInfo("Steamcore Scholar", 71, Rarity.RARE, mage.cards.s.SteamcoreScholar.class));
cards.add(new SetCardInfo("Sudden Setback", 72, Rarity.UNCOMMON, mage.cards.s.SuddenSetback.class));
cards.add(new SetCardInfo("Sumala Sentry", 233, Rarity.UNCOMMON, mage.cards.s.SumalaSentry.class));
+ cards.add(new SetCardInfo("Surveillance Monitor", 73, Rarity.UNCOMMON, mage.cards.s.SurveillanceMonitor.class));
cards.add(new SetCardInfo("Suspicious Detonation", 145, Rarity.COMMON, mage.cards.s.SuspiciousDetonation.class));
cards.add(new SetCardInfo("Swamp", 274, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS));
cards.add(new SetCardInfo("The Chase Is On", 116, Rarity.COMMON, mage.cards.t.TheChaseIsOn.class));
@@ -192,9 +200,11 @@ public final class MurdersAtKarlovManor extends ExpansionSet {
cards.add(new SetCardInfo("Undercover Crocodelf", 239, Rarity.COMMON, mage.cards.u.UndercoverCrocodelf.class));
cards.add(new SetCardInfo("Underground Mortuary", 271, Rarity.RARE, mage.cards.u.UndergroundMortuary.class));
cards.add(new SetCardInfo("Undergrowth Recon", 181, Rarity.MYTHIC, mage.cards.u.UndergrowthRecon.class));
+ cards.add(new SetCardInfo("Urgent Necropsy", 240, Rarity.MYTHIC, mage.cards.u.UrgentNecropsy.class));
cards.add(new SetCardInfo("Vein Ripper", 110, Rarity.MYTHIC, mage.cards.v.VeinRipper.class));
cards.add(new SetCardInfo("Vengeful Creeper", 182, Rarity.COMMON, mage.cards.v.VengefulCreeper.class));
cards.add(new SetCardInfo("Vengeful Tracker", 147, Rarity.UNCOMMON, mage.cards.v.VengefulTracker.class));
+ cards.add(new SetCardInfo("Vitu-Ghazi Inspector", 183, Rarity.COMMON, mage.cards.v.VituGhaziInspector.class));
cards.add(new SetCardInfo("Voja, Jaws of the Conclave", 432, Rarity.MYTHIC, mage.cards.v.VojaJawsOfTheConclave.class));
cards.add(new SetCardInfo("Warleader's Call", 242, Rarity.RARE, mage.cards.w.WarleadersCall.class));
cards.add(new SetCardInfo("Wispdrinker Vampire", 243, Rarity.UNCOMMON, mage.cards.w.WispdrinkerVampire.class));
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/CollectEvidenceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/CollectEvidenceTest.java
new file mode 100644
index 00000000000..659c4bdb6be
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/CollectEvidenceTest.java
@@ -0,0 +1,294 @@
+package org.mage.test.cards.cost.additional;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import mage.counters.CounterType;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author TheElk801
+ */
+public class CollectEvidenceTest extends CardTestPlayerBase {
+
+ private static final String ferox = "Axebane Ferox";
+ private static final String murder = "Murder";
+ private static final String elemental = "Earth Elemental";
+ private static final String giant = "Hill Giant";
+ private static final String ogre = "Gray Ogre";
+ private static final String piker = "Goblin Piker";
+ private static final String raiders = "Mons's Goblin Raiders";
+ private static final String effigy = "Fuming Effigy";
+ private static final String sprite = "Crimestopper Sprite";
+ private static final String monitor = "Surveillance Monitor";
+
+ @Test
+ public void testNoPay() {
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.HAND, playerA, murder);
+ addCard(Zone.BATTLEFIELD, playerB, ferox);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, ferox);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertGraveyardCount(playerA, murder, 1);
+ assertPermanentCount(playerB, ferox, 1);
+ }
+
+ @Test
+ public void testPayWith5() {
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.HAND, playerA, murder);
+ addCard(Zone.GRAVEYARD, playerA, elemental);
+ addCard(Zone.BATTLEFIELD, playerB, ferox);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, ferox);
+ setChoice(playerA, true);
+ setChoice(playerA, elemental);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertGraveyardCount(playerA, murder, 1);
+ assertExileCount(playerA, elemental, 1);
+ assertPermanentCount(playerB, ferox, 0);
+ }
+
+ @Test
+ public void testPayWith411() {
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.HAND, playerA, murder);
+ addCard(Zone.GRAVEYARD, playerA, ogre);
+ addCard(Zone.GRAVEYARD, playerA, raiders);
+ addCard(Zone.BATTLEFIELD, playerB, ferox);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, ferox);
+ setChoice(playerA, true);
+ setChoice(playerA, ogre);
+ setChoice(playerA, raiders);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertGraveyardCount(playerA, murder, 1);
+ assertExileCount(playerA, ogre, 1);
+ assertExileCount(playerA, raiders, 1);
+ assertPermanentCount(playerB, ferox, 0);
+ }
+
+ @Test
+ public void testPayWith4() {
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.HAND, playerA, murder);
+ addCard(Zone.GRAVEYARD, playerA, giant);
+ addCard(Zone.BATTLEFIELD, playerB, ferox);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, ferox);
+ setChoice(playerA, true);
+ setChoice(playerA, giant);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertGraveyardCount(playerA, murder, 1);
+ assertExileCount(playerA, giant, 1);
+ assertPermanentCount(playerB, ferox, 0);
+ }
+
+ @Test
+ public void testPayWith31() {
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.HAND, playerA, murder);
+ addCard(Zone.GRAVEYARD, playerA, ogre);
+ addCard(Zone.GRAVEYARD, playerA, raiders);
+ addCard(Zone.BATTLEFIELD, playerB, ferox);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, ferox);
+ setChoice(playerA, true);
+ setChoice(playerA, ogre);
+ setChoice(playerA, raiders);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertGraveyardCount(playerA, murder, 1);
+ assertExileCount(playerA, ogre, 1);
+ assertExileCount(playerA, raiders, 1);
+ assertPermanentCount(playerB, ferox, 0);
+ }
+
+ @Test
+ public void testPayWith22() {
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.HAND, playerA, murder);
+ addCard(Zone.GRAVEYARD, playerA, piker, 2);
+ addCard(Zone.BATTLEFIELD, playerB, ferox);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, ferox);
+ setChoice(playerA, true);
+ setChoice(playerA, piker, 2);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertGraveyardCount(playerA, murder, 1);
+ assertExileCount(playerA, piker, 2);
+ assertPermanentCount(playerB, ferox, 0);
+ }
+
+ @Test
+ public void testPayWith211() {
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.HAND, playerA, murder);
+ addCard(Zone.GRAVEYARD, playerA, piker);
+ addCard(Zone.GRAVEYARD, playerA, raiders, 2);
+ addCard(Zone.BATTLEFIELD, playerB, ferox);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, ferox);
+ setChoice(playerA, true);
+ setChoice(playerA, piker);
+ setChoice(playerA, raiders, 2);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertGraveyardCount(playerA, murder, 1);
+ assertExileCount(playerA, piker, 1);
+ assertExileCount(playerA, raiders, 2);
+ assertPermanentCount(playerB, ferox, 0);
+ }
+
+ @Test
+ public void testPayWith1111() {
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.HAND, playerA, murder);
+ addCard(Zone.GRAVEYARD, playerA, raiders, 4);
+ addCard(Zone.BATTLEFIELD, playerB, ferox);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, ferox);
+ setChoice(playerA, true);
+ setChoice(playerA, raiders, 4);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertGraveyardCount(playerA, murder, 1);
+ assertExileCount(playerA, raiders, 4);
+ assertPermanentCount(playerB, ferox, 0);
+ }
+
+ @Test
+ public void testFumingEffigy() {
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+ addCard(Zone.BATTLEFIELD, playerA, effigy);
+ addCard(Zone.HAND, playerA, murder);
+ addCard(Zone.GRAVEYARD, playerA, raiders, 4);
+ addCard(Zone.BATTLEFIELD, playerB, ferox);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, murder, ferox);
+ setChoice(playerA, true);
+ setChoice(playerA, raiders, 4);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertGraveyardCount(playerA, murder, 1);
+ assertExileCount(playerA, raiders, 4);
+ assertPermanentCount(playerB, ferox, 0);
+ assertLife(playerB, 20 - 1);
+ }
+
+ @Test
+ public void testSpriteNoPay() {
+ addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
+ addCard(Zone.HAND, playerA, sprite);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sprite);
+ addTarget(playerA, sprite);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertTapped(sprite, true);
+ assertCounterCount(sprite, CounterType.STUN, 0);
+ }
+
+ @Test
+ public void testSpritePay() {
+ addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
+ addCard(Zone.HAND, playerA, sprite);
+ addCard(Zone.GRAVEYARD, playerA, ogre, 2);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sprite);
+ setChoice(playerA, true);
+ setChoice(playerA, ogre, 2);
+ addTarget(playerA, sprite);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertTapped(sprite, true);
+ assertCounterCount(sprite, CounterType.STUN, 1);
+ }
+
+ @Test
+ public void testMonitorTrigger() {
+ addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
+ addCard(Zone.HAND, playerA, monitor);
+ addCard(Zone.GRAVEYARD, playerA, giant);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, monitor);
+ setChoice(playerA, true);
+ setChoice(playerA, giant);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertPermanentCount(playerA, "Thopter Token", 1);
+ }
+
+ @Test
+ public void testMonitorTriggerTwice() {
+ addCard(Zone.BATTLEFIELD, playerA, "Island", 4 + 3);
+ addCard(Zone.HAND, playerA, monitor);
+ addCard(Zone.HAND, playerA, sprite);
+ addCard(Zone.GRAVEYARD, playerA, giant);
+ addCard(Zone.GRAVEYARD, playerA, ogre, 2);
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, monitor);
+ setChoice(playerA, true);
+ setChoice(playerA, giant);
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.BEGIN_COMBAT);
+ execute();
+
+ assertPermanentCount(playerA, "Thopter Token", 1);
+
+ castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, sprite);
+ setChoice(playerA, true);
+ setChoice(playerA, ogre, 2);
+ addTarget(playerA, sprite);
+
+ setStopAt(1, PhaseStep.END_TURN);
+ execute();
+
+ assertPermanentCount(playerA, "Thopter Token", 2);
+ assertTapped(sprite, true);
+ assertCounterCount(sprite, CounterType.STUN, 1);
+ }
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/RealitySmasherTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/RealitySmasherTest.java
index 826af8c88e3..bf68ce301a8 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/RealitySmasherTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/RealitySmasherTest.java
@@ -47,7 +47,6 @@ public class RealitySmasherTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Doom Blade");
addTarget(playerB, "Reality Smasher");
- setChoice(playerB, false); // no discard
setStopAt(1, PhaseStep.BEGIN_COMBAT);
setStrictChooseMode(true);
execute();
diff --git a/Mage/src/main/java/mage/abilities/common/CollectEvidenceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/CollectEvidenceTriggeredAbility.java
new file mode 100644
index 00000000000..4474fb8a6ca
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/common/CollectEvidenceTriggeredAbility.java
@@ -0,0 +1,37 @@
+package mage.abilities.common;
+
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.effects.Effect;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+
+/**
+ * @author TheElk801
+ */
+public class CollectEvidenceTriggeredAbility extends TriggeredAbilityImpl {
+
+ public CollectEvidenceTriggeredAbility(Effect effect, boolean optional) {
+ super(Zone.BATTLEFIELD, effect, optional);
+ setTriggerPhrase("Whenever you collect evidence, ");
+ }
+
+ private CollectEvidenceTriggeredAbility(final CollectEvidenceTriggeredAbility ability) {
+ super(ability);
+ }
+
+ @Override
+ public CollectEvidenceTriggeredAbility copy() {
+ return new CollectEvidenceTriggeredAbility(this);
+ }
+
+ @Override
+ public boolean checkEventType(GameEvent event, Game game) {
+ return event.getType() == GameEvent.EventType.EVIDENCE_COLLECTED;
+ }
+
+ @Override
+ public boolean checkTrigger(GameEvent event, Game game) {
+ return isControlledBy(event.getPlayerId());
+ }
+}
diff --git a/Mage/src/main/java/mage/abilities/condition/common/CollectedEvidenceCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CollectedEvidenceCondition.java
new file mode 100644
index 00000000000..9ff596d1e75
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/condition/common/CollectedEvidenceCondition.java
@@ -0,0 +1,26 @@
+package mage.abilities.condition.common;
+
+import mage.abilities.Ability;
+import mage.abilities.condition.Condition;
+import mage.abilities.keyword.CollectEvidenceAbility;
+import mage.game.Game;
+import mage.util.CardUtil;
+
+/**
+ * Checks if the spell was cast with the alternate collect evidence cost
+ *
+ * @author TheElk801
+ */
+public enum CollectedEvidenceCondition implements Condition {
+ instance;
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ return CardUtil.checkSourceCostsTagExists(game, source, CollectEvidenceAbility.COLLECT_EVIDENCE_ACTIVATION_VALUE_KEY);
+ }
+
+ @Override
+ public String toString() {
+ return "Evidence was collected";
+ }
+}
diff --git a/Mage/src/main/java/mage/abilities/costs/common/CollectEvidenceCost.java b/Mage/src/main/java/mage/abilities/costs/common/CollectEvidenceCost.java
new file mode 100644
index 00000000000..83242ad81a5
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/costs/common/CollectEvidenceCost.java
@@ -0,0 +1,99 @@
+package mage.abilities.costs.common;
+
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.costs.Cost;
+import mage.abilities.costs.CostImpl;
+import mage.abilities.hint.HintUtils;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.players.Player;
+import mage.target.Target;
+import mage.target.common.TargetCardInYourGraveyard;
+
+import java.awt.*;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public class CollectEvidenceCost extends CostImpl {
+
+ private final int amount;
+
+ public CollectEvidenceCost(int amount) {
+ super();
+ this.amount = amount;
+ this.text = "collect evidence " + amount;
+ }
+
+ private CollectEvidenceCost(final CollectEvidenceCost cost) {
+ super(cost);
+ this.amount = cost.amount;
+ }
+
+ @Override
+ public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
+ Player player = game.getPlayer(controllerId);
+ return player != null && player
+ .getGraveyard()
+ .getCards(game)
+ .stream()
+ .filter(Objects::nonNull)
+ .mapToInt(MageObject::getManaValue)
+ .sum() >= amount;
+ }
+
+ @Override
+ public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
+ Player player = game.getPlayer(controllerId);
+ if (player == null) {
+ paid = false;
+ return paid;
+ }
+ // TODO: require target to have minimum selected total mana value (requires refactor)
+ Target target = new TargetCardInYourGraveyard(1, Integer.MAX_VALUE) {
+ @Override
+ public String getMessage() {
+ // shows selected mana value
+ int totalMV = this
+ .getTargets()
+ .stream()
+ .map(game::getCard)
+ .filter(Objects::nonNull)
+ .mapToInt(MageObject::getManaValue)
+ .sum();
+ return super.getMessage() + HintUtils.prepareText(
+ " (selected mana value " + totalMV + " of " + amount + ")",
+ totalMV >= amount ? Color.GREEN : Color.RED
+ );
+ }
+ }.withNotTarget(true);
+ player.choose(Outcome.Exile, target, source, game);
+ Cards cards = new CardsImpl(target.getTargets());
+ paid = cards
+ .getCards(game)
+ .stream()
+ .filter(Objects::nonNull)
+ .mapToInt(MageObject::getManaValue)
+ .sum() >= amount;
+ if (paid) {
+ player.moveCards(cards, Zone.EXILED, source, game);
+ game.fireEvent(GameEvent.getEvent(
+ GameEvent.EventType.EVIDENCE_COLLECTED,
+ source.getSourceId(), source, source.getControllerId(), amount
+ ));
+ }
+ return paid;
+ }
+
+ @Override
+ public CollectEvidenceCost copy() {
+ return new CollectEvidenceCost(this);
+ }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/CounterUnlessPaysEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CounterUnlessPaysEffect.java
index c020c65bd18..f45ac670e47 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/CounterUnlessPaysEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/CounterUnlessPaysEffect.java
@@ -84,7 +84,8 @@ public class CounterUnlessPaysEffect extends OneShotEffect {
message += costValueMessage + '?';
costToPay.clearPaid();
- if (!(player.chooseUse(Outcome.Benefit, message, source, game)
+ if (!(costToPay.canPay(source, source, player.getId(), game)
+ && player.chooseUse(Outcome.Benefit, message, source, game)
&& costToPay.pay(source, game, source, spell.getControllerId(), false, null))) {
game.informPlayers(player.getLogName() + " chooses not to pay " + costValueMessage + " to prevent the counter effect");
game.getStack().counter(spell.getId(), source, game, exile ? PutCards.EXILED : PutCards.GRAVEYARD);
diff --git a/Mage/src/main/java/mage/abilities/keyword/CollectEvidenceAbility.java b/Mage/src/main/java/mage/abilities/keyword/CollectEvidenceAbility.java
new file mode 100644
index 00000000000..13e9d5a29a2
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/keyword/CollectEvidenceAbility.java
@@ -0,0 +1,103 @@
+package mage.abilities.keyword;
+
+import mage.abilities.Ability;
+import mage.abilities.SpellAbility;
+import mage.abilities.StaticAbility;
+import mage.abilities.condition.common.CollectedEvidenceCondition;
+import mage.abilities.costs.*;
+import mage.abilities.costs.common.CollectEvidenceCost;
+import mage.abilities.hint.ConditionTrueHint;
+import mage.abilities.hint.Hint;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.players.Player;
+
+/**
+ * @author TheElk801
+ */
+public class CollectEvidenceAbility extends StaticAbility implements OptionalAdditionalSourceCosts {
+
+ private static final String promptString = "Collect evidence ";
+ private static final String keywordText = "As an additional cost to cast this spell, you may collect evidence ";
+ private static final String reminderText = "Exile cards with total mana value $$$ or greater from your graveyard";
+ private final String rule;
+ private final int amount;
+
+ public static final String COLLECT_EVIDENCE_ACTIVATION_VALUE_KEY = "collectEvidenceActivation";
+
+ protected OptionalAdditionalCost additionalCost;
+
+ private static final Hint hint = new ConditionTrueHint(CollectedEvidenceCondition.instance, "evidence was collected");
+
+ public static OptionalAdditionalCost makeCost(int amount) {
+ OptionalAdditionalCost cost = new OptionalAdditionalCostImpl(
+ keywordText + amount,
+ reminderText.replace("$$$", "" + amount),
+ new CollectEvidenceCost(amount)
+ );
+ cost.setRepeatable(false);
+ return cost;
+ }
+
+ public CollectEvidenceAbility(int amount) {
+ super(Zone.STACK, null);
+ this.additionalCost = makeCost(amount);
+ this.rule = additionalCost.getName() + ' ' + additionalCost.getReminderText();
+ this.setRuleAtTheTop(true);
+ this.addHint(hint);
+ this.amount = amount;
+ }
+
+ private CollectEvidenceAbility(final CollectEvidenceAbility ability) {
+ super(ability);
+ this.rule = ability.rule;
+ this.additionalCost = ability.additionalCost.copy();
+ this.amount = ability.amount;
+ }
+
+ @Override
+ public CollectEvidenceAbility copy() {
+ return new CollectEvidenceAbility(this);
+ }
+
+ public void resetCost() {
+ if (additionalCost != null) {
+ additionalCost.reset();
+ }
+ }
+
+ @Override
+ public void addOptionalAdditionalCosts(Ability ability, Game game) {
+ if (!(ability instanceof SpellAbility)) {
+ return;
+ }
+
+ Player player = game.getPlayer(ability.getControllerId());
+ if (player == null) {
+ return;
+ }
+
+ this.resetCost();
+ boolean canPay = additionalCost.canPay(ability, this, ability.getControllerId(), game);
+ if (!canPay || !player.chooseUse(Outcome.Exile, promptString + amount + '?', ability, game)) {
+ return;
+ }
+
+ additionalCost.activate();
+ for (Cost cost : ((Costs) additionalCost)) {
+ ability.getCosts().add(cost.copy());
+ }
+ ability.setCostsTag(COLLECT_EVIDENCE_ACTIVATION_VALUE_KEY, null);
+ }
+
+ @Override
+ public String getCastMessageSuffix() {
+ return additionalCost.getCastSuffixMessage(0);
+ }
+
+ @Override
+ public String getRule() {
+ return rule;
+ }
+}
diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java
index 9e43083a098..a1640d142b6 100644
--- a/Mage/src/main/java/mage/game/events/GameEvent.java
+++ b/Mage/src/main/java/mage/game/events/GameEvent.java
@@ -573,6 +573,12 @@ public class GameEvent implements Serializable {
playerId the player suspecting
*/
BECOME_SUSPECTED,
+ /* Evidence collected
+ targetId same as sourceId
+ sourceId of the ability for the cost
+ playerId the player paying the cost
+ */
+ EVIDENCE_COLLECTED,
//custom events
CUSTOM_EVENT
}
diff --git a/Mage/src/main/java/mage/game/permanent/token/IzoniSpiderToken.java b/Mage/src/main/java/mage/game/permanent/token/IzoniSpiderToken.java
new file mode 100644
index 00000000000..5837ce32ebb
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/IzoniSpiderToken.java
@@ -0,0 +1,37 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.abilities.keyword.MenaceAbility;
+import mage.abilities.keyword.ReachAbility;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+/**
+ * @author TheElk801
+ */
+public final class IzoniSpiderToken extends TokenImpl {
+
+ public IzoniSpiderToken() {
+ super("Spider Token", "2/1 black and green Spider creature token with menace and reach");
+ cardType.add(CardType.CREATURE);
+ color.setBlack(true);
+ color.setGreen(true);
+ subtype.add(SubType.SPIDER);
+ power = new MageInt(2);
+ toughness = new MageInt(1);
+
+ // Menace
+ this.addAbility(new MenaceAbility());
+
+ // Reach
+ this.addAbility(ReachAbility.getInstance());
+ }
+
+ private IzoniSpiderToken(final IzoniSpiderToken token) {
+ super(token);
+ }
+
+ public IzoniSpiderToken copy() {
+ return new IzoniSpiderToken(this);
+ }
+}
diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java
index 9cc6767be7a..af9fb494478 100644
--- a/Mage/src/main/java/mage/util/CardUtil.java
+++ b/Mage/src/main/java/mage/util/CardUtil.java
@@ -74,7 +74,7 @@ public final class CardUtil {
public static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
private static final List costWords = Arrays.asList(
- "put", "return", "exile", "discard", "sacrifice", "remove", "tap", "reveal", "pay"
+ "put", "return", "exile", "discard", "sacrifice", "remove", "tap", "reveal", "pay", "collect"
);
public static final int TESTS_SET_CODE_LOOKUP_LENGTH = 6; // search set code in commands like "set_code-card_name"
@@ -1764,8 +1764,8 @@ public final class CardUtil {
* Warning, don't use self reference objects because it will raise StackOverflowError
*
* @param value
- * @return
* @param
+ * @return
*/
public static T deepCopyObject(T value) {
if (isImmutableObject(value)) {