forked from External/mage
Merge pull request 'master' (#7) from External/mage:master into master
All checks were successful
/ example-docker-compose (push) Successful in 15m4s
All checks were successful
/ example-docker-compose (push) Successful in 15m4s
Reviewed-on: #7
This commit is contained in:
commit
a204b33f69
26 changed files with 253 additions and 58 deletions
|
|
@ -5,6 +5,8 @@ import mage.client.util.AppUtil;
|
|||
import mage.client.util.GUISizeHelper;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* GUI: error dialog with copyable error message
|
||||
* // TODO: add game logs and data for game errors (client side info from GameView)
|
||||
|
|
@ -28,7 +30,10 @@ public class ErrorDialog extends MageDialog {
|
|||
|
||||
// add additional info
|
||||
String fullError = "Error type: " + fullTitle + "\n"
|
||||
+ "\n"
|
||||
+ "Client version: " + MageFrame.getInstance().getVersion().toString() + "\n"
|
||||
+ "Java version: " + System.getProperty("java.version") + "\n"
|
||||
+ "Default charset: " + Charset.defaultCharset() + "\n"
|
||||
+ "\n"
|
||||
+ errorText;
|
||||
this.textError.setText(fullError);
|
||||
|
|
@ -68,7 +73,8 @@ public class ErrorDialog extends MageDialog {
|
|||
AppUtil.openUrlInSystemBrowser(url);
|
||||
}
|
||||
|
||||
/** This method is called from within the constructor to
|
||||
/**
|
||||
* This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import mage.target.common.TargetCreaturePermanent;
|
|||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
|
|
@ -44,7 +45,7 @@ public final class ComeBackWrong extends CardImpl {
|
|||
class ComeBackWrongEffect extends OneShotEffect {
|
||||
|
||||
ComeBackWrongEffect() {
|
||||
super(Outcome.Benefit);
|
||||
super(Outcome.Neutral);
|
||||
staticText = "destroy target creature. If a creature card is put into a graveyard this way, " +
|
||||
"return it to the battlefield under your control. Sacrifice it at the beginning of your next end step";
|
||||
}
|
||||
|
|
@ -65,8 +66,14 @@ class ComeBackWrongEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
permanent.destroy(source, game);
|
||||
// tokens are not creature cards
|
||||
if (permanent instanceof PermanentToken) {
|
||||
return false;
|
||||
}
|
||||
Card card = permanent.getMainCard();
|
||||
if (card == null || !card.isCreature(game) || !Zone.GRAVEYARD.match(game.getState().getZone(card.getId()))) {
|
||||
if (card == null
|
||||
|| !card.isCreature(game)
|
||||
|| !Zone.GRAVEYARD.match(game.getState().getZone(card.getId()))) {
|
||||
return true;
|
||||
}
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@ import mage.constants.*;
|
|||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.TokenImpl;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -78,13 +80,18 @@ class ElvishBranchbenderEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
int xValue = new PermanentsOnBattlefieldCount(filter).calculate(game, source, this);
|
||||
Permanent targetForest = game.getPermanent(this.getTargetPointer().copy().getFirst(game, source));
|
||||
if (targetForest == null) {
|
||||
return false;
|
||||
}
|
||||
ContinuousEffect effect = new BecomesCreatureTargetEffect(
|
||||
new ElvishBranchbenderToken(xValue),
|
||||
false, false, Duration.EndOfTurn)
|
||||
.withDurationRuleAtStart(true);
|
||||
effect.setTargetPointer(this.getTargetPointer().copy());
|
||||
// works well with blinked effects
|
||||
effect.setTargetPointer(new FixedTarget(targetForest, game));
|
||||
game.addEffect(effect, source);
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -101,6 +108,7 @@ class ElvishBranchbenderToken extends TokenImpl {
|
|||
super(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElvishBranchbenderToken copy() {
|
||||
return new ElvishBranchbenderToken(this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,9 @@ class ForTheAncestorsEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
ChoiceCreatureType choice = new ChoiceCreatureType(game, source);
|
||||
player.choose(outcome, choice, game);
|
||||
if (!player.choose(outcome, choice, game)) {
|
||||
return false;
|
||||
}
|
||||
SubType subType = SubType.byDescription(choice.getChoiceKey());
|
||||
FilterCard filter;
|
||||
if (subType != null) {
|
||||
|
|
|
|||
|
|
@ -66,18 +66,14 @@ class HarshMercyEffect extends OneShotEffect {
|
|||
MageObject sourceObject = game.getObject(source);
|
||||
if (controller != null && sourceObject != null) {
|
||||
Set<String> chosenTypes = new HashSet<>();
|
||||
PlayerIteration:
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
Choice typeChoice = new ChoiceCreatureType(game, source);
|
||||
if (player != null && !player.choose(Outcome.DestroyPermanent, typeChoice, game)) {
|
||||
continue PlayerIteration;
|
||||
}
|
||||
String chosenType = typeChoice.getChoiceKey();
|
||||
if (chosenType != null) {
|
||||
game.informPlayers(sourceObject.getIdName() + ": " + player.getLogName() + " has chosen " + chosenType);
|
||||
chosenTypes.add(chosenType);
|
||||
continue;
|
||||
}
|
||||
game.informPlayers(sourceObject.getIdName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoiceKey());
|
||||
chosenTypes.add(typeChoice.getChoiceKey());
|
||||
}
|
||||
|
||||
FilterPermanent filter = new FilterCreaturePermanent("creatures");
|
||||
|
|
|
|||
|
|
@ -128,13 +128,10 @@ class KaronaFalseGodEffect extends OneShotEffect {
|
|||
if (!controller.choose(Outcome.BoostCreature, typeChoice, game)) {
|
||||
return false;
|
||||
}
|
||||
String typeChosen = typeChoice.getChoiceKey();
|
||||
if (!typeChosen.isEmpty()) {
|
||||
game.informPlayers(controller.getLogName() + " has chosen " + typeChosen);
|
||||
FilterCreaturePermanent filter = new FilterCreaturePermanent();
|
||||
filter.add(SubType.byDescription(typeChosen).getPredicate());
|
||||
game.addEffect(new BoostAllEffect(3, 3, Duration.EndOfTurn, filter, false), source);
|
||||
}
|
||||
game.informPlayers(controller.getLogName() + " has chosen " + typeChoice.getChoiceKey());
|
||||
FilterCreaturePermanent filter = new FilterCreaturePermanent();
|
||||
filter.add(SubType.byDescription(typeChoice.getChoiceKey()).getPredicate());
|
||||
game.addEffect(new BoostAllEffect(3, 3, Duration.EndOfTurn, filter, false), source);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ class LongListOfTheEntsEffect extends OneShotEffect {
|
|||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
ChoiceCreatureType choice = new ChoiceCreatureType(game, source);
|
||||
|
||||
Object existingEntList = game.getState().getValue(LongListOfTheEnts.getKey(game, source, 0));
|
||||
int offset;
|
||||
Set<SubType> newEntList;
|
||||
|
|
@ -124,12 +124,13 @@ class LongListOfTheEntsEffect extends OneShotEffect {
|
|||
.stream()
|
||||
.map(SubType::toString)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
ChoiceCreatureType choice = new ChoiceCreatureType(game, source);
|
||||
choice.getKeyChoices().keySet().removeIf(chosenTypes::contains);
|
||||
player.choose(Outcome.BoostCreature, choice, game);
|
||||
SubType subType = SubType.byDescription(choice.getChoiceKey());
|
||||
if (subType == null) {
|
||||
if (!player.choose(Outcome.BoostCreature, choice, game)) {
|
||||
return false;
|
||||
}
|
||||
SubType subType = SubType.byDescription(choice.getChoiceKey());
|
||||
game.informPlayers(player.getLogName() + " notes the creature type " + subType);
|
||||
newEntList.add(subType);
|
||||
game.getState().setValue(LongListOfTheEnts.getKey(game, source, offset), newEntList);
|
||||
|
|
|
|||
|
|
@ -78,10 +78,8 @@ class BecomesChosenCreatureTypeControlledEffect extends OneShotEffect {
|
|||
String chosenType = "";
|
||||
if (player != null && card != null) {
|
||||
Choice typeChoice = new ChoiceCreatureType(game, source);
|
||||
while (!player.choose(Outcome.BoostCreature, typeChoice, game)) {
|
||||
if (!player.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
if (!player.choose(Outcome.BoostCreature, typeChoice, game)) {
|
||||
return false;
|
||||
}
|
||||
game.informPlayers(card.getName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoiceKey());
|
||||
chosenType = typeChoice.getChoiceKey();
|
||||
|
|
|
|||
|
|
@ -83,6 +83,9 @@ class NadierAgentOfTheDuskenelEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// For Nadier's second ability, use its power from when it was last on the battlefield to determine
|
||||
// how many tokens to create.
|
||||
// (2020-11-10)
|
||||
Object obj = getValue("permanentLeftBattlefield");
|
||||
if (!(obj instanceof Permanent)) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -69,9 +69,8 @@ class PatriarchsBiddingEffect extends OneShotEffect {
|
|||
if (!player.choose(Outcome.PutCreatureInPlay, typeChoice, game)) {
|
||||
continue;
|
||||
}
|
||||
String chosenType = typeChoice.getChoiceKey();
|
||||
game.informPlayers(sourceObject.getLogName() + ": " + player.getLogName() + " has chosen " + chosenType);
|
||||
chosenTypes.add(chosenType);
|
||||
game.informPlayers(sourceObject.getLogName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoiceKey());
|
||||
chosenTypes.add(typeChoice.getChoiceKey());
|
||||
}
|
||||
|
||||
List<SubType.SubTypePredicate> predicates = new ArrayList<>();
|
||||
|
|
|
|||
|
|
@ -73,11 +73,10 @@ class PolJamaarIllusionistEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
ChoiceCreatureType choice = new ChoiceCreatureType(game, source);
|
||||
player.choose(outcome, choice, game);
|
||||
SubType subType = SubType.byDescription(choice.getChoice());
|
||||
if (subType == null) {
|
||||
if (!player.choose(outcome, choice, game)) {
|
||||
return false;
|
||||
}
|
||||
SubType subType = SubType.byDescription(choice.getChoiceKey());
|
||||
game.informPlayers(player.getLogName() + " chooses " + subType);
|
||||
int amount = game
|
||||
.getBattlefield()
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ class StandardizeEffect extends OneShotEffect {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
MageObject sourceObject = game.getObject(source);
|
||||
String chosenType = "";
|
||||
if (player != null && sourceObject != null) {
|
||||
Choice typeChoice = new ChoiceCreatureType(game, source);
|
||||
typeChoice.setMessage("Choose a creature type other than Wall");
|
||||
|
|
@ -66,16 +65,12 @@ class StandardizeEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
game.informPlayers(sourceObject.getLogName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoiceKey());
|
||||
chosenType = typeChoice.getChoiceKey();
|
||||
if (chosenType != null && !chosenType.isEmpty()) {
|
||||
// ADD TYPE TO TARGET
|
||||
game.addEffect(new BecomesSubtypeAllEffect(
|
||||
Duration.EndOfTurn, Arrays.asList(SubType.byDescription(chosenType)),
|
||||
StaticFilters.FILTER_PERMANENT_CREATURE, true
|
||||
), source);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ADD TYPE TO TARGET
|
||||
game.addEffect(new BecomesSubtypeAllEffect(
|
||||
Duration.EndOfTurn, Arrays.asList(SubType.byDescription(typeChoice.getChoiceKey())),
|
||||
StaticFilters.FILTER_PERMANENT_CREATURE, true
|
||||
), source);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ class ThreeTreeScribeTriggeredAbility extends TriggeredAbilityImpl {
|
|||
super(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
|
||||
this.setTriggerPhrase("Whenever {this} or another creature you control leaves the battlefield without dying, ");
|
||||
this.addTarget(new TargetControlledCreaturePermanent());
|
||||
setLeavesTheBattlefieldTrigger(true);
|
||||
}
|
||||
|
||||
private ThreeTreeScribeTriggeredAbility(final ThreeTreeScribeTriggeredAbility ability) {
|
||||
|
|
|
|||
|
|
@ -67,9 +67,7 @@ class TribalUnityEffect extends OneShotEffect {
|
|||
Choice typeChoice = new ChoiceCreatureType(game, source);
|
||||
if (player != null && player.choose(outcome, typeChoice, game)) {
|
||||
int boost = amount.calculate(game, source, this);
|
||||
if (typeChoice.getChoiceKey() != null) {
|
||||
game.informPlayers(sourceObject.getLogName() + " chosen type: " + typeChoice.getChoiceKey());
|
||||
}
|
||||
game.informPlayers(sourceObject.getLogName() + " chosen type: " + typeChoice.getChoiceKey());
|
||||
FilterCreaturePermanent filterCreaturePermanent = new FilterCreaturePermanent();
|
||||
filterCreaturePermanent.add(SubType.byDescription(typeChoice.getChoiceKey()).getPredicate());
|
||||
game.addEffect(new BoostAllEffect(
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ class LeavesTheBattlefieldAttachedTriggeredAbility extends ZoneChangeTriggeredAb
|
|||
|
||||
public LeavesTheBattlefieldAttachedTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken()), "When enchanted creature leaves the battlefield, ", Boolean.FALSE);
|
||||
setLeavesTheBattlefieldTrigger(true);
|
||||
}
|
||||
|
||||
private LeavesTheBattlefieldAttachedTriggeredAbility(final LeavesTheBattlefieldAttachedTriggeredAbility ability) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
package org.mage.test.cards.single.cmr;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestCommanderDuelBase;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
|
||||
public class NadierAgentOfTheDuskenelTest extends CardTestCommanderDuelBase {
|
||||
|
||||
@Test
|
||||
public void test_DieAnother() {
|
||||
addCustomEffect_TargetDestroy(playerA);
|
||||
|
||||
// Whenever a token you control leaves the battlefield, put a +1/+1 counter on Nadier, Agent of the Duskenel.
|
||||
// When Nadier leaves the battlefield, create a number of 1/1 green Elf Warrior creature tokens equal to its power.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Nadier, Agent of the Duskenel", 1);
|
||||
//
|
||||
// {5}, {T}: Create a 1/1 colorless Insect artifact creature token with flying named Wasp.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "The Hive", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||
|
||||
// prepare token
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{5}, {T}: Create");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkPermanentCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wasp", 1);
|
||||
|
||||
// on leaves non-token -- nothing to happen
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "The Hive");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1);
|
||||
checkGraveyardCount("on non-token", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "The Hive", 1);
|
||||
checkStackSize("on non-token - no triggers", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0);
|
||||
|
||||
// on leaves token - must trigger
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Wasp");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkPermanentCount("on token", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wasp", 0);
|
||||
checkPermanentCounters("on token - must trigger", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nadier, Agent of the Duskenel", CounterType.P1P1, 1);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DieItself() {
|
||||
addCustomEffect_TargetDestroy(playerA, 2);
|
||||
|
||||
// Whenever a token you control leaves the battlefield, put a +1/+1 counter on Nadier, Agent of the Duskenel.
|
||||
// When Nadier leaves the battlefield, create a number of 1/1 green Elf Warrior creature tokens equal to its power.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Nadier, Agent of the Duskenel", 1);
|
||||
//
|
||||
// {5}, {T}: Create a 1/1 colorless Insect artifact creature token with flying named Wasp.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "The Hive", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||
|
||||
// prepare token
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{5}, {T}: Create");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkPermanentCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wasp", 1);
|
||||
|
||||
// on leaves token and itself -- must x2 triggers:
|
||||
// * one with counter to fizzle
|
||||
// * one with new token
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Wasp^Nadier, Agent of the Duskenel");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1);
|
||||
checkStackSize("must triggers x2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2);
|
||||
setChoice(playerA, "Whenever a token you control leaves"); // x2 triggers from Nadier
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "The Hive", 1);
|
||||
assertPermanentCount(playerA, "Wasp", 0);
|
||||
assertGraveyardCount(playerA, "Nadier, Agent of the Duskenel", 1);
|
||||
assertPermanentCount(playerA, "Elf Warrior Token", 3);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package org.mage.test.cards.single.khm;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class ValorOfTheWorthyTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void test_DieTarget() {
|
||||
addCustomEffect_TargetDestroy(playerA);
|
||||
|
||||
// Enchant creature
|
||||
// Enchanted creature gets +1/+1.
|
||||
// When enchanted creature leaves the battlefield, create a 1/1 white Spirit creature token with flying.
|
||||
addCard(Zone.HAND, playerA, "Valor of the Worthy"); // {W}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
|
||||
|
||||
// attach
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Valor of the Worthy", "Grizzly Bears");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
checkPT("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 2 + 1, 2 + 1);
|
||||
|
||||
// destroy target - must trigger
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy");
|
||||
addTarget(playerA, "Grizzly Bears");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); // resolve destroy
|
||||
checkStackObject("must trigger on destroy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "When enchanted creature leaves the battlefield", 1);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertTokenCount(playerA, "Spirit Token", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DieItself() {
|
||||
addCustomEffect_TargetDestroy(playerA, 2);
|
||||
|
||||
// Enchant creature
|
||||
// Enchanted creature gets +1/+1.
|
||||
// When enchanted creature leaves the battlefield, create a 1/1 white Spirit creature token with flying.
|
||||
addCard(Zone.HAND, playerA, "Valor of the Worthy"); // {W}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
|
||||
|
||||
// attach
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Valor of the Worthy", "Grizzly Bears");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
checkPT("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 2 + 1, 2 + 1);
|
||||
|
||||
// destroy all - must trigger anyway
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy");
|
||||
addTarget(playerA, "Valor of the Worthy^Grizzly Bears");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); // resolve destroy
|
||||
checkStackObject("must trigger on destroy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "When enchanted creature leaves the battlefield", 1);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertTokenCount(playerA, "Spirit Token", 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ public class PrimalPrayersTest extends CardTestPlayerBase {
|
|||
setChoice(playerA, "Cast with alternative cost: Pay {E}"); // alternative cost chosen
|
||||
checkPlayableAbility("no more energy to cast third Bears", 1, PhaseStep.BEGIN_COMBAT, playerA,
|
||||
"Cast Grizzly Bears", false);
|
||||
runCode("3: energy counter is 0", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> checkEnergyCount(info, player, 0));
|
||||
runCode("3: energy counter is 0", 1, PhaseStep.BEGIN_COMBAT, playerA, (info, player, game) -> checkEnergyCount(info, player, 0));
|
||||
|
||||
setStopAt(1, PhaseStep.END_COMBAT);
|
||||
execute();
|
||||
|
|
|
|||
|
|
@ -102,10 +102,11 @@ public class BloodCultistTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blood Cultist");
|
||||
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {this} deals", "Devilthorn Fox");
|
||||
activateAbility(5, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: {this} deals", "Shambling Ghoul");
|
||||
attack(5, playerA, "Silvercoat Lion");
|
||||
block(5, playerB, "Shambling Ghoul", "Silvercoat Lion");
|
||||
activateAbility(5, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: {this} deals", "Shambling Ghoul");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(5, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ public class PlayerAction {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "T" + this.turnNum + "." + this.step.getStepShortText() + ": " + this.action;
|
||||
return "T" + this.turnNum + "." + this.step.getStepShortText()
|
||||
+ ": " + this.action
|
||||
+ (this.actionName.isEmpty() ? "" : " (" + this.actionName + ")");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -262,6 +262,28 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
|||
+ " (found actions after stop on " + maxTurn + " / " + maxPhase + ")",
|
||||
(maxTurn > this.stopOnTurn) || (maxTurn == this.stopOnTurn && maxPhase > this.stopAtStep.getIndex()));
|
||||
|
||||
// check commands order
|
||||
for (Player player : currentGame.getPlayers().values()) {
|
||||
if (true) break; // TODO: delete/comment and fix all failed tests
|
||||
if (player instanceof TestPlayer) {
|
||||
TestPlayer testPlayer = (TestPlayer) player;
|
||||
int lastActionIndex = 0;
|
||||
PlayerAction lastAction = null;
|
||||
for (PlayerAction currentAction : testPlayer.getActions()) {
|
||||
int currentActionIndex = 1000 * currentAction.getTurnNum() + currentAction.getStep().getIndex();
|
||||
if (currentActionIndex < lastActionIndex) {
|
||||
// how-to fix: find typo in step/turn number
|
||||
Assert.fail("Found wrong commands order for " + testPlayer.getName() + ":" + "\n"
|
||||
+ lastAction + "\n"
|
||||
+ currentAction);
|
||||
} else {
|
||||
lastActionIndex = currentActionIndex;
|
||||
lastAction = currentAction;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentGame.isPaused()) {
|
||||
// workaround to fill range info (cause real range fills after game start, but some cheated cards needs range on ETB)
|
||||
for (Player player : currentGame.getPlayers().values()) {
|
||||
|
|
|
|||
|
|
@ -2021,10 +2021,12 @@ public class VerifyCardDataTest {
|
|||
boolean isPutToGraveAbility = rules.contains("put into")
|
||||
&& rules.contains("graveyard")
|
||||
&& rules.contains("from the battlefield");
|
||||
boolean isLeavesBattlefield = rules.contains("leaves the battlefield");
|
||||
isLeavesBattlefield = false; // TODO: remove and fix all bad cards
|
||||
if (triggeredAbility.isLeavesTheBattlefieldTrigger()) {
|
||||
// TODO: add check for wrongly enabled settings too?
|
||||
} else {
|
||||
if (isDiesAbility || isPutToGraveAbility) {
|
||||
if (isDiesAbility || isPutToGraveAbility || isLeavesBattlefield) {
|
||||
fail(card, "abilities", "dies related trigger must use setLeavesTheBattlefieldTrigger(true) and possibly override isInUseableZone - "
|
||||
+ triggeredAbility.getClass().getSimpleName());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ public class LeavesBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl {
|
|||
this.filter = filter;
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
setTriggerPhrase("Whenever " + filter.getMessage() + " leaves the battlefield, ");
|
||||
setLeavesTheBattlefieldTrigger(true);
|
||||
}
|
||||
|
||||
protected LeavesBattlefieldAllTriggeredAbility(final LeavesBattlefieldAllTriggeredAbility ability) {
|
||||
|
|
|
|||
|
|
@ -59,10 +59,9 @@ public class BecomesChosenCreatureTypeTargetEffect extends OneShotEffect {
|
|||
if (nonWall) {
|
||||
typeChoice.getKeyChoices().remove(SubType.WALL.getDescription());
|
||||
}
|
||||
while (!player.choose(Outcome.BoostCreature, typeChoice, game)) {
|
||||
if (!player.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!player.choose(Outcome.BoostCreature, typeChoice, game)) {
|
||||
return false;
|
||||
}
|
||||
game.informPlayers(card.getName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoiceKey());
|
||||
chosenType = typeChoice.getChoiceKey();
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ import java.util.*;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Game's choose dialog to ask about creature type. Return getChoice
|
||||
* Game's choose dialog to ask about creature type.
|
||||
* <p>
|
||||
* Warning, must use getChoiceKey as result
|
||||
*/
|
||||
public class ChoiceCreatureType extends ChoiceImpl {
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,9 @@ class VolosJournalTokenEffect extends OneShotEffect {
|
|||
return true;
|
||||
}
|
||||
|
||||
player.choose(outcome, choice, game);
|
||||
if (!player.choose(outcome, choice, game)) {
|
||||
return false;
|
||||
}
|
||||
notedTypes.add(choice.getChoiceKey());
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue