add thrumming stone and surging dementia

This commit is contained in:
klayhamn 2015-07-26 19:30:15 +03:00
parent 72b3c1e2b3
commit 4d798e81b8
2 changed files with 230 additions and 0 deletions

View file

@ -0,0 +1,112 @@
package org.mage.test.cards.abilities.keywords;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author klayhamn
*/
public class RippleTest extends CardTestPlayerBase {
/**
* 702.59.Ripple
* 702.59a Ripple is a triggered ability that functions only while the card with ripple is on the stack. Ripple N means
* When you cast this spell, you may reveal the top N cards of your library, or, if there are fewer than N cards in your
* library, you may reveal all the cards in your library. If you reveal cards from your library this way, you may cast any
* of those cards with the same name as this spell without paying their mana costs, then put all revealed cards not cast
* this way on the bottom of your library in any order.
* 702.59b If a spell has multiple instances of ripple, each triggers separately.
*/
@Test
public void testRippleWhenSameCardNotFound() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
addCard(Zone.HAND, playerA, "Surging Dementia",2 );
addCard(Zone.LIBRARY, playerA, "Swamp", 4);
addCard(Zone.HAND, playerB, "Island", 3);
addCard(Zone.LIBRARY, playerB, "Island", 3);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Surging Dementia", playerB);
setChoice(playerA, "Yes");
setStopAt(2, PhaseStep.END_TURN);
execute();
assertHandCount(playerB, 3); // should have 2 less
assertHandCount(playerA, 1); // after cast, one remains
assertGraveyardCount(playerA, "Surging Dementia", 1);
assertGraveyardCount(playerB, "Island", 1);
}
@Test
public void testRippleWhenSameCardFoundOnce() {
removeAllCardsFromLibrary(playerA);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
addCard(Zone.HAND, playerA, "Surging Dementia",2 );
addCard(Zone.LIBRARY, playerA, "Surging Dementia",1);
addCard(Zone.LIBRARY, playerA, "Swamp", 3);
addCard(Zone.HAND, playerB, "Island", 3);
addCard(Zone.LIBRARY, playerB, "Island", 3);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Surging Dementia", playerB);
setChoice(playerA, "Yes");
addTarget(playerA, playerB);
setStopAt(2, PhaseStep.END_TURN);
execute();
assertHandCount(playerB, 2); // should have 2 less
assertHandCount(playerA, 1); // after cast, none remain
assertGraveyardCount(playerA, "Surging Dementia", 2);
assertGraveyardCount(playerB, "Island", 2);
}
@Test
public void testRippleWhenSameCardFoundMoreThanOnce() {
removeAllCardsFromLibrary(playerA);
skipInitShuffling();
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
addCard(Zone.HAND, playerA, "Surging Dementia",2 );
addCard(Zone.LIBRARY, playerA, "Surging Dementia",1);
addCard(Zone.LIBRARY, playerA, "Swamp", 2);
addCard(Zone.LIBRARY, playerA, "Surging Dementia",1);
addCard(Zone.LIBRARY, playerA, "Swamp", 2);
addCard(Zone.HAND, playerB, "Island", 3);
addCard(Zone.LIBRARY, playerB, "Island", 3);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Surging Dementia", playerB);
setChoice(playerA, "Yes");
addTarget(playerA, playerB);
setStopAt(2, PhaseStep.END_TURN);
execute();
assertHandCount(playerB, 1); // should have 2 less
assertHandCount(playerA, 1); // after cast, none remain
assertGraveyardCount(playerA, "Surging Dementia", 3);
assertGraveyardCount(playerB, "Island", 3);
}
}

View file

@ -0,0 +1,118 @@
package mage.abilities.effects.keyword;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.TargetCard;
import mage.util.CardUtil;
import java.util.UUID;
/**
* @author klayhamn
*/
public class RippleEffect extends OneShotEffect {
protected int rippleNumber;
protected boolean isTargetSelf; // is the source of the ripple also the target of the ripple?
public RippleEffect(int rippleNumber) {
this(rippleNumber, true); // by default, the source is also the target
}
public RippleEffect(int rippleNumber, boolean isTargetSelf) {
super(Outcome.PlayForFree);
this.rippleNumber = rippleNumber;
this.isTargetSelf = isTargetSelf;
this.setText();
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source.getSourceId());
if (player != null) {
if (!player.chooseUse(Outcome.Neutral, "Reveal "+ rippleNumber + " cards from the top of your library?", source, game )){
return false; //fizzle
}
Cards cards = new CardsImpl();
int count = Math.min(rippleNumber, player.getLibrary().size());
if (count == 0) {
return true;
}
player.revealCards(sourceObject.getIdName(), cards, game);
for (int i = 0; i < count; i++) {
Card card = player.getLibrary().removeFromTop(game);
cards.add(card);
}
// Select cards with the same name as the spell on which the ripple effect applies
// FIXME: I'm not sure the "isTargetSelf" flag is the most elegant solution
String cardNameToRipple;
if (isTargetSelf) { // if the ripple applies to the same card that triggered it
cardNameToRipple = sourceObject.getName();
} else { // if the ripple is caused by something else (e.g. Thrumming Stone)
if (targetPointer == null) {
return true; // this might be possible if the "rememberSource" param wasn't used (which would constitute a bug)
}
UUID triggeringSpellID = targetPointer.getFirst(game, source);
Spell spellOnStack = game.getStack().getSpell(triggeringSpellID);
if (spellOnStack == null) {
return true; // spell was countered or exiled, effect should fizzle
}
cardNameToRipple = spellOnStack.getName();
}
FilterCard sameNameFilter = new FilterCard("card(s) with the name: \"" + cardNameToRipple + "\" to cast without paying their mana cost");
sameNameFilter.add(new NamePredicate(cardNameToRipple));
TargetCard target1 = new TargetCard(Zone.LIBRARY, sameNameFilter);
target1.setRequired(false);
// Choose cards to play for free
while (player.isInGame() && cards.count(sameNameFilter, game) > 0 && player.choose(Outcome.PlayForFree, cards, target1, game)) {
Card card = cards.get(target1.getFirstTarget(), game);
if (card != null) {
player.cast(card.getSpellAbility(), game, true);
cards.remove(card);
}
target1.clearChosen();
}
// move cards that weren't cast to the bottom of the library
player.putCardsOnBottomOfLibrary(cards, game, source, true);
// do we need to fire an event here? there is nothing that listens to ripple so far...
return true;
}
return false;
}
public RippleEffect(final RippleEffect effect) {
super(effect);
this.rippleNumber = effect.rippleNumber;
this.isTargetSelf = effect.isTargetSelf;
}
@Override
public RippleEffect copy() {
return new RippleEffect(this);
}
private void setText() {
StringBuilder sb = new StringBuilder("Ripple ").append(rippleNumber);
sb.append(". <i>(You may reveal the top ");
sb.append(CardUtil.numberToText(rippleNumber));
sb.append(" cards of your library. You may cast any revealed cards with the same name as this spell without paying their mana costs. Put the rest on the bottom of your library.)</i>");
staticText = sb.toString();
}
}