[WOE] Implement Ashiok, Wicked Manipulator (#10909)

* [WOE] Implement Ashiok, Wicket Manipulator

* Add Ashiok's abilities

* basic pay life replacement tests

* many tests later

* add warning on token expecting watcher

* apply review

* rework text generation
This commit is contained in:
Susucre 2023-08-31 01:15:56 +02:00 committed by GitHub
parent fe165f1fd0
commit 2a5dd4103c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 634 additions and 19 deletions

View file

@ -0,0 +1,30 @@
package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.game.Game;
import mage.watchers.common.CardsExiledThisTurnWatcher;
/**
* Checks if at least one card was put into exile this turn.
* <p>
* /!\ Need the CardsExiledThisTurnWatcher to be set up.
*
* @author Susucr
*/
public enum WasCardExiledThisTurnCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {
CardsExiledThisTurnWatcher watcher = game.getState().getWatcher(CardsExiledThisTurnWatcher.class);
return watcher != null && watcher.getCountCardsExiledThisTurn() > 0;
}
@Override
public String toString() {
return "a card was put into exile this turn";
}
}

View file

@ -0,0 +1,52 @@
package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.cards.Card;
import mage.game.Game;
import java.util.List;
public enum TotalCardsExiledOwnedManaValue implements DynamicValue {
instance;
private static final Hint hint = new ValueHint("Total mana value of cards you own in exile", instance);
private TotalCardsExiledOwnedManaValue() {
}
@Override
public TotalCardsExiledOwnedManaValue copy() {
return this;
}
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
int totalCMC = 0;
List<Card> cards = game.getExile().getAllCards(
game,
sourceAbility.getControllerId()
);
for (Card card : cards) {
totalCMC += card.getManaValue();
}
return totalCMC;
}
@Override
public String getMessage() {
return "the total mana value of cards you own in exile";
}
@Override
public String toString() {
return "X";
}
public static Hint getHint() {
return hint;
}
}

View file

@ -1,6 +1,9 @@
package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Cards;
import mage.cards.CardsImpl;
@ -11,29 +14,24 @@ import mage.players.Player;
import mage.util.CardUtil;
/**
* @author LevelX2
* @author LevelX2, Susucr
*/
public class ExileCardsFromTopOfLibraryTargetEffect extends OneShotEffect {
int amount;
String targetName;
private final DynamicValue amount;
public ExileCardsFromTopOfLibraryTargetEffect(int amount) {
this(amount, null);
this(StaticValue.get(amount));
}
public ExileCardsFromTopOfLibraryTargetEffect(int amount, String targetName) {
public ExileCardsFromTopOfLibraryTargetEffect(DynamicValue amount) {
super(Outcome.Exile);
this.amount = amount;
this.staticText = (targetName == null ? "that player" : targetName) + " exiles the top "
+ CardUtil.numberToText(amount, "")
+ (amount == 1 ? "card" : " cards") + " of their library";
this.amount = amount.copy();
}
protected ExileCardsFromTopOfLibraryTargetEffect(final ExileCardsFromTopOfLibraryTargetEffect effect) {
super(effect);
this.amount = effect.amount;
this.amount = effect.amount.copy();
}
@Override
@ -43,12 +41,24 @@ public class ExileCardsFromTopOfLibraryTargetEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
int milled = amount.calculate(game, source, this);
Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
if (targetPlayer != null) {
if (milled > 0 && targetPlayer != null) {
Cards cards = new CardsImpl();
cards.addAllCards(targetPlayer.getLibrary().getTopCards(game, amount));
cards.addAllCards(targetPlayer.getLibrary().getTopCards(game, milled));
return targetPlayer.moveCards(cards, Zone.EXILED, source, game);
}
return false;
}
@Override
public String getText(Mode mode) {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
return getTargetPointer().describeTargets(mode.getTargets(), "that player")
+ " exiles the top "
+ (amount.toString().equals("1") ? "card" : CardUtil.numberToText(amount.toString(), "a") + " cards")
+ " of their library";
}
}

View file

@ -76,7 +76,7 @@ public class Exile implements Serializable, Copyable<Exile> {
}
/**
* Return exiled cards from specific player. Use it in effects to find all cards in range.
* Return exiled cards owned by a specific player. Use it in effects to find all cards in range.
*
* @param game
* @param fromPlayerId

View file

@ -331,7 +331,7 @@ public class GameEvent implements Serializable {
PLANESWALK, PLANESWALKED,
PAID_CUMULATIVE_UPKEEP,
DIDNT_PAY_CUMULATIVE_UPKEEP,
LIFE_PAID,
PAY_LIFE, LIFE_PAID,
CASCADE_LAND,
LEARN,
//permanent events

View file

@ -0,0 +1,52 @@
package mage.game.permanent.token;
import mage.MageInt;
import mage.abilities.common.BeginningOfCombatTriggeredAbility;
import mage.abilities.condition.common.WasCardExiledThisTurnCondition;
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.hint.ConditionHint;
import mage.abilities.hint.Hint;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.counters.CounterType;
/**
* @author Susucr
*/
public final class AshiokWickedManipulatorNightmareToken extends TokenImpl {
private static final Hint hint = new ConditionHint(WasCardExiledThisTurnCondition.instance);
/**
* /!\ You need to add CardsExiledThisTurnWatcher to any card using this token
*/
public AshiokWickedManipulatorNightmareToken() {
super("Nightmare Token", "1/1 black Nightmare creature tokens with \"At the beginning of combat on your turn, if a card was put into exile this turn, put a +1/+1 counter on this creature.\"");
cardType.add(CardType.CREATURE);
color.setBlack(true);
subtype.add(SubType.NIGHTMARE);
power = new MageInt(1);
toughness = new MageInt(1);
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
new BeginningOfCombatTriggeredAbility(
new AddCountersSourceEffect(CounterType.P1P1.createInstance()),
TargetController.YOU,
false
),
WasCardExiledThisTurnCondition.instance,
"At the beginning of combat on your turn, if a card was put into exile "
+ "this turn, put a +1/+1 counter on this creature."
).addHint(hint));
}
private AshiokWickedManipulatorNightmareToken(final AshiokWickedManipulatorNightmareToken token) {
super(token);
}
public AshiokWickedManipulatorNightmareToken copy() {
return new AshiokWickedManipulatorNightmareToken(this);
}
}

View file

@ -1496,6 +1496,11 @@ public final class CardUtil {
return false;
}
if (game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PAY_LIFE, player.getId(), source, player.getId(), lifeToPay))) {
// 2023-08-20: For now, Cost being replaced are paid.
// Waiting on actual ruling of Ashiok, Wicked Manipulator.
return true;
}
if (player.loseLife(lifeToPay, game, source, false) >= lifeToPay) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LIFE_PAID, player.getId(), source, player.getId(), lifeToPay));
return true;

View file

@ -0,0 +1,38 @@
package mage.watchers.common;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.watchers.Watcher;
/**
* @author Susucr
*/
public class CardsExiledThisTurnWatcher extends Watcher {
private int countExiled = 0;
public CardsExiledThisTurnWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.ZONE_CHANGE
&& ((ZoneChangeEvent) event).getToZone() == Zone.EXILED) {
countExiled++;
}
}
public int getCountCardsExiledThisTurn() {
return countExiled;
}
@Override
public void reset() {
super.reset();
countExiled = 0;
}
}