diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/LifelinkInCombatTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/LifelinkInCombatTest.java new file mode 100644 index 00000000000..bd4a7bc4d8b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/combat/LifelinkInCombatTest.java @@ -0,0 +1,161 @@ +package org.mage.test.combat; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class LifelinkInCombatTest extends CardTestPlayerBase { + @Test + public void testOneBlockerTrample() { + // 4/4 Trample Lifelink + addCard(Zone.BATTLEFIELD, playerA, "Elderwood Scion"); + // Whenever you gain life, put a +1/+1 counter on target creature or enchantment you control. + addCard(Zone.BATTLEFIELD, playerA, "Heliod, Sun-Crowned"); + // 2/2 + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears"); + + attack(1, playerA, "Elderwood Scion"); + block(1, playerB, "Grizzly Bears", "Elderwood Scion"); + setChoice(playerA, "X=2"); // Damage to Grizzly Bears + addTarget(playerA, "Elderwood Scion"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Elderwood Scion", 5, 5); + assertLife(playerA, 24); + assertLife(playerB, 18); + } + + + @Test + public void testTwoBlockers() { + // 4/4 Lifelink + addCard(Zone.BATTLEFIELD, playerA, "Brion Stoutarm"); + // Whenever you gain life, put a +1/+1 counter on target creature or enchantment you control. + addCard(Zone.BATTLEFIELD, playerA, "Heliod, Sun-Crowned"); + // 1/1 + addCard(Zone.BATTLEFIELD, playerB, "Boros Recruit", 2); + addCard(Zone.BATTLEFIELD, playerB, "Suntail Hawk", 1); + + attack(1, playerA, "Brion Stoutarm"); + block(1, playerB, "Boros Recruit", "Brion Stoutarm"); + block(1, playerB, "Suntail Hawk", "Brion Stoutarm"); + setChoice(playerA, "X=1"); // Damage assignment + setChoice(playerA, "X=1"); // Damage assignment + addTarget(playerA, "Brion Stoutarm"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Brion Stoutarm", 5, 5); + assertLife(playerA, 24); + assertLife(playerB, 20); + } + + + @Test + public void testTwoBlockersTrample() { + // 4/4 Trample Lifelink + addCard(Zone.BATTLEFIELD, playerA, "Elderwood Scion"); + addCard(Zone.BATTLEFIELD, playerA, "Heliod, Sun-Crowned"); + // 1/1 + addCard(Zone.BATTLEFIELD, playerB, "Boros Recruit", 1); + addCard(Zone.BATTLEFIELD, playerB, "Suntail Hawk", 1); + + attack(1, playerA, "Elderwood Scion"); + block(1, playerB, "Boros Recruit", "Elderwood Scion"); + block(1, playerB, "Suntail Hawk", "Elderwood Scion"); + setChoice(playerA, "X=1"); // Damage assignment + setChoice(playerA, "X=1"); // Damage assignment + addTarget(playerA, "Elderwood Scion"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Elderwood Scion", 5, 5); + assertLife(playerA, 24); + assertLife(playerB, 18); + } + + @Test + public void testDamagePrevented() { + // 2/2 Lifelink + addCard(Zone.BATTLEFIELD, playerA, "Ajani's Sunstriker"); + // Whenever you gain life, put a +1/+1 counter on target creature or enchantment you control. + addCard(Zone.BATTLEFIELD, playerA, "Heliod, Sun-Crowned"); + // 1/1 Protection from creatures + addCard(Zone.BATTLEFIELD, playerB, "Beloved Chaplain"); + + attack(1, playerA, "Ajani's Sunstriker"); + block(1, playerB, "Beloved Chaplain", "Ajani's Sunstriker"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Ajani's Sunstriker", 2, 2); + assertLife(playerA, 20); + assertLife(playerB, 20); + } + + @Test + public void testSomeDamagePreventedTrample() { + // 4/4 Lifelink Trample + addCard(Zone.BATTLEFIELD, playerA, "Elderwood Scion"); + // Whenever you gain life, put a +1/+1 counter on target creature or enchantment you control. + addCard(Zone.BATTLEFIELD, playerA, "Heliod, Sun-Crowned"); + // 1/1 Protection from creatures + addCard(Zone.BATTLEFIELD, playerB, "Beloved Chaplain"); + + attack(1, playerA, "Elderwood Scion"); + block(1, playerB, "Beloved Chaplain", "Elderwood Scion"); + setChoice(playerA, "X=1"); + addTarget(playerA, "Elderwood Scion"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Elderwood Scion", 5, 5); + assertLife(playerA, 23); + assertLife(playerB, 17); + } + + @Test + public void testPlayerDamagePrevented() { + // 4/4 Lifelink Trample + addCard(Zone.BATTLEFIELD, playerA, "Elderwood Scion"); + // Whenever you gain life, put a +1/+1 counter on target creature or enchantment you control. + addCard(Zone.BATTLEFIELD, playerA, "Heliod, Sun-Crowned"); + // 3/3 + addCard(Zone.HAND, playerB, "Seht's Tiger"); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 4); + + castSpell(1, PhaseStep.UPKEEP, playerB, "Seht's Tiger"); + setChoice(playerB, "Green"); + + attack(1, playerA, "Elderwood Scion"); + block(1, playerB, "Seht's Tiger", "Elderwood Scion"); + setChoice(playerA, "X=3"); + addTarget(playerA, "Elderwood Scion"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertPowerToughness(playerA, "Elderwood Scion", 5, 5); + assertLife(playerA, 23); + assertLife(playerB, 20); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index 6102332e9ee..da45f0341bb 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -132,6 +132,8 @@ public interface Permanent extends Card, Controllable { */ int markDamage(int damage, UUID sourceId, Game game, boolean preventable, boolean combat); + void markLifelink(int damage); + int applyDamage(Game game); void removeAllDamage(Game game); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 7c20cb36c8f..16073d3c834 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -97,6 +97,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { protected List bandedCards = new ArrayList<>(); protected Counters counters; protected List markedDamage; + protected int markedLifelink; protected int timesLoyaltyUsed = 0; protected Map info; protected int createOrder; @@ -133,6 +134,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { this.blocking = permanent.blocking; this.maxBlocks = permanent.maxBlocks; this.deathtouched = permanent.deathtouched; + this.markedLifelink = permanent.markedLifelink; for (Map.Entry> entry : permanent.connectedCards.entrySet()) { this.connectedCards.put(entry.getKey(), entry.getValue()); @@ -886,8 +888,12 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } if (source != null && sourceAbilities != null) { if (sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) { - Player player = game.getPlayer(sourceControllerId); - player.gainLife(damageDone, game, sourceId); + if (markDamage) { + game.getPermanent(sourceId).markLifelink(damageDone); + } else { + Player player = game.getPlayer(sourceControllerId); + player.gainLife(damageDone, game, sourceId); + } } if (sourceAbilities.containsKey(DeathtouchAbility.getInstance().getId())) { deathtouched = true; @@ -912,6 +918,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return damageDone; } + @Override + public void markLifelink(int damage) { + markedLifelink += damage; + } + @Override public int markDamage(int damageAmount, UUID sourceId, Game game, boolean preventable, boolean combat) { return damage(damageAmount, sourceId, game, preventable, combat, true, null); @@ -919,6 +930,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { @Override public int applyDamage(Game game) { + if (markedLifelink > 0) { + Player player = game.getPlayer(this.getControllerId()); + player.gainLife(markedLifelink, game, this.getId()); + markedLifelink = 0; + } if (markedDamage == null) { return 0; } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 942826f2bb6..520800392d6 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2161,8 +2161,12 @@ public abstract class PlayerImpl implements Player, Serializable { } } if (sourceAbilities != null && sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) { - Player player = game.getPlayer(sourceControllerId); - player.gainLife(actualDamage, game, sourceId); + if (combatDamage) { + game.getPermanent(sourceId).markLifelink(actualDamage); + } else { + Player player = game.getPlayer(sourceControllerId); + player.gainLife(actualDamage, game, sourceId); + } } // Unstable ability - Earl of Squirrel if (sourceAbilities != null && sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) {