Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework drawing cards, associated replacement effects, and removing cards from library; implement [WHO] River Song #12700

Merged
merged 12 commits into from
Aug 24, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ protected Game createMCTSGame(Game game) {
newPlayer.getHand().clear();
newPlayer.getLibrary().shuffle();
for (int i = 0; i < handSize; i++) {
Card card = newPlayer.getLibrary().removeFromTop(mcts);
Card card = newPlayer.getLibrary().drawFromTop(mcts);
card.setZone(Zone.HAND, mcts);
newPlayer.getHand().add(card);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.PlayLandAbility;
import mage.abilities.common.PassAbility;
import mage.cards.Card;
Expand Down Expand Up @@ -270,7 +269,7 @@ protected void randomizePlayers(Game game, UUID playerId) {
player.getHand().clear();
player.getLibrary().shuffle();
for (int i = 0; i < handSize; i++) {
Card card = player.getLibrary().removeFromTop(game);
Card card = player.getLibrary().drawFromTop(game);
card.setZone(Zone.HAND, game);
player.getHand().add(card);
}
Expand Down
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/a/AlmsCollector.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) {

@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DRAW_CARDS;
return event.getType() == GameEvent.EventType.DRAW_TWO_OR_MORE_CARDS;
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/c/CellarDoor.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public CellarDoorEffect copy() {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getFirstTarget());
if (player != null && player.getLibrary().hasCards()) {
Card card = player.getLibrary().removeFromBottom(game);
Card card = player.getLibrary().getFromBottom(game);
if (card != null) {
player.moveCards(card, Zone.GRAVEYARD, source, game);
if (card.isCreature(game)) {
Expand Down
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/i/InzervaMasterOfInsights.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public boolean apply(Game game, Ability source) {
continue;
}
for (int i = 0; i < count; i++) {
Card card = opponent.getLibrary().removeFromTop(game);
Card card = opponent.getLibrary().getFromTop(game);
cards.add(card);
}
TargetCard targets = new TargetCard(0, cards.size(), Zone.LIBRARY, new FilterCard("cards to PUT on the BOTTOM of " + opponent.getName() + "'s library"));
Expand Down
4 changes: 2 additions & 2 deletions Mage.Sets/src/mage/cards/l/LidlessGaze.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public boolean apply(Game game, Ability source) {
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
Player player = game.getPlayer(playerId);
if (player != null) {
Card card = player.getLibrary().removeFromTop(game);
Card card = player.getLibrary().getFromTop(game);
if (card != null) {
controller.moveCardsToExile(card, source, game, true, exileId, exileName);
cards.add(card);
Expand All @@ -89,4 +89,4 @@ public boolean apply(Game game, Ability source) {

return true;
}
}
}
134 changes: 134 additions & 0 deletions Mage.Sets/src/mage/cards/r/RiverSong.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package mage.cards.r;

import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount;
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;
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;

import java.util.UUID;

/**
* @author xenohedron
*/
public final class RiverSong extends CardImpl {

public RiverSong(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}");

this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.TIME_LORD);
this.subtype.add(SubType.ROGUE);
this.power = new MageInt(2);
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())
.withFlavorWord("Meet in Reverse"));

// Spoilers -- Whenever an opponent scries, surveils, or searches their library, put a +1/+1 counter on River Song.
// Then River Song deals damage to that player equal to its power.
TriggeredAbility trigger = new RiverSongTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()));
trigger.addEffect(new DamageTargetEffect(new SourcePermanentPowerCount(false))
.setText("Then {this} deals damage to that player equal to its power"));
this.addAbility(trigger.withFlavorWord("Spoilers"));
}

private RiverSong(final RiverSong card) {
super(card);
}

@Override
public RiverSong copy() {
return new RiverSong(this);
}
}

class RiverSongDrawFromBottomReplacementEffect extends ReplacementEffectImpl {

RiverSongDrawFromBottomReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.Neutral);
staticText = "You draw cards from the bottom of your library rather than the top";
}

private RiverSongDrawFromBottomReplacementEffect(final RiverSongDrawFromBottomReplacementEffect effect) {
super(effect);
}

@Override
public RiverSongDrawFromBottomReplacementEffect copy() {
return new RiverSongDrawFromBottomReplacementEffect(this);
}

@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
((DrawCardEvent) event).setFromBottom(true);
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());
}
}

