more tests + some fixes

This commit is contained in:
BetaSteward 2012-02-25 23:24:06 -05:00
parent 868d51f924
commit 9ae7ff87ea
15 changed files with 325 additions and 55 deletions

View file

@ -122,7 +122,7 @@ class BackFromTheBrinkCost extends CostImpl<BackFromTheBrinkCost> {
@Override
public boolean canPay(UUID sourceId, UUID controllerId, Game game) {
return targets.canChoose(controllerId, controllerId, game);
return targets.canChoose(controllerId, game);
}
@Override

View file

@ -44,6 +44,8 @@ import mage.players.Player;
import mage.target.common.TargetDiscard;
import java.util.UUID;
import mage.game.events.GameEvent;
import mage.watchers.WatcherImpl;
/**
* @author nantuko
@ -67,7 +69,7 @@ public class CivilizedScholar extends CardImpl<CivilizedScholar> {
this.addAbility(new SimpleActivatedAbility(Constants.Zone.BATTLEFIELD, new CivilizedScholarEffect(), new TapSourceCost()));
this.addAbility(new TransformAbility());
// this.addWatcher(new HomicidalBrute.HomicidalBruteWatcher());
this.addWatcher(new HomicidalBruteWatcher());
}
public CivilizedScholar(final CivilizedScholar card) {
@ -80,6 +82,31 @@ public class CivilizedScholar extends CardImpl<CivilizedScholar> {
}
}
class HomicidalBruteWatcher extends WatcherImpl<HomicidalBruteWatcher> {
public HomicidalBruteWatcher() {
super("HomicidalBruteAttacked", Constants.WatcherScope.CARD);
}
public HomicidalBruteWatcher(final HomicidalBruteWatcher watcher) {
super(watcher);
}
@Override
public HomicidalBruteWatcher copy() {
return new HomicidalBruteWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (condition == true)
return;
if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED && event.getSourceId().equals(sourceId)) {
condition = true;
}
}
}
class CivilizedScholarEffect extends OneShotEffect<CivilizedScholarEffect> {
public CivilizedScholarEffect() {

View file

@ -65,7 +65,6 @@ public class HomicidalBrute extends CardImpl<HomicidalBrute> {
// At the beginning of your end step, if Homicidal Brute didn't attack this turn, tap Homicidal Brute, then transform it.
this.addAbility(new HomicidalBruteTriggeredAbility());
this.addWatcher(new HomicidalBruteWatcher());
}
public HomicidalBrute(final HomicidalBrute card) {
@ -79,31 +78,6 @@ public class HomicidalBrute extends CardImpl<HomicidalBrute> {
}
class HomicidalBruteWatcher extends WatcherImpl<HomicidalBruteWatcher> {
public HomicidalBruteWatcher() {
super("HomicidalBruteAttacked", WatcherScope.CARD);
}
public HomicidalBruteWatcher(final HomicidalBruteWatcher watcher) {
super(watcher);
}
@Override
public HomicidalBruteWatcher copy() {
return new HomicidalBruteWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (condition == true)
return;
if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED && event.getSourceId().equals(sourceId)) {
condition = true;
}
}
}
class HomicidalBruteTriggeredAbility extends TriggeredAbilityImpl<HomicidalBruteTriggeredAbility> {
public HomicidalBruteTriggeredAbility() {
@ -124,7 +98,7 @@ class HomicidalBruteTriggeredAbility extends TriggeredAbilityImpl<HomicidalBrute
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.END_PHASE_PRE && event.getPlayerId().equals(this.controllerId)) {
Watcher watcher = game.getState().getWatchers().get("HomicidalBruteAttacked", sourceId);
if (watcher != null && !watcher.conditionMet()) {
if (watcher == null || !watcher.conditionMet()) {
return true;
}
}

View file

@ -42,6 +42,7 @@ import mage.abilities.common.OnEventTriggeredAbility;
import mage.abilities.common.DiesTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.SacrificeSourceEffect;
@ -73,7 +74,7 @@ public class NecroticPlague extends CardImpl<NecroticPlague> {
this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment));
Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability);
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NecroticPlagueEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NecroticPlagueEffect(this.objectId)));
}
@ -81,21 +82,44 @@ public class NecroticPlague extends CardImpl<NecroticPlague> {
super(card);
}
@Override
public void assignNewId() {
super.assignNewId();
updateSource();
}
@Override
public NecroticPlague copy() {
return new NecroticPlague(this);
}
private void updateSource() {
for (Ability ability: abilities) {
for (Effect effect: ability.getEffects()) {
if (effect instanceof NecroticPlagueEffect) {
((NecroticPlagueEffect)effect).updateSource(objectId);
}
}
}
}
}
class NecroticPlagueEffect extends ContinuousEffectImpl<NecroticPlagueEffect> {
public NecroticPlagueEffect() {
private Ability ability1;
private Ability ability2;
public NecroticPlagueEffect(UUID cardId) {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
ability1 = new OnEventTriggeredAbility(EventType.UPKEEP_STEP_PRE, "beginning of your upkeep", new SacrificeSourceEffect());
ability2 = new DiesTriggeredAbility(new NecroticPlagueEffect2(cardId), false);
staticText = "Enchanted creature has \"At the beginning of your upkeep, sacrifice this creature.\" When enchanted creature is put into a graveyard, its controller chooses target creature one of his or her opponents controls. Return {this} from its owner's graveyard to the battlefield attached to that creature.";
}
public NecroticPlagueEffect(final NecroticPlagueEffect effect) {
super(effect);
this.ability1 = effect.ability1.copy();
this.ability2 = effect.ability2.copy();
}
@Override
@ -112,8 +136,8 @@ class NecroticPlagueEffect extends ContinuousEffectImpl<NecroticPlagueEffect> {
switch (layer) {
case AbilityAddingRemovingEffects_6:
if (sublayer == SubLayer.NA) {
creature.addAbility(new OnEventTriggeredAbility(EventType.UPKEEP_STEP_PRE, "beginning of your upkeep", new SacrificeSourceEffect()), game);
creature.addAbility(new DiesTriggeredAbility(new NecroticPlagueEffect2(source.getSourceId()), false), game);
creature.addAbility(ability1, game);
creature.addAbility(ability2, game);
}
break;
}
@ -123,6 +147,14 @@ class NecroticPlagueEffect extends ContinuousEffectImpl<NecroticPlagueEffect> {
return false;
}
public void updateSource(UUID id) {
for (Effect effect: ability2.getEffects()) {
if (effect instanceof NecroticPlagueEffect2) {
((NecroticPlagueEffect2)effect).updateSource(id);
}
}
}
@Override
public boolean apply(Game game, Ability source) {
return false;
@ -137,14 +169,14 @@ class NecroticPlagueEffect extends ContinuousEffectImpl<NecroticPlagueEffect> {
class NecroticPlagueEffect2 extends OneShotEffect<NecroticPlagueEffect2> {
private UUID cardId;
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls");
static {
filter.setTargetController(TargetController.OPPONENT);
}
protected UUID cardId;
public NecroticPlagueEffect2(UUID cardId) {
super(Outcome.PutCardInPlay);
this.cardId = cardId;
@ -177,6 +209,10 @@ class NecroticPlagueEffect2 extends OneShotEffect<NecroticPlagueEffect2> {
return false;
}
public void updateSource(UUID id) {
this.cardId = id;
}
@Override
public NecroticPlagueEffect2 copy() {
return new NecroticPlagueEffect2(this);

View file

@ -12,15 +12,75 @@ public class TestHomicidalBrute extends CardTestPlayerBase {
@Test
public void testCard() {
removeAllCardsFromHand(playerA);
removeAllCardsFromLibrary(playerA);
addCard(Constants.Zone.BATTLEFIELD, playerA, "Civilized Scholar");
addCard(Constants.Zone.LIBRARY, playerA, "Sejiri Merfolk");
setStopAt(2, Constants.PhaseStep.BEGIN_COMBAT);
activateAbility(1, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw a card, then discard a card. ");
setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20);
assertLife(playerB, 20);
assertPermanentCount(playerA, "Civilized Scholar", 0);
assertPermanentCount(playerA, "Homicidal Brute", 1);
assertTapped("Homicidal Brute", false);
}
@Test
public void testCardNegative() {
removeAllCardsFromHand(playerA);
removeAllCardsFromLibrary(playerA);
addCard(Constants.Zone.BATTLEFIELD, playerA, "Civilized Scholar");
addCard(Constants.Zone.LIBRARY, playerA, "Lightning Bolt");
activateAbility(1, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw a card, then discard a card. ");
setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20);
assertLife(playerB, 20);
assertPermanentCount(playerA, "Civilized Scholar", 1);
assertTapped("Civilized Scholar", true);
assertPermanentCount(playerA, "Homicidal Brute", 0);
}
@Test
public void testCardTransform() {
removeAllCardsFromHand(playerA);
removeAllCardsFromLibrary(playerA);
addCard(Constants.Zone.BATTLEFIELD, playerA, "Civilized Scholar");
addCard(Constants.Zone.LIBRARY, playerA, "Sejiri Merfolk");
activateAbility(1, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw a card, then discard a card. ");
setStopAt(2, Constants.PhaseStep.UPKEEP);
execute();
assertLife(playerA, 20);
assertLife(playerB, 20);
assertPermanentCount(playerA, "Civilized Scholar", 1);
assertTapped("Civilized Scholar", true);
assertPermanentCount(playerA, "Homicidal Brute", 0);
}
@Test
public void testCardNotTransform() {
removeAllCardsFromHand(playerA);
removeAllCardsFromLibrary(playerA);
addCard(Constants.Zone.BATTLEFIELD, playerA, "Civilized Scholar");
addCard(Constants.Zone.LIBRARY, playerA, "Sejiri Merfolk", 2);
activateAbility(3, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw a card, then discard a card. ");
attack(3, playerA, "Homicidal Brute");
setStopAt(4, Constants.PhaseStep.UPKEEP);
execute();
assertLife(playerA, 20);
assertLife(playerB, 15);
assertPermanentCount(playerA, "Civilized Scholar", 0);
assertPermanentCount(playerA, "Homicidal Brute", 1);
assertTapped("Homicidal Brute", true);
}
}

View file

@ -0,0 +1,59 @@
package org.mage.test.cards;
import mage.Constants;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* also tests triggered abilities that switch from one permanent to another
*
* @author BetaSteward
*
*/
public class TestNecroticPlague extends CardTestPlayerBase {
@Test
public void testCard1() {
addCard(Constants.Zone.BATTLEFIELD, playerA, "Swamp", 4);
addCard(Constants.Zone.HAND, playerA, "Necrotic Plague");
addCard(Constants.Zone.BATTLEFIELD, playerB, "Sejiri Merfolk");
castSpell(1, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "Necrotic Plague", "Sejiri Merfolk");
setStopAt(2, Constants.PhaseStep.PRECOMBAT_MAIN);
execute();
assertLife(playerA, 20);
assertLife(playerB, 20);
assertPermanentCount(playerB, "Sejiri Merfolk", 0);
assertGraveyardCount(playerA, "Necrotic Plague", 1);
assertGraveyardCount(playerB, 1);
assertGraveyardCount(playerB, "Sejiri Merfolk", 1);
}
@Test
public void testCard2() {
addCard(Constants.Zone.BATTLEFIELD, playerA, "Swamp", 4);
addCard(Constants.Zone.BATTLEFIELD, playerA, "Goblin Deathraiders");
addCard(Constants.Zone.HAND, playerA, "Necrotic Plague");
addCard(Constants.Zone.BATTLEFIELD, playerB, "Sejiri Merfolk");
castSpell(1, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "Necrotic Plague", "Sejiri Merfolk");
setStopAt(3, Constants.PhaseStep.PRECOMBAT_MAIN);
execute();
assertLife(playerA, 20);
assertLife(playerB, 20);
assertPermanentCount(playerA, "Goblin Deathraiders", 0);
assertPermanentCount(playerB, "Sejiri Merfolk", 0);
assertGraveyardCount(playerA, 2);
assertGraveyardCount(playerA, "Necrotic Plague", 1);
assertGraveyardCount(playerA, "Goblin Deathraiders", 1);
assertGraveyardCount(playerB, 1);
assertGraveyardCount(playerB, "Sejiri Merfolk", 1);
}
}

View file

@ -0,0 +1,61 @@
package org.mage.test.cards;
import mage.Constants;
import mage.counters.CounterType;
import mage.filter.Filter;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* also tests triggered abilities that are added to permanents
*
* @author BetaSteward
*/
public class TestTurnToFrog extends CardTestPlayerBase {
@Test
public void testCard() {
addCard(Constants.Zone.BATTLEFIELD, playerA, "Mountain", 3);
addCard(Constants.Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Constants.Zone.BATTLEFIELD, playerA, "Raging Ravine");
addCard(Constants.Zone.BATTLEFIELD, playerB, "Island", 2);
addCard(Constants.Zone.HAND, playerB, "Turn to Frog");
activateAbility(1, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}{G}: until end of turn {this} becomes a 3/3 red and green Elemental creature with \"Whenever this creature attacks, put a +1/+1 counter on it.\" that's still a land. ");
castSpell(1, Constants.PhaseStep.PRECOMBAT_MAIN, playerB, "Turn to Frog", "Raging Ravine");
attack(1, playerA, "Raging Ravine");
setStopAt(1, Constants.PhaseStep.END_COMBAT);
execute();
assertLife(playerA, 20);
assertLife(playerB, 19);
assertPowerToughness(playerA, "Raging Ravine", 1, 1, Filter.ComparisonScope.Any);
assertCounterCount("Raging Ravine", CounterType.P1P1, 0);
}
@Test
public void testCard2() {
addCard(Constants.Zone.BATTLEFIELD, playerA, "Mountain", 3);
addCard(Constants.Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Constants.Zone.BATTLEFIELD, playerA, "Raging Ravine");
addCard(Constants.Zone.BATTLEFIELD, playerB, "Island", 2);
addCard(Constants.Zone.HAND, playerB, "Turn to Frog");
activateAbility(1, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}{G}: until end of turn {this} becomes a 3/3 red and green Elemental creature with \"Whenever this creature attacks, put a +1/+1 counter on it.\" that's still a land. ");
castSpell(1, Constants.PhaseStep.PRECOMBAT_MAIN, playerB, "Turn to Frog", "Raging Ravine");
activateAbility(3, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{R}{G}: until end of turn {this} becomes a 3/3 red and green Elemental creature with \"Whenever this creature attacks, put a +1/+1 counter on it.\" that's still a land. ");
attack(3, playerA, "Raging Ravine");
setStopAt(3, Constants.PhaseStep.END_COMBAT);
execute();
assertLife(playerA, 20);
assertLife(playerB, 16);
assertPowerToughness(playerA, "Raging Ravine", 4, 4, Filter.ComparisonScope.Any);
assertCounterCount("Raging Ravine", CounterType.P1P1, 1);
}
}

