Introduce new batch event for life lost for a specific player (#13071)

* Introduce new batch event for life lost for a specific player

closes #12202, fix #10805

* implement [DSC] Valgavoth, Harrower of Souls

* text fixes
This commit is contained in:
xenohedron 2024-11-19 23:41:34 -05:00 committed by GitHub
parent 95e986dee7
commit d6cf207a8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 693 additions and 707 deletions

View file

@ -1,4 +1,4 @@
package org.mage.test.cards.watchers;
package org.mage.test.cards.single.aer;
import mage.constants.PhaseStep;
import mage.constants.Zone;
@ -7,7 +7,6 @@ import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author escplan9
*/
public class GontisMachinationsTest extends CardTestPlayerBase {
@ -19,57 +18,59 @@ Whenever you lose life for the first time each turn, you get {E}. (You get an en
Pay {E}{E}, Sacrifice Gonti's Machinations: Each opponent loses 3 life. You gain life equal to the life lost this way.
*/
private final String gMachinations = "Gonti's Machinations";
/*
* Reported bug: [[Gonti's Machinations]] currently triggers and gain 1 energy whenever you lose life instead of only the first life loss of each turn.
* See issue #3499 (test is currently failing due to bug in code)
*/
* See issue #3499 for context
*/
@Test
public void machinations_ThreeCreaturesCombatDamage_OneTrigger() {
setStrictChooseMode(true);
String memnite = "Memnite"; // {0} 1/1
String gBears = "Grizzly Bears"; // {1}{G} 2/2
String hGiant = "Hill Giant"; // {2}{R} 3/3
addCard(Zone.BATTLEFIELD, playerB, gMachinations);
addCard(Zone.BATTLEFIELD, playerA, memnite);
addCard(Zone.BATTLEFIELD, playerA, gBears);
addCard(Zone.BATTLEFIELD, playerA, hGiant);
attack(3, playerA, memnite);
attack(3, playerA, gBears);
attack(3, playerA, hGiant);
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertTapped(memnite, true);
assertTapped(gBears, true);
assertTapped(hGiant, true);
assertLife(playerB, 14); // 1 + 2 + 3 damage
assertCounterCount(playerB, CounterType.ENERGY, 1);
}
/*
* Reported bug: [[Gonti's Machinations]] currently triggers and gain 1 energy whenever you lose life instead of only the first life loss of each turn.
* See issue #3499 (test is currently failing due to bug in code)
*/
* See issue #3499 for context
*/
@Test
public void machinations_NonCombatDamageThreeTimes_OneTrigger() {
setStrictChooseMode(true);
String bolt = "Lightning Bolt"; // {R} deal 3
addCard(Zone.BATTLEFIELD, playerB, gMachinations);
addCard(Zone.HAND, playerA, bolt, 3);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertTappedCount("Mountain", true, 3);
assertGraveyardCount(playerA, bolt, 3);
assertLife(playerB, 11); // 3 x 3 damage

View file

@ -7,13 +7,14 @@ import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author noxx
*/
public class ExquisiteBloodTest extends CardTestPlayerBase {
@Test
public void BasicCardTest() {
public void basicCardTest() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
@ -45,10 +46,12 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
}
/**
* Ajani, Inspiring leader does not trigger Exquisite Blood + Defiant Bloodlord #6464
* Ajani, Inspiring leader does not trigger Exquisite Blood + Defiant Bloodlord #6464
*/
@Test
public void triggerCascadeTest() {
setStrictChooseMode(true);
// +2: You gain 2 life. Put two +1/+1 counters on up to one target creature.
// 3: Exile target creature. Its controller gains 2 life.
// 10: Creatures you control gain flying and double strike until end of turn.
@ -57,14 +60,12 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
// Flying
// Whenever you gain life, target opponent loses that much life.
addCard(Zone.BATTLEFIELD, playerA, "Defiant Bloodlord", 1); // Creature 4/5 {5}{B}{B}
// Whenever an opponent loses life, you gain that much life.
addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1); // Enchantment {4}{B}
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2:", "Defiant Bloodlord");
addTarget(playerA, playerB); // Target opponent of Defiant Bloodlord triggered ability (looping until opponent is dead)
addTarget(playerA, playerB);
addTarget(playerA, playerB);
addTarget(playerA, playerB);
addTarget(playerA, playerB);
addTarget(playerA, playerB);
@ -72,7 +73,9 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
addTarget(playerA, playerB);
addTarget(playerA, playerB);
addTarget(playerA, playerB);
addTarget(playerA, playerB);
addTarget(playerA, playerB);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
@ -82,15 +85,17 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
assertLife(playerB, 0); // Player B is dead, game ends
assertLife(playerA, 40);
}
/**
* Ajani, Inspiring leader does not trigger Exquisite Blood + Defiant Bloodlord #6464
* Ajani, Inspiring leader does not trigger Exquisite Blood + Defiant Bloodlord #6464
*/
@Test
public void triggerCascadeAjaniSecondAbilityTest() {
setStrictChooseMode(true);
// +2: You gain 2 life. Put two +1/+1 counters on up to one target creature.
// 3: Exile target creature. Its controller gains 2 life.
// 10: Creatures you control gain flying and double strike until end of turn.
@ -100,14 +105,12 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
// Flying
// Whenever you gain life, target opponent loses that much life.
addCard(Zone.BATTLEFIELD, playerA, "Defiant Bloodlord", 1); // Creature 4/5 {5}{B}{B}
// Whenever an opponent loses life, you gain that much life.
addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1); // Enchantment {4}{B}
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-3:", "Silvercoat Lion");
addTarget(playerA, playerB); // Target opponent of Defiant Bloodlord triggered ability (looping until opponent is dead)
addTarget(playerA, playerB);
addTarget(playerA, playerB);
addTarget(playerA, playerB);
addTarget(playerA, playerB);
addTarget(playerA, playerB);
@ -115,7 +118,9 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
addTarget(playerA, playerB);
addTarget(playerA, playerB);
addTarget(playerA, playerB);
addTarget(playerA, playerB);
addTarget(playerA, playerB);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
@ -126,7 +131,28 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
assertLife(playerB, 0); // Player B is dead, game ends
assertLife(playerA, 40);
}
@Test
public void attackWithTwoCreatures() {
setStrictChooseMode(true);
// Whenever an opponent loses life, you gain that much life.
addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1);
addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard");
addCard(Zone.BATTLEFIELD, playerA, "Memnite");
attack(1, playerA, "Elite Vanguard", playerB);
attack(1, playerA, "Memnite", playerB);
// no trigger stacking, only 1 trigger
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLife(playerB, 20 - 2 - 1);
assertLife(playerA, 20 + 3);
}
}

View file

@ -0,0 +1,116 @@
package org.mage.test.cards.single.ice;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class OathOfLimDulTest extends CardTestPlayerBase {
/**
* {@link mage.cards.o.OathOfLimDul Oath of Lim-Dûl} {3}{B}
* Enchantment
* Whenever you lose life, for each 1 life you lost, sacrifice a permanent other than Oath of Lim-Dûl unless you discard a card. (Damage dealt to you causes you to lose life.)
* {B}{B}: Draw a card.
*/
private static final String oath = "Oath of Lim-Dul";
@Test
public void test_3Sacrifice() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, oath, 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.HAND, playerA, "Swamp", 5);
addCard(Zone.HAND, playerA, "Lightning Bolt");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA);
setChoice(playerA, false); // No to discard on first instance.
setChoice(playerA, "Mountain"); // sacrifice Mountain
setChoice(playerA, false); // No to discard on second instance.
setChoice(playerA, "Mountain"); // sacrifice Mountain
setChoice(playerA, false); // No to discard on third instance.
setChoice(playerA, "Mountain"); // sacrifice Mountain
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20 - 3);
assertGraveyardCount(playerA, "Mountain", 3);
}
@Test
public void test_3Discard() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, oath, 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.HAND, playerA, "Swamp", 5);
addCard(Zone.HAND, playerA, "Lightning Bolt");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA);
setChoice(playerA, true); // Yes to discard on first instance.
setChoice(playerA, "Swamp"); // sacrifice Swamp
setChoice(playerA, true); // Yes to discard on second instance.
setChoice(playerA, "Swamp"); // sacrifice Swamp
setChoice(playerA, true); // Yes to discard on third instance.
setChoice(playerA, "Swamp"); // sacrifice Swamp
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20 - 3);
assertGraveyardCount(playerA, "Swamp", 3);
}
@Test
public void test_1Sacrifice1Discard_NoOther() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, oath, 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.HAND, playerA, "Swamp", 1);
addCard(Zone.HAND, playerA, "Lightning Bolt");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA);
setChoice(playerA, true); // Yes to discard on first instance.
setChoice(playerA, "Swamp"); // discard Swamp
// No more possibility to Discard
setChoice(playerA, "Mountain"); // sacrifice Mountain
// No more things to Sacrifice
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20 - 3);
assertGraveyardCount(playerA, "Mountain", 1);
assertGraveyardCount(playerA, "Swamp", 1);
}
@Test
public void test_AllSacrificeNoDiscard_KeepCardInHand() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, oath, 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.HAND, playerA, "Swamp", 1);
addCard(Zone.HAND, playerA, "Lightning Bolt");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA);
setChoice(playerA, false); // No to discard on first instance.
setChoice(playerA, "Mountain"); // sacrifice Mountain
setChoice(playerA, false); // No to discard on second instance.
setChoice(playerA, false); // No to discard on third instance.
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20 - 3);
assertPermanentCount(playerA, oath, 1);
assertGraveyardCount(playerA, "Mountain", 1);
assertHandCount(playerA, "Swamp", 1);
}
}

