diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mat/TyvarTheBellicoseTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mat/TyvarTheBellicoseTest.java new file mode 100644 index 00000000000..e3bdd61e12e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mat/TyvarTheBellicoseTest.java @@ -0,0 +1,104 @@ +package org.mage.test.cards.single.mat; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author jimga150 + */ +public class TyvarTheBellicoseTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TyvarTheBellicose} + * Tyvar the Bellicose + * {2}{B}{G} + * Legendary Creature — Elf Warrior + * Whenever one or more Elves you control attack, they gain deathtouch until end of turn. + * Each creature you control has “Whenever a mana ability of this creature resolves, put a number of +1/+1 + * counters on it equal to the amount of mana this creature produced. This ability triggers only once each turn.” + * 5/4 + */ + private static final String tyvar = "Tyvar the Bellicose"; + + /** + * {@link mage.cards.e.EnduringVitality} + *
+ * Creatures you control have “{T}: Add one mana of any color.” + * 3/3 + */ + private static final String enduringvitality = "Enduring Vitality"; + + /** + * {@link mage.cards.l.LlanowarElves} + *
+ * {T}: Add {G}. + * 1/1 + */ + private static final String llanowarelves = "Llanowar Elves"; + + /** + * {@link mage.cards.a.AkkiDrillmaster} + *
+ * {T}: Target creature gains haste until end of turn. + * 2/2 + */ + private static final String akkidrillmaster = "Akki Drillmaster"; + + private static final String memnite = "Memnite"; // vanilla 1/1 + + @Test + public void testPrintedAbility() { + + addCard(Zone.BATTLEFIELD, playerA, llanowarelves); + addCard(Zone.BATTLEFIELD, playerA, tyvar); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, llanowarelves, 2, 2); + } + + @Test + public void testNonManaAbility() { + + addCard(Zone.BATTLEFIELD, playerA, akkidrillmaster); + addCard(Zone.BATTLEFIELD, playerA, tyvar); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + addTarget(playerA, tyvar); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, akkidrillmaster, 2, 2); + } + + @Test + public void testGainedAbility() { + + addCard(Zone.BATTLEFIELD, playerA, enduringvitality); + addCard(Zone.BATTLEFIELD, playerA, memnite); + addCard(Zone.BATTLEFIELD, playerA, tyvar); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + setChoice(playerA, "Green"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + setChoice(playerA, "Green"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + setChoice(playerA, "Green"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, enduringvitality, 4, 4); + assertPowerToughness(playerA, memnite, 2, 2); + assertPowerToughness(playerA, tyvar, 6, 5); + } +} diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index c05feea8548..aaecc0d44e7 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -38,6 +38,11 @@ public interface Ability extends Controllable, Serializable { */ void newId(); + /** + * Assigns a new {@link java.util.UUID} based on the input String. Will always generate the same UUID from a given String. + */ + void newDeterministicId(String string); + /** * Assigns a new {@link java.util.UUID} */ diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index af9c1ec624e..8deb8838921 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -161,6 +161,18 @@ public abstract class AbilityImpl implements Ability { } } + @Override + public void newDeterministicId(String string) { + if (!(this instanceof MageSingleton)) { + this.id = UUID.nameUUIDFromBytes(string.getBytes()); + } + getEffects().newId(); + + for (Ability sub : getSubAbilities()) { + sub.newId(); + } + } + @Override public void newOriginalId() { this.id = UUID.randomUUID(); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 1b096877cdc..b7397ccd111 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -453,7 +453,19 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { // other abilities -- any amount of instances if (!abilities.containsKey(ability.getId())) { Ability copyAbility = ability.copy(); - copyAbility.newId(); // needed so that source can get an ability multiple times (e.g. Raging Ravine) + + // needed so that source can get an ability multiple times (e.g. Raging Ravine) + // Generate deterministically from sourceId, ability ID, and Permanent's ID + // so that future calls to addAbility will always give the resulting ability the same ID + String seedString = getId().toString(); + if (sourceId != null) { + seedString += sourceId.toString(); + } + if (ability.getId() != null) { + seedString += ability.getId().toString(); + } + copyAbility.newDeterministicId(seedString); + copyAbility.setControllerId(controllerId); copyAbility.setSourceId(objectId); // triggered abilities must be added to the state().triggers diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index 66211b60147..4a358703e25 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -498,6 +498,13 @@ public class StackAbility extends StackObjectImpl implements Ability { } } + @Override + public void newDeterministicId(String string) { + if (!(this instanceof MageSingleton)) { + this.ability.newDeterministicId(string); + } + } + @Override public void newOriginalId() { }