Skip to content

Commit

Permalink
implement [WHO] River Song
Browse files Browse the repository at this point in the history
  • Loading branch information
xenohedron committed Aug 22, 2024
1 parent b79fc46 commit fd00c1c
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 3 deletions.
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;
}
}
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
11 changes: 11 additions & 0 deletions Mage/src/main/java/mage/game/events/DrawCardEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
*/
public class DrawCardEvent extends GameEvent {

private boolean fromBottom = false; // for replacement effects that draw from bottom of library instead

public DrawCardEvent(UUID playerId, Ability source, GameEvent originalDrawEvent) {
super(GameEvent.EventType.DRAW_CARD, playerId, null, playerId, 0, false);

Expand All @@ -22,4 +24,13 @@ public DrawCardEvent(UUID playerId, Ability source, GameEvent originalDrawEvent)
this.addAppliedEffects(originalDrawEvent.getAppliedEffects());
}
}

public void setFromBottom(boolean fromBottom) {
this.fromBottom = fromBottom;
}

public boolean isFromBottom() {
return fromBottom;
}

}
7 changes: 4 additions & 3 deletions Mage/src/main/java/mage/players/PlayerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public abstract class PlayerImpl implements Player, Serializable {
/**
* During some steps we can't play anything
*/
final static Map<PhaseStep, Step.StepPart> SILENT_PHASES_STEPS = ImmutableMap.<PhaseStep, Step.StepPart>builder().
static final Map<PhaseStep, Step.StepPart> SILENT_PHASES_STEPS = ImmutableMap.<PhaseStep, Step.StepPart>builder().
put(PhaseStep.DECLARE_ATTACKERS, Step.StepPart.PRE).build();

/**
Expand Down Expand Up @@ -754,10 +754,11 @@ public int drawCards(int num, Ability source, Game game, GameEvent event) {
}
int numDrawn = 0;
for (int i = 0; i < num; i++) {
if (game.replaceEvent(new DrawCardEvent(getId(), source, event))) {
DrawCardEvent drawCardEvent = new DrawCardEvent(getId(), source, event);
if (game.replaceEvent(drawCardEvent)) {
continue;
}
Card card = getLibrary().drawFromTop(game);
Card card = drawCardEvent.isFromBottom() ? 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()) {
Expand Down

0 comments on commit fd00c1c

Please sign in to comment.