View file

@ -0,0 +1,79 @@
package org.mage.test.cards.single.snc;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class VampireScrivenerTest extends CardTestPlayerBase {
/**
* {@link mage.cards.v.VampireScrivener Vampire Scrivener} {4}{B}
* Creature Vampire Warlock
* Flying
* Whenever you gain life during your turn, put a +1/+1 counter on Vampire Scrivener.
* Whenever you lose life during your turn, put a +1/+1 counter on Vampire Scrivener.
* 2/2
*/
private static final String scrivener = "Vampire Scrivener";
@Test
public void test_LoseLife_Twice() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, scrivener, 1);
addCard(Zone.BATTLEFIELD, playerA, "Battlefield Forge"); // painland
addCard(Zone.HAND, playerA, "Lightning Bolt");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); // cause 1 trigger
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA); // cause 1 trigger
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20 - 3 - 1);
assertCounterCount(playerA, scrivener, CounterType.P1P1, 2);
}
@Test
public void test_RakdosCharm() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, scrivener, 1);
addCard(Zone.BATTLEFIELD, playerA, "Kobolds of Kher Keep", 3);
addCard(Zone.BATTLEFIELD, playerA, "Badlands", 2);
addCard(Zone.HAND, playerA, "Rakdos Charm");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rakdos Charm");
setModeChoice(playerA, "3"); // Choose third mode
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20 - 4);
assertCounterCount(playerA, scrivener, CounterType.P1P1, 1);
}
@Test
public void test_RakdosCharm_NotYourTurn() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, scrivener, 1);
addCard(Zone.BATTLEFIELD, playerA, "Kobolds of Kher Keep", 3);
addCard(Zone.BATTLEFIELD, playerA, "Badlands", 2);
addCard(Zone.HAND, playerA, "Rakdos Charm");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Rakdos Charm");
setModeChoice(playerA, "3"); // Choose third mode
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20 - 4);
assertCounterCount(playerA, scrivener, CounterType.P1P1, 0); // No trigger, as not your turn.
}
}