mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
Implementing "collect evidence" mechanic (#11671)
* [MKM] Implement Axebane Ferox * add exile to cost, fix text * add targeting message copied from crew ability * [MKM] Implement Forensic Researcher * [MKM] Implement Izoni, Center of the Web * implement requested changes * merge fix * [MKM] Implement Sample Collector * [MKM] Implement Evidence Examiner * [MKM] Implement Surveillance Monitor * [MKM] Implement Vitu-Ghazi Inspector * [MKM] Implement Crimestopper Sprite * [MKM] Implement Urgent Necropsy * [MKM] Implement Analyze the Pollen * implement requested changes * add can pay cost check to counter unless pays effect * fix test failure * add tests * fix prompt message
This commit is contained in:
parent
322c49e37f
commit
99c2ffa231
21 changed files with 1162 additions and 4 deletions
44
Mage.Sets/src/mage/cards/a/AnalyzeThePollen.java
Normal file
44
Mage.Sets/src/mage/cards/a/AnalyzeThePollen.java
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
45
Mage.Sets/src/mage/cards/a/AxebaneFerox.java
Normal file
45
Mage.Sets/src/mage/cards/a/AxebaneFerox.java
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
58
Mage.Sets/src/mage/cards/c/CrimestopperSprite.java
Normal file
58
Mage.Sets/src/mage/cards/c/CrimestopperSprite.java
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
48
Mage.Sets/src/mage/cards/e/EvidenceExaminer.java
Normal file
48
Mage.Sets/src/mage/cards/e/EvidenceExaminer.java
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
52
Mage.Sets/src/mage/cards/f/ForensicResearcher.java
Normal file
52
Mage.Sets/src/mage/cards/f/ForensicResearcher.java
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
66
Mage.Sets/src/mage/cards/i/IzoniCenterOfTheWeb.java
Normal file
66
Mage.Sets/src/mage/cards/i/IzoniCenterOfTheWeb.java
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
49
Mage.Sets/src/mage/cards/s/SampleCollector.java
Normal file
49
Mage.Sets/src/mage/cards/s/SampleCollector.java
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
49
Mage.Sets/src/mage/cards/s/SurveillanceMonitor.java
Normal file
49
Mage.Sets/src/mage/cards/s/SurveillanceMonitor.java
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
76
Mage.Sets/src/mage/cards/u/UrgentNecropsy.java
Normal file
76
Mage.Sets/src/mage/cards/u/UrgentNecropsy.java
Normal file
|
|
@ -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(
|
||||
"<br>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));
|
||||
}
|
||||
}
|
||||
59
Mage.Sets/src/mage/cards/v/VituGhaziInspector.java
Normal file
59
Mage.Sets/src/mage/cards/v/VituGhaziInspector.java
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<Cost>) 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -74,7 +74,7 @@ public final class CardUtil {
|
|||
public static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
|
||||
|
||||
private static final List<String> 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 <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> T deepCopyObject(T value) {
|
||||
if (isImmutableObject(value)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue