mirror of
https://github.com/magefree/mage.git
synced 2025-12-21 19:11:59 -08:00
fix River Song (#12727)
This commit is contained in:
parent
317f536dc9
commit
9fe5d6bd1b
6 changed files with 100 additions and 39 deletions
|
|
@ -6,8 +6,8 @@ import mage.abilities.TriggeredAbility;
|
|||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
import mage.cards.CardImpl;
|
||||
|
|
@ -15,7 +15,6 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DrawCardEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
|
@ -38,7 +37,7 @@ public final class RiverSong extends CardImpl {
|
|||
this.toughness = new MageInt(2);
|
||||
|
||||
// Meet in Reverse -- You draw cards from the bottom of your library rather than the top.
|
||||
this.addAbility(new SimpleStaticAbility(new RiverSongDrawFromBottomReplacementEffect())
|
||||
this.addAbility(new SimpleStaticAbility(new RiverSongDrawFromBottomEffect())
|
||||
.withFlavorWord("Meet in Reverse"));
|
||||
|
||||
// Spoilers -- Whenever an opponent scries, surveils, or searches their library, put a +1/+1 counter on River Song.
|
||||
|
|
@ -59,36 +58,30 @@ public final class RiverSong extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class RiverSongDrawFromBottomReplacementEffect extends ReplacementEffectImpl {
|
||||
class RiverSongDrawFromBottomEffect extends ContinuousEffectImpl {
|
||||
|
||||
RiverSongDrawFromBottomReplacementEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Neutral);
|
||||
staticText = "You draw cards from the bottom of your library rather than the top";
|
||||
RiverSongDrawFromBottomEffect() {
|
||||
super(Duration.WhileOnBattlefield, Layer.RulesEffects, SubLayer.NA, Outcome.Benefit);
|
||||
staticText = "you draw cards from the bottom of your library rather than the top";
|
||||
}
|
||||
|
||||
private RiverSongDrawFromBottomReplacementEffect(final RiverSongDrawFromBottomReplacementEffect effect) {
|
||||
private RiverSongDrawFromBottomEffect(final RiverSongDrawFromBottomEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RiverSongDrawFromBottomReplacementEffect copy() {
|
||||
return new RiverSongDrawFromBottomReplacementEffect(this);
|
||||
public RiverSongDrawFromBottomEffect copy() {
|
||||
return new RiverSongDrawFromBottomEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
((DrawCardEvent) event).setFromBottom(true);
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DRAW_CARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return source.getControllerId().equals(event.getPlayerId());
|
||||
player.setDrawsFromBottom(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -480,4 +480,53 @@ public class DrawEffectsTest extends CardTestPlayerBase {
|
|||
assertLibraryCount(playerA, "Shock", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRiverSongExtended() {
|
||||
skipInitShuffling();
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 3);
|
||||
addCard(Zone.LIBRARY, playerA, "Healing Salve"); // bottom
|
||||
addCard(Zone.LIBRARY, playerA, "Giant Growth");
|
||||
addCard(Zone.LIBRARY, playerA, "Dark Ritual");
|
||||
addCard(Zone.LIBRARY, playerA, "Ornithopter");
|
||||
addCard(Zone.LIBRARY, playerA, "Shock"); // top
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Blood Bairn"); // sac outlet
|
||||
addCard(Zone.HAND, playerA, "River Song");
|
||||
// You draw cards from the bottom of your library rather than the top.
|
||||
|
||||
checkHandCardCount("first draw", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Shock", 1);
|
||||
|
||||
castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "River Song");
|
||||
|
||||
checkHandCardCount("second draw", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Healing Salve", 1);
|
||||
|
||||
checkHandCardCount("third draw", 7, PhaseStep.PRECOMBAT_MAIN, playerA, "Giant Growth", 1);
|
||||
|
||||
activateAbility(7, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sacrifice");
|
||||
setChoice(playerA, "River Song");
|
||||
|
||||
checkHandCardCount("fourth draw", 9, PhaseStep.PRECOMBAT_MAIN, playerA, "Ornithopter", 1);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(9, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertHandCount(playerA, 4);
|
||||
assertLibraryCount(playerA, "Dark Ritual", 1);
|
||||
assertGraveyardCount(playerA, "River Song", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRiverSongLaboratoryManiac() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "River Song");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Laboratory Maniac");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertWonTheGame(playerA);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3847,11 +3847,6 @@ public class TestPlayer implements Player {
|
|||
return computerPlayer.canPlayCardsFromGraveyard();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawsOnOpponentsTurn(boolean drawsOnOpponentsTurn) {
|
||||
computerPlayer.setDrawsOnOpponentsTurn(drawsOnOpponentsTurn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlotFromTopOfLibrary() {
|
||||
return computerPlayer.canPlotFromTopOfLibrary();
|
||||
|
|
@ -3862,6 +3857,21 @@ public class TestPlayer implements Player {
|
|||
computerPlayer.setPlotFromTopOfLibrary(canPlotFromTopOfLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawsFromBottom(boolean drawsFromBottom) {
|
||||
computerPlayer.setDrawsFromBottom(drawsFromBottom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDrawsFromBottom() {
|
||||
return computerPlayer.isDrawsFromBottom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawsOnOpponentsTurn(boolean drawsOnOpponentsTurn) {
|
||||
computerPlayer.setDrawsOnOpponentsTurn(drawsOnOpponentsTurn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDrawsOnOpponentsTurn() {
|
||||
return computerPlayer.isDrawsOnOpponentsTurn();
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ import java.util.UUID;
|
|||
*/
|
||||
public class DrawCardEvent extends GameEvent {
|
||||
|
||||
private boolean fromBottom = false; // for replacement effects that draw from bottom of library instead
|
||||
|
||||
private int cardsDrawn = 0; // for replacement effects to keep track for "cards drawn this way"
|
||||
|
||||
public DrawCardEvent(UUID playerId, Ability source, GameEvent originalDrawEvent) {
|
||||
|
|
@ -27,14 +25,6 @@ public class DrawCardEvent extends GameEvent {
|
|||
}
|
||||
}
|
||||
|
||||
public void setFromBottom(boolean fromBottom) {
|
||||
this.fromBottom = fromBottom;
|
||||
}
|
||||
|
||||
public boolean isFromBottom() {
|
||||
return fromBottom;
|
||||
}
|
||||
|
||||
public void incrementCardsDrawn(int cardsDrawn) {
|
||||
this.cardsDrawn += cardsDrawn;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,6 +204,10 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
|
||||
boolean canPlotFromTopOfLibrary();
|
||||
|
||||
void setDrawsFromBottom(boolean drawsFromBottom);
|
||||
|
||||
boolean isDrawsFromBottom();
|
||||
|
||||
void setDrawsOnOpponentsTurn(boolean drawsOnOpponentsTurn);
|
||||
|
||||
boolean isDrawsOnOpponentsTurn();
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
protected boolean loseByZeroOrLessLife = true;
|
||||
protected boolean canPlayCardsFromGraveyard = true;
|
||||
protected boolean canPlotFromTopOfLibrary = false;
|
||||
protected boolean drawsFromBottom = false;
|
||||
protected boolean drawsOnOpponentsTurn = false;
|
||||
|
||||
protected FilterPermanent sacrificeCostFilter;
|
||||
|
|
@ -253,6 +254,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.loseByZeroOrLessLife = player.loseByZeroOrLessLife;
|
||||
this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard;
|
||||
this.canPlotFromTopOfLibrary = player.canPlotFromTopOfLibrary;
|
||||
this.drawsFromBottom = player.drawsFromBottom;
|
||||
this.drawsOnOpponentsTurn = player.drawsOnOpponentsTurn;
|
||||
|
||||
this.attachments.addAll(player.attachments);
|
||||
|
|
@ -367,6 +369,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife();
|
||||
this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard();
|
||||
this.canPlotFromTopOfLibrary = player.canPlotFromTopOfLibrary();
|
||||
this.drawsFromBottom = player.isDrawsFromBottom();
|
||||
this.drawsOnOpponentsTurn = player.isDrawsOnOpponentsTurn();
|
||||
this.alternativeSourceCosts = CardUtil.deepCopyObject(player.getAlternativeSourceCosts());
|
||||
|
||||
|
|
@ -481,6 +484,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.loseByZeroOrLessLife = true;
|
||||
this.canPlayCardsFromGraveyard = true;
|
||||
this.canPlotFromTopOfLibrary = false;
|
||||
this.drawsFromBottom = false;
|
||||
this.drawsOnOpponentsTurn = false;
|
||||
|
||||
this.sacrificeCostFilter = null;
|
||||
|
|
@ -524,6 +528,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.loseByZeroOrLessLife = true;
|
||||
this.canPlayCardsFromGraveyard = false;
|
||||
this.canPlotFromTopOfLibrary = false;
|
||||
this.drawsFromBottom = false;
|
||||
this.drawsOnOpponentsTurn = false;
|
||||
this.topCardRevealed = false;
|
||||
this.alternativeSourceCosts.clear();
|
||||
|
|
@ -768,7 +773,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
numDrawn += drawCardEvent.getCardsDrawn();
|
||||
continue;
|
||||
}
|
||||
Card card = drawCardEvent.isFromBottom() ? getLibrary().drawFromBottom(game) : getLibrary().drawFromTop(game);
|
||||
Card card = isDrawsFromBottom() ? getLibrary().drawFromBottom(game) : getLibrary().drawFromTop(game);
|
||||
if (card != null) {
|
||||
card.moveToZone(Zone.HAND, source, game, false); // if you want to use event.getSourceId() here then thinks x10 times
|
||||
if (isTopCardRevealed()) {
|
||||
|
|
@ -4661,6 +4666,16 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.canPlotFromTopOfLibrary = canPlotFromTopOfLibrary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawsFromBottom(boolean drawsFromBottom) {
|
||||
this.drawsFromBottom = drawsFromBottom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDrawsFromBottom() {
|
||||
return drawsFromBottom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawsOnOpponentsTurn(boolean drawsOnOpponentsTurn) {
|
||||
this.drawsOnOpponentsTurn = drawsOnOpponentsTurn;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue