mirror of
https://github.com/magefree/mage.git
synced 2025-12-26 05:22:02 -08:00
Merge branch 'master' of https://github.com/magefree/mage
This commit is contained in:
commit
f35ca8112c
39 changed files with 769 additions and 202 deletions
|
|
@ -241,7 +241,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
|
|||
assignPayment(game, ability, player.getManaPool(), costToPay != null ? costToPay : this);
|
||||
}
|
||||
game.getState().getSpecialActions().removeManaActions();
|
||||
while (!isPaid()) {
|
||||
while (player.canRespond() && !isPaid()) {
|
||||
ManaCost unpaid = this.getUnpaid();
|
||||
String promptText = ManaUtil.addSpecialManaPayAbilities(ability, game, unpaid);
|
||||
if (player.playMana(ability, unpaid, promptText, game)) {
|
||||
|
|
@ -251,7 +251,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
|
|||
}
|
||||
game.getState().getSpecialActions().removeManaActions();
|
||||
}
|
||||
return true;
|
||||
return isPaid();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
assignPayment(game, ability, player.getManaPool(), this);
|
||||
}
|
||||
game.getState().getSpecialActions().removeManaActions();
|
||||
while (!isPaid()) {
|
||||
while (player.canRespond() && !isPaid()) {
|
||||
ManaCost unpaid = this.getUnpaid();
|
||||
String promptText = ManaUtil.addSpecialManaPayAbilities(ability, game, unpaid);
|
||||
if (player.playMana(ability, unpaid, promptText, game)) {
|
||||
|
|
@ -138,7 +138,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
}
|
||||
game.getState().getSpecialActions().removeManaActions();
|
||||
}
|
||||
return true;
|
||||
return isPaid();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,10 +24,11 @@ public class BoosterDraft extends DraftImpl {
|
|||
cardNum = 1;
|
||||
fireUpdatePlayersEvent();
|
||||
while (!isAbort() && pickCards()) {
|
||||
if ((boosterNum + 1) % 2 == 1) {
|
||||
passLeft();
|
||||
// pass booster order: left -> right -> left
|
||||
if (boosterNum % 2 == 1) {
|
||||
passBoosterToLeft();
|
||||
} else {
|
||||
passRight();
|
||||
passBoosterToRight();
|
||||
}
|
||||
fireUpdatePlayersEvent();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,11 +149,11 @@ public abstract class DraftImpl implements Draft {
|
|||
this.addPick(playerId, booster.get(booster.size()-1).getId(), null);
|
||||
}
|
||||
|
||||
protected void passLeft() {
|
||||
protected void passBoosterToLeft() {
|
||||
synchronized (players) {
|
||||
UUID startId = table.get(0);
|
||||
UUID currentId = startId;
|
||||
UUID nextId = table.getNext();
|
||||
UUID nextId = table.getNext(); // getNext return left player by default
|
||||
DraftPlayer current = players.get(currentId);
|
||||
DraftPlayer next = players.get(nextId);
|
||||
List<Card> currentBooster = current.booster;
|
||||
|
|
@ -170,11 +170,11 @@ public abstract class DraftImpl implements Draft {
|
|||
}
|
||||
}
|
||||
|
||||
protected void passRight() {
|
||||
protected void passBoosterToRight() {
|
||||
synchronized (players) {
|
||||
UUID startId = table.get(0);
|
||||
UUID currentId = startId;
|
||||
UUID prevId = table.getPrevious();
|
||||
UUID prevId = table.getPrevious(); // getPrevious return right player by default
|
||||
DraftPlayer current = players.get(currentId);
|
||||
DraftPlayer prev = players.get(prevId);
|
||||
List<Card> currentBooster = current.booster;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ public class RichManBoosterDraft extends DraftImpl {
|
|||
cardNum = 1;
|
||||
fireUpdatePlayersEvent();
|
||||
while (!isAbort() && pickCards()) {
|
||||
passLeft();
|
||||
// new booster each time, so order is irrelevant
|
||||
passBoosterToLeft();
|
||||
fireUpdatePlayersEvent();
|
||||
}
|
||||
boosterNum++;
|
||||
|
|
@ -42,7 +43,7 @@ public class RichManBoosterDraft extends DraftImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void passLeft() {
|
||||
protected void passBoosterToLeft() {
|
||||
synchronized (players) {
|
||||
UUID startId = table.get(0);
|
||||
UUID currentId = startId;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ public class RichManCubeBoosterDraft extends DraftImpl {
|
|||
cardNum = 1;
|
||||
fireUpdatePlayersEvent();
|
||||
while (!isAbort() && pickCards()) {
|
||||
passLeft();
|
||||
// new booster each time, so order is irrelevant
|
||||
passBoosterToLeft();
|
||||
fireUpdatePlayersEvent();
|
||||
}
|
||||
boosterNum++;
|
||||
|
|
@ -40,7 +41,7 @@ public class RichManCubeBoosterDraft extends DraftImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void passLeft() {
|
||||
protected void passBoosterToLeft() {
|
||||
synchronized (players) {
|
||||
UUID startId = table.get(0);
|
||||
UUID currentId = startId;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import mage.util.CircularList;
|
|||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Default players order: left (next player seated to the active player's left)
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class PlayerList extends CircularList<UUID> {
|
||||
|
|
@ -34,7 +36,9 @@ public class PlayerList extends CircularList<UUID> {
|
|||
}
|
||||
|
||||
/**
|
||||
* checkNextTurnReached - use it turns/priority code only to mark leaved player as "reached next turn end" (need for some continous effects)
|
||||
* Find next player. Default order: next player from the left
|
||||
*
|
||||
* @checkNextTurnReached - use it turns/priority code only to mark leaved player as "reached next turn end" (need for some continous effects)
|
||||
*/
|
||||
public Player getNext(Game game, boolean checkNextTurnReached) {
|
||||
UUID start = this.get();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue