implement [MH3] Detective's Phoenix

This commit is contained in:
Susucre 2024-05-26 16:17:14 +02:00
parent a3b7bb785d
commit faa868aa16
4 changed files with 243 additions and 1 deletions

View file

@ -0,0 +1,104 @@
package mage.cards.d;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.common.CollectEvidenceCost;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
import mage.abilities.keyword.BestowAbility;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.HasteAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import java.util.UUID;
/**
* @author Susucr
*/
public final class DetectivesPhoenix extends CardImpl {
public DetectivesPhoenix(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{2}{R}");
this.subtype.add(SubType.PHOENIX);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Bestow--{R}, Collect evidence 6
Ability ability = new BestowAbility(this, "{R}");
ability.addCost(new CollectEvidenceCost(6));
this.addAbility(ability);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Haste
this.addAbility(HasteAbility.getInstance());
// Enchanted creature gets +2/+2 and has flying and haste.
ability = new SimpleStaticAbility(new BoostEnchantedEffect(2, 2));
ability.addEffect(
new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA)
.setText("and has flying")
);
ability.addEffect(
new GainAbilityAttachedEffect(HasteAbility.getInstance(), AttachmentType.AURA)
.setText("and haste")
);
this.addAbility(ability);
// You may cast Detective's Phoenix from your graveyard using its bestow ability.
this.addAbility(new SimpleStaticAbility(Zone.ALL, new DetectivesPhoenixEffect()));
}
private DetectivesPhoenix(final DetectivesPhoenix card) {
super(card);
}
@Override
public DetectivesPhoenix copy() {
return new DetectivesPhoenix(this);
}
}
// Similar to Tenacious Underdog
class DetectivesPhoenixEffect extends AsThoughEffectImpl {
DetectivesPhoenixEffect() {
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.PutCreatureInPlay);
staticText = "You may cast {this} from your graveyard using its bestow ability";
}
private DetectivesPhoenixEffect(final DetectivesPhoenixEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public DetectivesPhoenixEffect copy() {
return new DetectivesPhoenixEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
return objectId.equals(source.getSourceId())
&& source.isControlledBy(playerId)
&& affectedAbility instanceof BestowAbility
&& game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD
&& game.getCard(source.getSourceId()) != null;
}
@Override
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
return false;
}
}

View file