class RiverSongTriggeredAbility extends TriggeredAbilityImpl {

RiverSongTriggeredAbility(Effect effect) {
super(Zone.BATTLEFIELD, effect);
setTriggerPhrase("Whenever an opponent scries, surveils, or searches their library, ");
}

private RiverSongTriggeredAbility(final RiverSongTriggeredAbility ability) {
super(ability);
}

@Override
public RiverSongTriggeredAbility copy() {
return new RiverSongTriggeredAbility(this);
}

@Override
public boolean checkEventType(GameEvent event, Game game) {
switch (event.getType()) {
case SCRIED:
case SURVEILED:
case LIBRARY_SEARCHED:
return true;
default:
return false;
}
}

@Override
public boolean checkTrigger(GameEvent event, Game game) {
Player controller = game.getPlayer(getControllerId());
if (controller != null
&& controller.hasOpponent(event.getPlayerId(), game)
&& event.getPlayerId().equals(event.getTargetId())) { // searches own library
getEffects().setTargetPointer(new FixedTarget(event.getPlayerId()));
return true;
}
return false;
}
}
4 changes: 2 additions & 2 deletions Mage.Sets/src/mage/cards/w/WriteIntoBeing.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ public boolean apply(Game game, Ability source) {
cardToManifest = cards.getRandom(game);
}
if (!controller.getLibrary().getFromTop(game).equals(cardToManifest)) {
Card cardToPutBack = controller.getLibrary().removeFromTop(game);
cardToManifest = controller.getLibrary().removeFromTop(game);
Card cardToPutBack = controller.getLibrary().getFromTop(game);
cardToManifest = controller.getLibrary().getFromTop(game);
controller.getLibrary().putOnTop(cardToPutBack, game);
controller.getLibrary().putOnTop(cardToManifest, game);
}
Expand Down
1 change: 1 addition & 0 deletions Mage.Sets/src/mage/sets/DoctorWho.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ private DoctorWho() {
cards.add(new SetCardInfo("Return the Past", 92, Rarity.RARE, mage.cards.r.ReturnThePast.class));
cards.add(new SetCardInfo("Return to Dust", 211, Rarity.UNCOMMON, mage.cards.r.ReturnToDust.class));
cards.add(new SetCardInfo("Reverse the Polarity", 54, Rarity.RARE, mage.cards.r.ReverseThePolarity.class));
cards.add(new SetCardInfo("River Song", 152, Rarity.RARE, mage.cards.r.RiverSong.class));
cards.add(new SetCardInfo("River of Tears", 297, Rarity.RARE, mage.cards.r.RiverOfTears.class));
cards.add(new SetCardInfo("Rockfall Vale", 298, Rarity.RARE, mage.cards.r.RockfallVale.class));
cards.add(new SetCardInfo("Rogue's Passage", 299, Rarity.UNCOMMON, mage.cards.r.RoguesPassage.class));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package org.mage.test.cards.replacement;

import mage.constants.PhaseStep;
Expand Down Expand Up @@ -104,4 +103,25 @@ public void WordsOfWilding() {
assertPermanentCount(playerA, "Bear Token", 1);
assertHandCount(playerA, 1);
}

@Test
public void testAlmsCollector() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
addCard(Zone.BATTLEFIELD, playerB, "Alms Collector");
// If an opponent would draw two or more cards, instead you and that player each draw a card.

// Draw two cards.
addCard(Zone.HAND, playerA, "Counsel of the Soratami", 1); // Sorcery {2}{U}

castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Counsel of the Soratami");

setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();

assertGraveyardCount(playerA, "Counsel of the Soratami", 1);
assertHandCount(playerA, 1);
assertHandCount(playerB, 1);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public boolean apply(Game game, Ability source) {
return true;
}
for (int i = 0; i < count; i++) {
Card card = opponent.getLibrary().removeFromTop(game);
Card card = opponent.getLibrary().getFromTop(game);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And don’t forget to add cards removing in code like that here. Maybe it’s better to keep removeFromXXX methods.

cards.add(card);
}
TargetCard target1 = new TargetCard(Zone.LIBRARY, filter1);
Expand Down
113 changes: 0 additions & 113 deletions Mage/src/main/java/mage/actions/MageDrawAction.java

This file was deleted.

Loading
Loading