Add deterministic ability gain to PermanentImpl.addAbility

This commit is contained in:
jimga150 2025-03-21 18:40:31 -04:00
parent 82d03b46e3
commit e20e648a87
5 changed files with 141 additions and 1 deletions

View file

@ -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}
* <p>
* 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}
* <p>
* {T}: Add {G}.
* 1/1
*/
private static final String llanowarelves = "Llanowar Elves";
/**
* {@link mage.cards.a.AkkiDrillmaster}
* <p>
* {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);
}
}

View file

@ -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}
*/

View file

@ -158,6 +158,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();

View file

@ -444,7 +444,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

View file

@ -488,6 +488,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() {
}