@ -41,6 +41,7 @@ public final class ModernHorizons3 extends ExpansionSet {
cards.add(new SetCardInfo("Cursed Mirror", 279, Rarity.RARE, mage.cards.c.CursedMirror.class));
cards.add(new SetCardInfo("Deep Analysis", 268, Rarity.UNCOMMON, mage.cards.d.DeepAnalysis.class));
cards.add(new SetCardInfo("Deserted Temple", 301, Rarity.RARE, mage.cards.d.DesertedTemple.class));
cards.add(new SetCardInfo("Detective's Phoenix", 116, Rarity.RARE, mage.cards.d.DetectivesPhoenix.class));
cards.add(new SetCardInfo("Devourer of Destiny", 2, Rarity.RARE, mage.cards.d.DevourerOfDestiny.class));
cards.add(new SetCardInfo("Disciple of Freyalise", 250, Rarity.UNCOMMON, mage.cards.d.DiscipleOfFreyalise.class));
cards.add(new SetCardInfo("Dreadmobile", 87, Rarity.UNCOMMON, mage.cards.d.Dreadmobile.class));

View file

@ -0,0 +1,126 @@
package org.mage.test.cards.single.mh3;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class DetectivesPhoenixTest extends CardTestPlayerBase {
/**
* {@link mage.cards.d.DetectivesPhoenix Detective's Phoenix} {2}{R}
* Enchantment Creature Phoenix
* Bestow{R}, Collect evidence 6. (To pay this bestow cost, pay {R} and exile cards with total mana value 6 or greater from your graveyard.)
* Flying, haste
* Enchanted creature gets +2/+2 and has flying and haste.
* You may cast Detective's Phoenix from your graveyard using its bestow ability.
* 2/2
*/
private static final String phoenix = "Detective's Phoenix";
@Test
public void test_Cast_FromHand_Normal() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Memnite");
addCard(Zone.HAND, playerA, phoenix);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
checkPlayableAbility("Can cast normal way", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + phoenix, true);
checkPlayableAbility("Can cast normal way", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + phoenix + " using bestow", false);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, phoenix);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertTappedCount("Mountain", true, 3);
assertPowerToughness(playerA, phoenix, 2, 2);
}
@Test
public void test_Cast_FromHand_Bestow() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Memnite");
addCard(Zone.HAND, playerA, phoenix);
addCard(Zone.GRAVEYARD, playerA, "Grave Titan");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
checkPlayableAbility("Can cast normal way", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + phoenix, true);
checkPlayableAbility("Can cast normal way", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + phoenix + " using bestow", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, phoenix + " using bestow", "Memnite");
setChoice(playerA, "Grave Titan"); // pay for Collect Evidence.
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertTappedCount("Mountain", true, 1);
assertExileCount("Grave Titan", 1);
assertPowerToughness(playerA, "Memnite", 1 + 2, 1 + 2);
}
@Test
public void test_Cast_FromGraveyard_Normal_NotPossible() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Memnite");
addCard(Zone.GRAVEYARD, playerA, phoenix);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
checkPlayableAbility("Can cast normal way", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + phoenix, false);
checkPlayableAbility("Can cast normal way", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + phoenix + " using bestow", false);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
}
@Test
public void test_Cast_FromGraveyard_Bestow() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Memnite");
addCard(Zone.GRAVEYARD, playerA, phoenix);
addCard(Zone.GRAVEYARD, playerA, "Grave Titan");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
checkPlayableAbility("Can cast normal way", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + phoenix, true); // It is a prefix check, so sadly can not check that can't cast using non-bestow
checkPlayableAbility("Can cast normal way", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + phoenix + " using bestow", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, phoenix + " using bestow", "Memnite");
setChoice(playerA, "Grave Titan"); // pay for Collect Evidence.
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertTappedCount("Mountain", true, 1);
assertExileCount("Grave Titan", 1);
assertPowerToughness(playerA, "Memnite", 1 + 2, 1 + 2);
}
@Test
public void test_Cast_FromGraveyard_BestowPossible_NotRegular() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Memnite");
addCard(Zone.GRAVEYARD, playerA, phoenix);
addCard(Zone.GRAVEYARD, playerA, "Grave Titan");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
checkPlayableAbility("Can cast normal way", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + phoenix, true); // It is a prefix check, so sadly can not check that can't cast using non-bestow
checkPlayableAbility("Can cast normal way", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + phoenix + " using bestow", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, phoenix, "Memnite");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
try {
execute();
Assert.fail("should have failed");
} catch (Throwable e) {
if (!e.getMessage().contains("Cast " + phoenix)) {
Assert.fail("Should have thrown error about not being able to cast the Phoenix, but got:\n" + e.getMessage());
}
}
}
}

View file

@ -4,6 +4,7 @@ import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.Costs;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.AttachEffect;
@ -15,6 +16,7 @@ import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent;
import mage.util.CardUtil;
/**
* 702.102. Bestow
@ -109,7 +111,16 @@ public class BestowAbility extends SpellAbility {
@Override
public String getRule() {
return "Bestow " + getManaCostsToPay().getText() + " <i>(If you cast this card for its bestow cost, it's an Aura spell with enchant creature. It becomes a creature again if it's not attached to a creature.)</i>";
StringBuilder sb = new StringBuilder("Bestow");
Costs costs = getCosts();
if (costs.size() > 0) {
sb.append("&mdash;").append(getManaCostsToPay().getText()).append(", ");
sb.append(CardUtil.getTextWithFirstCharUpperCase(costs.getText())).append('.');
} else {
sb.append(" ").append(getManaCostsToPay().getText());
}
sb.append(" <i>(If you cast this card for its bestow cost, it's an Aura spell with enchant creature. It becomes a creature again if it's not attached to a creature.)</i>");
return sb.toString();
}
public static void becomeCreature(Permanent permanent, Game game) {