[AFR] Implemented Xanathar, Guild Kingpin (#8045)

* [AFR] Implemented Xanathar, Guild Kingpin

Co-authored-by: Oleg Agafonov <jaydi85@gmail.com>
This commit is contained in:
Raphael-Schulz 2021-08-28 21:55:41 +02:00 committed by GitHub
parent 65761b085f
commit f03a410b9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 477 additions and 55 deletions

View file

@ -4,48 +4,102 @@ import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.cards.Card;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.*;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
* @author TheElk801
*/
public class LookAtTopCardOfLibraryAnyTimeEffect extends ContinuousEffectImpl {
private final TargetController targetLibrary;
public LookAtTopCardOfLibraryAnyTimeEffect() {
super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit);
staticText = "You may look at the top card of your library any time.";
this(TargetController.YOU, Duration.WhileOnBattlefield);
}
private LookAtTopCardOfLibraryAnyTimeEffect(final LookAtTopCardOfLibraryAnyTimeEffect effect) {
public LookAtTopCardOfLibraryAnyTimeEffect(TargetController targetLibrary, Duration duration) {
super(duration, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit);
this.targetLibrary = targetLibrary;
String libInfo;
switch (this.targetLibrary) {
case YOU:
libInfo = "your library";
break;
case OPPONENT:
libInfo = "opponents libraries";
break;
case SOURCE_TARGETS:
libInfo = "target player's library";
break;
default:
throw new IllegalArgumentException("Unknown target library type: " + targetLibrary);
}
staticText = duration.toString().isEmpty() ? "" : duration + " you may look at the top card of " + libInfo + " any time.";
}
protected LookAtTopCardOfLibraryAnyTimeEffect(final LookAtTopCardOfLibraryAnyTimeEffect effect) {
super(effect);
this.targetLibrary = effect.targetLibrary;
}
@Override
public boolean apply(Game game, Ability source) {
if (game.inCheckPlayableState()) { // Ignored - see https://github.com/magefree/mage/issues/6994
return false;
}
}
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
Card topCard = controller.getLibrary().getFromTop(game);
if (topCard == null) {
if (!canLookAtNextTopLibraryCard(game)) {
return false;
}
MageObject obj = source.getSourceObject(game);
if (obj == null) {
return false;
}
if (!canLookAtNextTopLibraryCard(game)) {
Set<UUID> needPlayers = new HashSet<>();
switch (this.targetLibrary) {
case YOU: {
needPlayers.add(source.getControllerId());
break;
}
case OPPONENT: {
needPlayers.addAll(game.getOpponents(source.getControllerId()));
break;
}
case SOURCE_TARGETS: {
needPlayers.addAll(CardUtil.getAllSelectedTargets(source, game));
break;
}
}
Set<Card> needCards = new HashSet<>();
needPlayers.stream()
.map(game::getPlayer)
.filter(Objects::nonNull)
.map(player -> player.getLibrary().getFromTop(game))
.filter(Objects::nonNull)
.forEach(needCards::add);
if (needCards.isEmpty()) {
return false;
}
controller.lookAtCards("Top card of " + obj.getIdName() + " controller's library", topCard, game);
// all fine, can show top card
needCards.forEach(topCard -> {
Player owner = game.getPlayer(topCard.getOwnerId());
controller.lookAtCards(String.format("%s: top card of %s", obj.getName(), owner == null ? "error" : owner.getName()), topCard, game);
});
return true;
}

View file

@ -0,0 +1,23 @@
package mage.abilities.effects.common.continuous;
import mage.constants.Duration;
import mage.constants.TargetController;
/**
* @author JayDi85
*/
public class LookAtTopCardOfLibraryAnyTimeTargetEffect extends LookAtTopCardOfLibraryAnyTimeEffect {
public LookAtTopCardOfLibraryAnyTimeTargetEffect(Duration duration) {
super(TargetController.SOURCE_TARGETS, duration);
}
private LookAtTopCardOfLibraryAnyTimeTargetEffect(final LookAtTopCardOfLibraryAnyTimeTargetEffect effect) {
super(effect);
}
@Override
public LookAtTopCardOfLibraryAnyTimeTargetEffect copy() {
return new LookAtTopCardOfLibraryAnyTimeTargetEffect(this);
}
}

View file

@ -6,11 +6,14 @@ import mage.cards.Card;
import mage.constants.AsThoughEffectType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
/**
@ -19,20 +22,44 @@ import java.util.UUID;
public class PlayTheTopCardEffect extends AsThoughEffectImpl {
private final FilterCard filter;
private final TargetController targetLibrary;
// can play card or can play lands/cast spells, see two modes below
private final boolean canPlayCardOnly;
/**
* Support targets, use TargetController.SOURCE_TARGETS
*/
public PlayTheTopCardEffect() {
this(new FilterCard("play lands and cast spells"), false);
this(TargetController.YOU);
}
public PlayTheTopCardEffect(FilterCard filter, boolean canPlayCardOnly) {
public PlayTheTopCardEffect(TargetController targetLibrary) {
this(targetLibrary, new FilterCard("play lands and cast spells"), false);
}
public PlayTheTopCardEffect(TargetController targetLibrary, FilterCard filter, boolean canPlayCardOnly) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
this.filter = filter;
this.targetLibrary = targetLibrary;
this.canPlayCardOnly = canPlayCardOnly;
this.staticText = "You may " + filter.getMessage() + " from the top of your library";
String libInfo;
switch (this.targetLibrary) {
case YOU:
libInfo = "your library";
break;
case OPPONENT:
libInfo = "opponents libraries";
break;
case SOURCE_TARGETS:
libInfo = "target player's library";
break;
default:
throw new IllegalArgumentException("Unknown target library type: " + targetLibrary);
}
this.staticText = "You may " + filter.getMessage() + " from the top of " + libInfo;
// verify check: if you see "card" text in the rules then use card mode
// (there aren't any real cards after oracle update, but can be added in the future)
@ -44,6 +71,7 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
public PlayTheTopCardEffect(final PlayTheTopCardEffect effect) {
super(effect);
this.filter = effect.filter;
this.targetLibrary = effect.targetLibrary;
this.canPlayCardOnly = effect.canPlayCardOnly;
}
@ -71,7 +99,7 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
}
if (this.canPlayCardOnly) {
// check whole card intead part
// check whole card instead part
cardToCheck = cardToCheck.getMainCard();
}
@ -80,16 +108,50 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
return false;
}
// must be your card
Player player = game.getPlayer(cardToCheck.getOwnerId());
if (player == null || !player.getId().equals(affectedControllerId)) {
Player cardOwner = game.getPlayer(cardToCheck.getOwnerId());
Player controller = game.getPlayer(source.getControllerId());
if (cardOwner == null || controller == null) {
return false;
}
// must be from your library
Card topCard = player.getLibrary().getFromTop(game);
if (topCard == null || !topCard.getId().equals(cardToCheck.getMainCard().getId())) {
return false;
// must be your or opponents library
switch (this.targetLibrary) {
case YOU: {
Card topCard = controller.getLibrary().getFromTop(game);
if (topCard == null || !topCard.getId().equals(cardToCheck.getMainCard().getId())) {
return false;
}
break;
}
case OPPONENT: {
if (!game.getOpponents(controller.getId()).contains(cardOwner.getId())) {
return false;
}
Card topCard = cardOwner.getLibrary().getFromTop(game);
if (topCard == null || !topCard.getId().equals(cardToCheck.getMainCard().getId())) {
return false;
}
break;
}
case SOURCE_TARGETS: {
UUID needCardId = cardToCheck.getMainCard().getId();
if (CardUtil.getAllSelectedTargets(source, game).stream()
.map(game::getPlayer)
.filter(Objects::nonNull)
.noneMatch(player -> {
Card topCard = player.getLibrary().getFromTop(game);
return topCard != null && topCard.getId().equals(needCardId);
})) {
return false;
}
break;
}
default: {
return false;
}
}
// can't cast without mana cost

View file

@ -0,0 +1,22 @@
package mage.abilities.effects.common.continuous;
import mage.constants.TargetController;
/**
* @author JayDi85
*/
public class PlayTheTopCardTargetEffect extends PlayTheTopCardEffect {
public PlayTheTopCardTargetEffect() {
super(TargetController.SOURCE_TARGETS);
}
public PlayTheTopCardTargetEffect(final PlayTheTopCardTargetEffect effect) {
super(effect);
}
@Override
public PlayTheTopCardTargetEffect copy() {
return new PlayTheTopCardTargetEffect(this);
}
}

View file

@ -28,7 +28,7 @@ public enum Outcome {
PutManaInPool(true),
Regenerate(true),
PreventDamage(true), // TODO: check good or bad
PreventCast(false), // TODO: check good or bad
PreventCast(false),
RedirectDamage(true), // TODO: check good or bad
Tap(false),
Transform(true),

View file

@ -25,7 +25,8 @@ public enum TargetController {
OWNER,
CONTROLLER_ATTACHED_TO,
NEXT,
EACH_PLAYER;
EACH_PLAYER,
SOURCE_TARGETS;
private final OwnerPredicate ownerPredicate;
private final PlayerPredicate playerPredicate;

View file

@ -11,11 +11,24 @@ import mage.players.Player;
*/
public enum CardOnTopOfLibraryPredicate implements ObjectPlayerPredicate<ObjectPlayer<Card>> {
instance;
YOUR,
ANY;
@Override
public boolean apply(ObjectPlayer<Card> input, Game game) {
Player player = game.getPlayer(input.getObject().getOwnerId());
Player player;
switch (this) {
case YOUR:
player = game.getPlayer(input.getPlayerId());
break;
case ANY:
default:
player = game.getPlayer(input.getObject().getOwnerId());
break;
}
if (player == null) {
return false;
}