View file

@ -29,6 +29,7 @@ public class TestPlayRandomGame extends MageTestBase {
private static List<String> colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu");
@Test
@Ignore
public void playGames() throws GameException, FileNotFoundException {
for (int i = 1; i < 100; i++) {
logger.info("Playing game: " + i);

View file

@ -82,6 +82,20 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
}
/**
* Removes all cards from player's hand from the game.
* Usually this should be used once before initialization to set the players hand.
*
* @param player {@link Player} to remove all cards from hand.
*/
public void removeAllCardsFromHand(Player player) {
if (player.equals(playerA)) {
commandsA.put(Constants.Zone.HAND, "clear");
} else if (player.equals(playerB)) {
commandsB.put(Constants.Zone.HAND, "clear");
}
}
/**
* Add a card to specified zone of specified player.
*
* @param gameZone {@link Constants.Zone} to add cards to.
@ -361,16 +375,38 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param count Expected count.
*/
public void assertCounterCount(String cardName, CounterType type, int count) throws AssertionError {
int actualCount = 0;
for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) {
if (permanent.getName().equals(cardName)) {
actualCount += permanent.getCounters().getCount(type);
}
Permanent found = null;
for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents()) {
if (permanent.getName().equals(cardName)) {
found = permanent;
}
}
Assert.assertEquals("(Battlefield) Counter counts are not equal (" + cardName + ":" + type + ")", count, actualCount);
Assert.assertNotNull("There is no such permanent on the battlefield, cardName=" + cardName, found);
Assert.assertEquals("(Battlefield) Counter counts are not equal (" + cardName + ":" + type + ")", count, found.getCounters().getCount(type));
}
/**
* Assert whether a permanent is tapped or not
*
* @param cardName Name of the permanent that should be checked.
* @param tapped Whether the permanent is tapped or not
*/
public void assertTapped(String cardName, boolean tapped) throws AssertionError {
Permanent found = null;
for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents()) {
if (permanent.getName().equals(cardName)) {
found = permanent;
}
}
Assert.assertNotNull("There is no such permanent on the battlefield, cardName=" + cardName, found);
Assert.assertEquals("(Battlefield) Tapped state is not equal (" + cardName + ")", tapped, found.isTapped());
}
/**
* Assert card count in player's hand.
*
* @param player {@link Player} who's hand should be counted.
@ -442,6 +478,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
player.addAction(turnNum, step, "activate:Cast " + cardName + ";target=" + targetName);
}
public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability) {
player.addAction(turnNum, step, "activate:" + ability);
}
public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, Player target) {
player.addAction(turnNum, step, "activate:" + ability + ";target=" + target.getName());
}

View file

@ -32,9 +32,9 @@ import java.util.UUID;
import mage.Constants.AbilityType;
import mage.Constants.TimingRule;
import mage.Constants.Zone;
import mage.MageObject;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.PhyrexianManaCost;
import mage.abilities.effects.Effect;
@ -176,7 +176,11 @@ public abstract class ActivatedAbilityImpl<T extends ActivatedAbilityImpl<T>> ex
protected String getMessageText(Game game) {
StringBuilder sb = new StringBuilder();
sb.append(game.getObject(this.sourceId).getName());
MageObject object = game.getObject(this.sourceId);
if (object != null)
sb.append(object.getName());
else
sb.append("unknown");
if (getTargets().size() > 0) {
sb.append(" targeting ");
for (Target target: getTargets()) {

View file

@ -28,9 +28,9 @@
package mage.abilities;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.Constants;
import mage.Constants.Zone;
import mage.MageObject;
import mage.game.Game;
import mage.game.events.GameEvent;
@ -39,18 +39,22 @@ import mage.game.events.GameEvent;
*
* @author BetaSteward_at_googlemail.com
*/
public class TriggeredAbilities extends AbilitiesImpl<TriggeredAbility> {
public class TriggeredAbilities extends HashMap<UUID, TriggeredAbility> {
public TriggeredAbilities() {}
public TriggeredAbilities(final TriggeredAbilities abilities) {
super(abilities);
for (Map.Entry<UUID, TriggeredAbility> entry: abilities.entrySet()) {
this.put(entry.getKey(), entry.getValue().copy());
}
}
public void checkTriggers(GameEvent event, Game game) {
for (TriggeredAbility ability: this) {
for (TriggeredAbility ability: this.values()) {
if (ability.isInUseableZone(game)) {
MageObject object = game.getObject(ability.getSourceId());
MageObject object = game.getLastKnownInformation(ability.getSourceId(), event.getZone());
if (object == null)
object = game.getObject(ability.getSourceId());
if (object != null && object.getAbilities().contains(ability)) {
if (ability.checkTrigger(event, game)) {
ability.trigger(game, ability.getControllerId());
@ -59,8 +63,11 @@ public class TriggeredAbilities extends AbilitiesImpl<TriggeredAbility> {
}
}
}
public void add(TriggeredAbility ability) {
this.put(ability.getId(), ability);
}
@Override
public TriggeredAbilities copy() {
return new TriggeredAbilities(this);
}

View file

@ -79,7 +79,7 @@ public class ExileFromGraveCost extends CostImpl<ExileFromGraveCost> {
@Override
public boolean canPay(UUID sourceId, UUID controllerId, Game game) {
return targets.canChoose(controllerId, controllerId, game);
return targets.canChoose(controllerId, game);
}
@Override

View file

@ -71,7 +71,7 @@ public class ReturnToHandTargetCost extends CostImpl<ReturnToHandTargetCost> {
@Override
public boolean canPay(UUID sourceId, UUID controllerId, Game game) {
return targets.canChoose(controllerId, controllerId, game);
return targets.canChoose(controllerId, game);
}
@Override

View file

@ -74,7 +74,7 @@ public class SacrificeTargetCost extends CostImpl<SacrificeTargetCost> {
@Override
public boolean canPay(UUID sourceId, UUID controllerId, Game game) {
return targets.canChoose(sourceId, controllerId, game);
return targets.canChoose(controllerId, game);
}
@Override

View file

@ -96,6 +96,7 @@ public class PermanentCard extends PermanentImpl<PermanentCard> {
this.abilities.clear();
this.abilities.addAll(card.getAbilities());
this.abilities.setControllerId(this.controllerId);
this.watchers.addAll(card.getWatchers());
this.cardType.clear();
this.cardType.addAll(card.getCardType());
this.color = card.getColor().copy();