forked from External/mage
* Fixed a problem with check playable methods causing e.g. endless loop if Shared Fate was on the battlefield.
This commit is contained in:
parent
32bd88a6c0
commit
133cc7342d
19 changed files with 227 additions and 230 deletions
|
|
@ -1,7 +1,28 @@
|
||||||
package mage.player.human;
|
package mage.player.human;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.*;
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.ActivatedAbility;
|
||||||
|
import mage.abilities.Mode;
|
||||||
|
import mage.abilities.Modes;
|
||||||
|
import mage.abilities.PlayLandAbility;
|
||||||
|
import mage.abilities.SpecialAction;
|
||||||
|
import mage.abilities.SpellAbility;
|
||||||
|
import mage.abilities.TriggeredAbility;
|
||||||
import mage.abilities.costs.VariableCost;
|
import mage.abilities.costs.VariableCost;
|
||||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||||
import mage.abilities.costs.common.TapSourceCost;
|
import mage.abilities.costs.common.TapSourceCost;
|
||||||
|
|
@ -16,6 +37,11 @@ import mage.cards.decks.Deck;
|
||||||
import mage.choices.Choice;
|
import mage.choices.Choice;
|
||||||
import mage.choices.ChoiceImpl;
|
import mage.choices.ChoiceImpl;
|
||||||
import mage.constants.*;
|
import mage.constants.*;
|
||||||
|
import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_RESET_ALL;
|
||||||
|
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
|
||||||
|
import static mage.constants.SpellAbilityType.SPLIT;
|
||||||
|
import static mage.constants.SpellAbilityType.SPLIT_AFTERMATH;
|
||||||
|
import static mage.constants.SpellAbilityType.SPLIT_FUSED;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.filter.common.FilterAttackingCreature;
|
import mage.filter.common.FilterAttackingCreature;
|
||||||
import mage.filter.common.FilterBlockingCreature;
|
import mage.filter.common.FilterBlockingCreature;
|
||||||
|
|
@ -46,16 +72,6 @@ import mage.util.ManaUtil;
|
||||||
import mage.util.MessageToClient;
|
import mage.util.MessageToClient;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_RESET_ALL;
|
|
||||||
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
|
|
@ -268,6 +284,9 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) {
|
public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) {
|
||||||
|
if (game.inCheckPlayableState()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
MessageToClient messageToClient = new MessageToClient(message, secondMessage);
|
MessageToClient messageToClient = new MessageToClient(message, secondMessage);
|
||||||
Map<String, Serializable> options = new HashMap<>(2);
|
Map<String, Serializable> options = new HashMap<>(2);
|
||||||
if (trueText != null) {
|
if (trueText != null) {
|
||||||
|
|
@ -334,6 +353,14 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int chooseReplacementEffect(Map<String, String> rEffects, Game game) {
|
public int chooseReplacementEffect(Map<String, String> rEffects, Game game) {
|
||||||
|
if (game.inCheckPlayableState()) {
|
||||||
|
logger.warn("player interaction in checkPlayableState.");
|
||||||
|
if (rEffects.size() <= 1) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
updateGameStatePriority("chooseEffect", game);
|
updateGameStatePriority("chooseEffect", game);
|
||||||
if (rEffects.size() <= 1) {
|
if (rEffects.size() <= 1) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -394,6 +421,11 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean choose(Outcome outcome, Choice choice, Game game) {
|
public boolean choose(Outcome outcome, Choice choice, Game game) {
|
||||||
|
if (game.inCheckPlayableState()) {
|
||||||
|
logger.warn("player interaction in checkPlayableState. Choice: " + choice.getMessage());
|
||||||
|
choice.setChoice(choice.getChoices().iterator().next());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (Outcome.PutManaInPool == outcome) {
|
if (Outcome.PutManaInPool == outcome) {
|
||||||
if (currentlyUnpaidMana != null
|
if (currentlyUnpaidMana != null
|
||||||
&& ManaUtil.tryToAutoSelectAManaColor(choice, currentlyUnpaidMana)) {
|
&& ManaUtil.tryToAutoSelectAManaColor(choice, currentlyUnpaidMana)) {
|
||||||
|
|
@ -429,6 +461,11 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map<String, Serializable> options) {
|
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map<String, Serializable> options) {
|
||||||
|
if (game.inCheckPlayableState()) {
|
||||||
|
logger.warn("player interaction in checkPlayableState. Target: " + target.getMessage());
|
||||||
|
// TODO: set default choice
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// choose one or multiple permanents
|
// choose one or multiple permanents
|
||||||
updateGameStatePriority("choose(5)", game);
|
updateGameStatePriority("choose(5)", game);
|
||||||
UUID abilityControllerId = playerId;
|
UUID abilityControllerId = playerId;
|
||||||
|
|
@ -520,6 +557,11 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) {
|
public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) {
|
||||||
|
if (game.inCheckPlayableState()) {
|
||||||
|
logger.warn("player interaction in checkPlayableState. Target: " + target.getMessage());
|
||||||
|
// TODO: set default choice
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// choose one or multiple targets
|
// choose one or multiple targets
|
||||||
updateGameStatePriority("chooseTarget", game);
|
updateGameStatePriority("chooseTarget", game);
|
||||||
UUID abilityControllerId = playerId;
|
UUID abilityControllerId = playerId;
|
||||||
|
|
@ -582,6 +624,11 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) {
|
public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) {
|
||||||
|
if (game.inCheckPlayableState()) {
|
||||||
|
logger.warn("player interaction in checkPlayableState. Target: " + target.getMessage());
|
||||||
|
// TODO: set default choice
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// choose one or multiple cards
|
// choose one or multiple cards
|
||||||
if (cards == null) {
|
if (cards == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -710,9 +757,9 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
if (!isExecutingMacro()) {
|
if (!isExecutingMacro()) {
|
||||||
String selectedNames = target.getTargetedName(game);
|
String selectedNames = target.getTargetedName(game);
|
||||||
game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage()
|
game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage()
|
||||||
+ "<br> Amount remaining: " + target.getAmountRemaining()
|
+ "<br> Amount remaining: " + target.getAmountRemaining()
|
||||||
+ (selectedNames.isEmpty() ? "" : ", selected: " + selectedNames),
|
+ (selectedNames.isEmpty() ? "" : ", selected: " + selectedNames),
|
||||||
getRelatedObjectName(source, game)),
|
getRelatedObjectName(source, game)),
|
||||||
target.possibleTargets(source == null ? null : source.getSourceId(), playerId, game),
|
target.possibleTargets(source == null ? null : source.getSourceId(), playerId, game),
|
||||||
target.isRequired(source),
|
target.isRequired(source),
|
||||||
getOptions(target, null));
|
getOptions(target, null));
|
||||||
|
|
@ -725,7 +772,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
boolean removeMode = target.getTargets().contains(targetId)
|
boolean removeMode = target.getTargets().contains(targetId)
|
||||||
&& chooseUse(outcome, "What do you want to do with " + (targetObject != null ? targetObject.getLogName() : "target") + "?", "",
|
&& chooseUse(outcome, "What do you want to do with " + (targetObject != null ? targetObject.getLogName() : "target") + "?", "",
|
||||||
"Remove from selected", "Add extra amount", source, game);
|
"Remove from selected", "Add extra amount", source, game);
|
||||||
|
|
||||||
if (removeMode) {
|
if (removeMode) {
|
||||||
target.remove(targetId);
|
target.remove(targetId);
|
||||||
|
|
@ -862,9 +909,9 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
if (!skippedAtLeastOnce
|
if (!skippedAtLeastOnce
|
||||||
|| (playerId.equals(game.getActivePlayerId())
|
|| (playerId.equals(game.getActivePlayerId())
|
||||||
&& !controllingPlayer
|
&& !controllingPlayer
|
||||||
.getUserData()
|
.getUserData()
|
||||||
.getUserSkipPrioritySteps()
|
.getUserSkipPrioritySteps()
|
||||||
.isStopOnAllEndPhases())) {
|
.isStopOnAllEndPhases())) {
|
||||||
skippedAtLeastOnce = true;
|
skippedAtLeastOnce = true;
|
||||||
if (passWithManaPoolCheck(game)) {
|
if (passWithManaPoolCheck(game)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -896,9 +943,9 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
if (haveNewObjectsOnStack
|
if (haveNewObjectsOnStack
|
||||||
&& (playerId.equals(game.getActivePlayerId())
|
&& (playerId.equals(game.getActivePlayerId())
|
||||||
&& controllingPlayer
|
&& controllingPlayer
|
||||||
.getUserData()
|
.getUserData()
|
||||||
.getUserSkipPrioritySteps()
|
.getUserSkipPrioritySteps()
|
||||||
.isStopOnStackNewObjects())) {
|
.isStopOnStackNewObjects())) {
|
||||||
// new objects on stack -- disable "pass until stack resolved"
|
// new objects on stack -- disable "pass until stack resolved"
|
||||||
passedUntilStackResolved = false;
|
passedUntilStackResolved = false;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1235,8 +1282,8 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
if (passedAllTurns
|
if (passedAllTurns
|
||||||
|| passedUntilEndStepBeforeMyTurn
|
|| passedUntilEndStepBeforeMyTurn
|
||||||
|| (!getControllingPlayersUserData(game)
|
|| (!getControllingPlayersUserData(game)
|
||||||
.getUserSkipPrioritySteps()
|
.getUserSkipPrioritySteps()
|
||||||
.isStopOnDeclareAttackers()
|
.isStopOnDeclareAttackers()
|
||||||
&& (passedTurn
|
&& (passedTurn
|
||||||
|| passedTurnSkipStack
|
|| passedTurnSkipStack
|
||||||
|| passedUntilEndOfTurn
|
|| passedUntilEndOfTurn
|
||||||
|
|
@ -1419,7 +1466,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
/**
|
/**
|
||||||
* Selects a defender for an attacker and adds the attacker to combat
|
* Selects a defender for an attacker and adds the attacker to combat
|
||||||
*
|
*
|
||||||
* @param defenders - list of possible defender
|
* @param defenders - list of possible defender
|
||||||
* @param attackerId - UUID of attacker
|
* @param attackerId - UUID of attacker
|
||||||
* @param game
|
* @param game
|
||||||
* @return
|
* @return
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ class ExpropriateDilemmaEffect extends CouncilsDilemmaVoteEffect {
|
||||||
|
|
||||||
public ExpropriateDilemmaEffect() {
|
public ExpropriateDilemmaEffect() {
|
||||||
super(Outcome.Benefit);
|
super(Outcome.Benefit);
|
||||||
this.staticText = "<i>Council's dilemma</i> — Starting with you, each player votes for time or money. For each time vote, take an extra turn after this one. For each money vote, choose a permanent owned by the voter and gain control of it.";
|
this.staticText = "<i>Council's dilemma</i> — Starting with you, each player votes for time or money. For each time vote, take an extra turn after this one. For each money vote, choose a permanent owned by the voter and gain control of it";
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpropriateDilemmaEffect(final ExpropriateDilemmaEffect effect) {
|
public ExpropriateDilemmaEffect(final ExpropriateDilemmaEffect effect) {
|
||||||
|
|
@ -110,7 +110,7 @@ class ExpropriateDilemmaEffect extends CouncilsDilemmaVoteEffect {
|
||||||
target.setNotTarget(true);
|
target.setNotTarget(true);
|
||||||
|
|
||||||
if (controller != null
|
if (controller != null
|
||||||
&& controller.choose(Outcome.GainControl, target, source.getSourceId(), game)) {
|
&& controller.chooseTarget(Outcome.GainControl, target, source, game)) {
|
||||||
Permanent targetPermanent = game.getPermanent(target.getFirstTarget());
|
Permanent targetPermanent = game.getPermanent(target.getFirstTarget());
|
||||||
|
|
||||||
if (targetPermanent != null) {
|
if (targetPermanent != null) {
|
||||||
|
|
@ -132,9 +132,7 @@ class ExpropriateDilemmaEffect extends CouncilsDilemmaVoteEffect {
|
||||||
protected void vote(String choiceOne, String choiceTwo, Player controller, Game game, Ability source) {
|
protected void vote(String choiceOne, String choiceTwo, Player controller, Game game, Ability source) {
|
||||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||||
Player player = game.getPlayer(playerId);
|
Player player = game.getPlayer(playerId);
|
||||||
if (player != null
|
if (player != null) {
|
||||||
&& player.canRespond()
|
|
||||||
&& player.isInGame()) {
|
|
||||||
if (player.chooseUse(Outcome.Vote, "Choose " + choiceOne + '?', source, game)) {
|
if (player.chooseUse(Outcome.Vote, "Choose " + choiceOne + '?', source, game)) {
|
||||||
voteOneCount++;
|
voteOneCount++;
|
||||||
game.informPlayers(player.getName() + " has voted for " + choiceOne);
|
game.informPlayers(player.getName() + " has voted for " + choiceOne);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.cards.f;
|
package mage.cards.f;
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
|
@ -6,7 +5,6 @@ import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.Mana;
|
import mage.Mana;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.ActivatedAbility;
|
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
|
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
|
||||||
import mage.abilities.keyword.CyclingAbility;
|
import mage.abilities.keyword.CyclingAbility;
|
||||||
|
|
@ -70,9 +68,8 @@ class FluctuatorEffect extends CostModificationEffectImpl {
|
||||||
reduceMax = 2;
|
reduceMax = 2;
|
||||||
}
|
}
|
||||||
if (reduceMax > 0) {
|
if (reduceMax > 0) {
|
||||||
int reduce = 0;
|
int reduce;
|
||||||
if (abilityToModify.getAbilityType() == AbilityType.ACTIVATED
|
if (game.inCheckPlayableState()) {
|
||||||
&& ((ActivatedAbility) abilityToModify).isCheckPlayableMode()) {
|
|
||||||
reduce = reduceMax;
|
reduce = reduceMax;
|
||||||
} else {
|
} else {
|
||||||
ChoiceImpl choice = new ChoiceImpl(true);
|
ChoiceImpl choice = new ChoiceImpl(true);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.cards.m;
|
package mage.cards.m;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -111,7 +110,7 @@ class MizzixOfTheIzmagnusCostReductionEffect extends CostModificationEffectImpl
|
||||||
Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
|
Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
|
||||||
if (spell != null) {
|
if (spell != null) {
|
||||||
return StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY.match(spell, source.getSourceId(), source.getControllerId(), game);
|
return StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY.match(spell, source.getSourceId(), source.getControllerId(), game);
|
||||||
} else if (((SpellAbility) abilityToModify).isCheckPlayableMode()) {
|
} else if (game.inCheckPlayableState()) {
|
||||||
// Spell is not on the stack yet, but possible playable spells are determined
|
// Spell is not on the stack yet, but possible playable spells are determined
|
||||||
Card sourceCard = game.getCard(abilityToModify.getSourceId());
|
Card sourceCard = game.getCard(abilityToModify.getSourceId());
|
||||||
return sourceCard != null && StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY.match(sourceCard, source.getSourceId(), source.getControllerId(), game);
|
return sourceCard != null && StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY.match(sourceCard, source.getSourceId(), source.getControllerId(), game);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
|
|
||||||
package mage.cards.n;
|
package mage.cards.n;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.ActivatedAbility;
|
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.costs.common.CyclingDiscardCost;
|
import mage.abilities.costs.common.CyclingDiscardCost;
|
||||||
|
|
@ -70,7 +68,8 @@ class NewPerspectivesCostModificationEffect extends CostModificationEffectImpl {
|
||||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||||
Player controller = game.getPlayer(abilityToModify.getControllerId());
|
Player controller = game.getPlayer(abilityToModify.getControllerId());
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
if ((abilityToModify instanceof ActivatedAbility && ((ActivatedAbility) abilityToModify).isCheckPlayableMode()) || controller.chooseUse(Outcome.PlayForFree, "Pay {0} to cycle?", source, game)) {
|
if (game.inCheckPlayableState()
|
||||||
|
|| controller.chooseUse(Outcome.PlayForFree, "Pay {0} to cycle?", source, game)) {
|
||||||
abilityToModify.getCosts().clear();
|
abilityToModify.getCosts().clear();
|
||||||
abilityToModify.getManaCostsToPay().clear();
|
abilityToModify.getManaCostsToPay().clear();
|
||||||
abilityToModify.getCosts().add(new CyclingDiscardCost());
|
abilityToModify.getCosts().add(new CyclingDiscardCost());
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.cards.s;
|
package mage.cards.s;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -30,7 +29,7 @@ import mage.util.CardUtil;
|
||||||
public final class SharedFate extends CardImpl {
|
public final class SharedFate extends CardImpl {
|
||||||
|
|
||||||
public SharedFate(UUID ownerId, CardSetInfo setInfo) {
|
public SharedFate(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{U}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}");
|
||||||
|
|
||||||
// If a player would draw a card, that player exiles the top card of one of their opponents' libraries face down instead.
|
// If a player would draw a card, that player exiles the top card of one of their opponents' libraries face down instead.
|
||||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SharedFateReplacementEffect()));
|
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SharedFateReplacementEffect()));
|
||||||
|
|
@ -78,12 +77,9 @@ class SharedFateReplacementEffect extends ReplacementEffectImpl {
|
||||||
Card card = chosenPlayer.getLibrary().getFromTop(game);
|
Card card = chosenPlayer.getLibrary().getFromTop(game);
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
playerToDraw.moveCardsToExile(
|
playerToDraw.moveCardsToExile(
|
||||||
card,
|
card, source, game, false,
|
||||||
source,
|
|
||||||
game,
|
|
||||||
false,
|
|
||||||
CardUtil.getExileZoneId(source.getSourceId().toString() + sourcePermanent.getZoneChangeCounter(game) + playerToDraw.getId().toString(), game),
|
CardUtil.getExileZoneId(source.getSourceId().toString() + sourcePermanent.getZoneChangeCounter(game) + playerToDraw.getId().toString(), game),
|
||||||
"Shared Fate (" + playerToDraw.getName() + ')');
|
sourcePermanent.getIdName() + " (" + playerToDraw.getName() + ')');
|
||||||
card.setFaceDown(true, game);
|
card.setFaceDown(true, game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +122,7 @@ class SharedFatePlayEffect extends AsThoughEffectImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||||
if (game.getState().getZone(objectId) == Zone.EXILED) {
|
if (game.getState().getZone(objectId) == Zone.EXILED) {
|
||||||
Player player = game.getPlayer(affectedControllerId);
|
Player player = game.getPlayer(affectedControllerId);
|
||||||
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
|
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
|
||||||
UUID exileId = CardUtil.getExileZoneId(source.getSourceId().toString() + sourcePermanent.getZoneChangeCounter(game) + affectedControllerId.toString(), game);
|
UUID exileId = CardUtil.getExileZoneId(source.getSourceId().toString() + sourcePermanent.getZoneChangeCounter(game) + affectedControllerId.toString(), game);
|
||||||
|
|
@ -166,7 +162,7 @@ class SharedFateLookEffect extends AsThoughEffectImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||||
if (game.getState().getZone(objectId) == Zone.EXILED) {
|
if (game.getState().getZone(objectId) == Zone.EXILED) {
|
||||||
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
|
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
|
||||||
UUID exileId = CardUtil.getExileZoneId(source.getSourceId().toString() + sourcePermanent.getZoneChangeCounter(game) + affectedControllerId.toString(), game);
|
UUID exileId = CardUtil.getExileZoneId(source.getSourceId().toString() + sourcePermanent.getZoneChangeCounter(game) + affectedControllerId.toString(), game);
|
||||||
if (exileId != null) {
|
if (exileId != null) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ public class ThousandYearStormTest extends CardTestPlayerBase {
|
||||||
Enchantment
|
Enchantment
|
||||||
Whenever you cast an instant or sorcery spell, copy it for each other instant and sorcery spell you’ve cast before it this turn. You may choose new targets for the copies.
|
Whenever you cast an instant or sorcery spell, copy it for each other instant and sorcery spell you’ve cast before it this turn. You may choose new targets for the copies.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_CalcBeforeStorm() {
|
public void test_CalcBeforeStorm() {
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
|
||||||
|
|
@ -233,7 +232,6 @@ public class ThousandYearStormTest extends CardTestPlayerBase {
|
||||||
addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton");
|
addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton");
|
||||||
|
|
||||||
// turn 1
|
// turn 1
|
||||||
|
|
||||||
// 1a
|
// 1a
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
|
||||||
checkLife("0x copy", 1, PhaseStep.BEGIN_COMBAT, playerB, 20 - 3);
|
checkLife("0x copy", 1, PhaseStep.BEGIN_COMBAT, playerB, 20 - 3);
|
||||||
|
|
@ -255,7 +253,6 @@ public class ThousandYearStormTest extends CardTestPlayerBase {
|
||||||
assertAllCommandsUsed();
|
assertAllCommandsUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_WaitStackResolvedWithBolts() {
|
public void test_WaitStackResolvedWithBolts() {
|
||||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 5);
|
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 5);
|
||||||
|
|
@ -282,6 +279,8 @@ public class ThousandYearStormTest extends CardTestPlayerBase {
|
||||||
You control enchanted permanent.
|
You control enchanted permanent.
|
||||||
Cycling {2} ({2}, Discard this card: Draw a card.)
|
Cycling {2} ({2}, Discard this card: Draw a card.)
|
||||||
*/
|
*/
|
||||||
|
// Test fails sometimes with the following message:
|
||||||
|
// java.lang.AssertionError: b 0x copy after control - PlayerA have wrong life: 20 <> 17 expected:<17> but was:<20>
|
||||||
@Test
|
@Test
|
||||||
public void test_GetControlNotCounts() {
|
public void test_GetControlNotCounts() {
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
|
||||||
|
|
@ -296,7 +295,6 @@ public class ThousandYearStormTest extends CardTestPlayerBase {
|
||||||
addCard(Zone.HAND, playerB, "Lay Claim");
|
addCard(Zone.HAND, playerB, "Lay Claim");
|
||||||
|
|
||||||
// turn 2
|
// turn 2
|
||||||
|
|
||||||
// pump card for A
|
// pump card for A
|
||||||
// 1
|
// 1
|
||||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
|
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
|
||||||
|
|
@ -313,7 +311,6 @@ public class ThousandYearStormTest extends CardTestPlayerBase {
|
||||||
checkLife("b 0x copy after control", 3, PhaseStep.UPKEEP, playerA, 20 - 3);
|
checkLife("b 0x copy after control", 3, PhaseStep.UPKEEP, playerA, 20 - 3);
|
||||||
|
|
||||||
// turn 4
|
// turn 4
|
||||||
|
|
||||||
// pump for B
|
// pump for B
|
||||||
// 1
|
// 1
|
||||||
castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA);
|
castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.abilities;
|
package mage.abilities;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -62,14 +61,6 @@ public interface ActivatedAbility extends Ability {
|
||||||
@Override
|
@Override
|
||||||
ActivatedAbility copy();
|
ActivatedAbility copy();
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a flag to know, that the ability is only created adn used to check
|
|
||||||
* what's playbable for the player.
|
|
||||||
*/
|
|
||||||
void setCheckPlayableMode();
|
|
||||||
|
|
||||||
boolean isCheckPlayableMode();
|
|
||||||
|
|
||||||
void setMaxActivationsPerTurn(int maxActivationsPerTurn);
|
void setMaxActivationsPerTurn(int maxActivationsPerTurn);
|
||||||
|
|
||||||
int getMaxActivationsPerTurn(Game game);
|
int getMaxActivationsPerTurn(Game game);
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,9 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
|
||||||
protected TimingRule timing = TimingRule.INSTANT;
|
protected TimingRule timing = TimingRule.INSTANT;
|
||||||
protected TargetController mayActivate = TargetController.YOU;
|
protected TargetController mayActivate = TargetController.YOU;
|
||||||
protected UUID activatorId;
|
protected UUID activatorId;
|
||||||
protected boolean checkPlayableMode;
|
|
||||||
|
|
||||||
protected ActivatedAbilityImpl(AbilityType abilityType, Zone zone) {
|
protected ActivatedAbilityImpl(AbilityType abilityType, Zone zone) {
|
||||||
super(abilityType, zone);
|
super(abilityType, zone);
|
||||||
this.checkPlayableMode = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActivatedAbilityImpl(final ActivatedAbilityImpl ability) {
|
public ActivatedAbilityImpl(final ActivatedAbilityImpl ability) {
|
||||||
|
|
@ -58,7 +56,6 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
|
||||||
timing = ability.timing;
|
timing = ability.timing;
|
||||||
mayActivate = ability.mayActivate;
|
mayActivate = ability.mayActivate;
|
||||||
activatorId = ability.activatorId;
|
activatorId = ability.activatorId;
|
||||||
checkPlayableMode = ability.checkPlayableMode;
|
|
||||||
maxActivationsPerTurn = ability.maxActivationsPerTurn;
|
maxActivationsPerTurn = ability.maxActivationsPerTurn;
|
||||||
condition = ability.condition;
|
condition = ability.condition;
|
||||||
}
|
}
|
||||||
|
|
@ -262,16 +259,6 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
|
||||||
this.timing = timing;
|
this.timing = timing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCheckPlayableMode() {
|
|
||||||
checkPlayableMode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCheckPlayableMode() {
|
|
||||||
return checkPlayableMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean hasMoreActivationsThisTurn(Game game) {
|
protected boolean hasMoreActivationsThisTurn(Game game) {
|
||||||
if (getMaxActivationsPerTurn(game) == Integer.MAX_VALUE) {
|
if (getMaxActivationsPerTurn(game) == Integer.MAX_VALUE) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package mage.abilities;
|
package mage.abilities;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.MageObjectReference;
|
import mage.MageObjectReference;
|
||||||
import mage.abilities.costs.Cost;
|
import mage.abilities.costs.Cost;
|
||||||
|
|
@ -15,9 +17,6 @@ import mage.game.events.GameEvent;
|
||||||
import mage.game.stack.Spell;
|
import mage.game.stack.Spell;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
|
|
@ -91,7 +90,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if rule modifying events prevent to cast the spell in check playable mode
|
// Check if rule modifying events prevent to cast the spell in check playable mode
|
||||||
if (this.isCheckPlayableMode()) {
|
if (game.inCheckPlayableState()) {
|
||||||
if (game.getContinuousEffects().preventedByRuleModification(
|
if (game.getContinuousEffects().preventedByRuleModification(
|
||||||
GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, this.getId(), this.getSourceId(), playerId), this, game, true)) {
|
GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, this.getId(), this.getSourceId(), playerId), this, game, true)) {
|
||||||
return ActivationStatus.getFalse();
|
return ActivationStatus.getFalse();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
package mage.abilities.effects;
|
package mage.abilities.effects;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.MageObjectReference;
|
import mage.MageObjectReference;
|
||||||
import mage.abilities.*;
|
import mage.abilities.*;
|
||||||
|
|
@ -23,11 +27,6 @@ import mage.players.Player;
|
||||||
import mage.target.common.TargetCardInHand;
|
import mage.target.common.TargetCardInHand;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
|
|
@ -333,7 +332,7 @@ public class ContinuousEffects implements Serializable {
|
||||||
}
|
}
|
||||||
// boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT);
|
// boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT);
|
||||||
//get all applicable transient Replacement effects
|
//get all applicable transient Replacement effects
|
||||||
for (Iterator<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext(); ) {
|
for (Iterator<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext();) {
|
||||||
ReplacementEffect effect = iterator.next();
|
ReplacementEffect effect = iterator.next();
|
||||||
if (!effect.checksEventType(event, game)) {
|
if (!effect.checksEventType(event, game)) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -366,7 +365,7 @@ public class ContinuousEffects implements Serializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext(); ) {
|
for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext();) {
|
||||||
PreventionEffect effect = iterator.next();
|
PreventionEffect effect = iterator.next();
|
||||||
if (!effect.checksEventType(event, game)) {
|
if (!effect.checksEventType(event, game)) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -720,10 +719,10 @@ public class ContinuousEffects implements Serializable {
|
||||||
* Checks if an event won't happen because of an rule modifying effect
|
* Checks if an event won't happen because of an rule modifying effect
|
||||||
*
|
*
|
||||||
* @param event
|
* @param event
|
||||||
* @param targetAbility ability the event is attached to. can be null.
|
* @param targetAbility ability the event is attached to. can be null.
|
||||||
* @param game
|
* @param game
|
||||||
* @param checkPlayableMode true if the event does not really happen but
|
* @param checkPlayableMode true if the event does not really happen but
|
||||||
* it's checked if the event would be replaced
|
* it's checked if the event would be replaced
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean preventedByRuleModification(GameEvent event, Ability targetAbility, Game game, boolean checkPlayableMode) {
|
public boolean preventedByRuleModification(GameEvent event, Ability targetAbility, Game game, boolean checkPlayableMode) {
|
||||||
|
|
@ -737,10 +736,7 @@ public class ContinuousEffects implements Serializable {
|
||||||
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
||||||
effect.setValue("targetAbility", targetAbility);
|
effect.setValue("targetAbility", targetAbility);
|
||||||
if (effect.applies(event, sourceAbility, game)) {
|
if (effect.applies(event, sourceAbility, game)) {
|
||||||
if (targetAbility instanceof ActivatedAbility && ((ActivatedAbility) targetAbility).isCheckPlayableMode()) {
|
if (!game.inCheckPlayableState()) {
|
||||||
checkPlayableMode = true;
|
|
||||||
}
|
|
||||||
if (!checkPlayableMode) {
|
|
||||||
String message = effect.getInfoMessage(sourceAbility, event, game);
|
String message = effect.getInfoMessage(sourceAbility, event, game);
|
||||||
if (message != null && !message.isEmpty()) {
|
if (message != null && !message.isEmpty()) {
|
||||||
if (effect.sendMessageToUser()) {
|
if (effect.sendMessageToUser()) {
|
||||||
|
|
@ -770,7 +766,7 @@ public class ContinuousEffects implements Serializable {
|
||||||
do {
|
do {
|
||||||
Map<ReplacementEffect, Set<Ability>> rEffects = getApplicableReplacementEffects(event, game);
|
Map<ReplacementEffect, Set<Ability>> rEffects = getApplicableReplacementEffects(event, game);
|
||||||
// Remove all consumed effects (ability dependant)
|
// Remove all consumed effects (ability dependant)
|
||||||
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext(); ) {
|
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext();) {
|
||||||
ReplacementEffect entry = it1.next();
|
ReplacementEffect entry = it1.next();
|
||||||
if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9.
|
if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9.
|
||||||
Set<UUID> consumedAbilitiesIds = consumed.get(entry.getId());
|
Set<UUID> consumedAbilitiesIds = consumed.get(entry.getId());
|
||||||
|
|
@ -961,7 +957,7 @@ public class ContinuousEffects implements Serializable {
|
||||||
|
|
||||||
if (!waitingEffects.isEmpty()) {
|
if (!waitingEffects.isEmpty()) {
|
||||||
// check if waiting effects can be applied now
|
// check if waiting effects can be applied now
|
||||||
for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) {
|
for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) {
|
||||||
Map.Entry<ContinuousEffect, Set<UUID>> entry = iterator.next();
|
Map.Entry<ContinuousEffect, Set<UUID>> entry = iterator.next();
|
||||||
if (appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself
|
if (appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself
|
||||||
appliedAbilities = appliedEffectAbilities.get(entry.getKey());
|
appliedAbilities = appliedEffectAbilities.get(entry.getKey());
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.abilities.effects.common.cost;
|
package mage.abilities.effects.common.cost;
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
|
@ -6,7 +5,6 @@ import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.Mana;
|
import mage.Mana;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.ActivatedAbility;
|
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.choices.ChoiceImpl;
|
import mage.choices.ChoiceImpl;
|
||||||
|
|
@ -70,11 +68,9 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||||
if (upTo) {
|
if (upTo) {
|
||||||
if (abilityToModify instanceof ActivatedAbility) {
|
if (game.inCheckPlayableState()) {
|
||||||
if (((ActivatedAbility) abilityToModify).isCheckPlayableMode()) {
|
CardUtil.reduceCost(abilityToModify, this.amount);
|
||||||
CardUtil.reduceCost(abilityToModify, this.amount);
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Mana mana = abilityToModify.getManaCostsToPay().getMana();
|
Mana mana = abilityToModify.getManaCostsToPay().getMana();
|
||||||
int reduceMax = mana.getGeneric();
|
int reduceMax = mana.getGeneric();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.abilities.effects.common.cost;
|
package mage.abilities.effects.common.cost;
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
|
@ -6,7 +5,6 @@ import java.util.Set;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.Mana;
|
import mage.Mana;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.ActivatedAbility;
|
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
import mage.abilities.costs.mana.ManaCost;
|
import mage.abilities.costs.mana.ManaCost;
|
||||||
import mage.abilities.costs.mana.ManaCosts;
|
import mage.abilities.costs.mana.ManaCosts;
|
||||||
|
|
@ -85,7 +83,7 @@ public class SpellsCostReductionControllerEffect extends CostModificationEffectI
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int reduce = reduceMax;
|
int reduce = reduceMax;
|
||||||
if (!(abilityToModify instanceof ActivatedAbility) || !((ActivatedAbility) abilityToModify).isCheckPlayableMode()) {
|
if (!game.inCheckPlayableState()) {
|
||||||
ChoiceImpl choice = new ChoiceImpl(false);
|
ChoiceImpl choice = new ChoiceImpl(false);
|
||||||
Set<String> set = new LinkedHashSet<>();
|
Set<String> set = new LinkedHashSet<>();
|
||||||
for (int i = 0; i <= amount; i++) {
|
for (int i = 0; i <= amount; i++) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package mage.abilities.keyword;
|
package mage.abilities.keyword;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.StaticAbility;
|
import mage.abilities.StaticAbility;
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
|
|
@ -23,8 +24,6 @@ import mage.players.Player;
|
||||||
import mage.target.TargetCard;
|
import mage.target.TargetCard;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
* <p>
|
* <p>
|
||||||
|
|
@ -122,7 +121,7 @@ class HideawayLookAtFaceDownCardEffect extends AsThoughEffectImpl {
|
||||||
|
|
||||||
public HideawayLookAtFaceDownCardEffect() {
|
public HideawayLookAtFaceDownCardEffect() {
|
||||||
super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit);
|
super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit);
|
||||||
staticText = "You may look at cards exiled with {this}";
|
staticText = "You may look at the cards exiled with {this}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private HideawayLookAtFaceDownCardEffect(final HideawayLookAtFaceDownCardEffect effect) {
|
private HideawayLookAtFaceDownCardEffect(final HideawayLookAtFaceDownCardEffect effect) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package mage.abilities.keyword;
|
package mage.abilities.keyword;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
import mage.MageObjectReference;
|
import mage.MageObjectReference;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
|
|
@ -17,8 +18,6 @@ import mage.target.Target;
|
||||||
import mage.target.common.TargetControlledCreaturePermanent;
|
import mage.target.common.TargetControlledCreaturePermanent;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 702.46. Offering # 702.46a Offering is a static ability of a card that
|
* 702.46. Offering # 702.46a Offering is a static ability of a card that
|
||||||
* functions in any zone from which the card can be cast. "[Subtype] offering"
|
* functions in any zone from which the card can be cast. "[Subtype] offering"
|
||||||
|
|
@ -121,7 +120,7 @@ class OfferingAsThoughEffect extends AsThoughEffectImpl {
|
||||||
|
|
||||||
if (game.getBattlefield().count(((OfferingAbility) source).getFilter(), source.getSourceId(), source.getControllerId(), game) > 0) {
|
if (game.getBattlefield().count(((OfferingAbility) source).getFilter(), source.getSourceId(), source.getControllerId(), game) > 0) {
|
||||||
|
|
||||||
if (CardUtil.isCheckPlayableMode(affectedAbility)) {
|
if (game.inCheckPlayableState()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
FilterControlledCreaturePermanent filter = ((OfferingAbility) source).getFilter();
|
FilterControlledCreaturePermanent filter = ((OfferingAbility) source).getFilter();
|
||||||
|
|
@ -130,7 +129,7 @@ class OfferingAsThoughEffect extends AsThoughEffectImpl {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Player player = game.getPlayer(source.getControllerId());
|
Player player = game.getPlayer(source.getControllerId());
|
||||||
if (player != null && !CardUtil.isCheckPlayableMode(affectedAbility)
|
if (player != null && !game.inCheckPlayableState()
|
||||||
&& player.chooseUse(Outcome.Benefit, "Offer a " + filter.getMessage() + " to cast " + spellToCast.getName() + '?', source, game)) {
|
&& player.chooseUse(Outcome.Benefit, "Offer a " + filter.getMessage() + " to cast " + spellToCast.getName() + '?', source, game)) {
|
||||||
Target target = new TargetControlledCreaturePermanent(1, 1, filter, true);
|
Target target = new TargetControlledCreaturePermanent(1, 1, filter, true);
|
||||||
player.chooseTarget(Outcome.Sacrifice, target, source, game);
|
player.chooseTarget(Outcome.Sacrifice, target, source, game);
|
||||||
|
|
@ -193,7 +192,7 @@ class OfferingCostReductionEffect extends CostModificationEffectImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||||
if (CardUtil.isCheckPlayableMode(abilityToModify)) { // Cost modifaction does not work correctly for checking available spells
|
if (game.inCheckPlayableState()) { // Cost modifaction does not work correctly for checking available spells
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (abilityToModify.getId().equals(spellAbilityId) && abilityToModify instanceof SpellAbility) {
|
if (abilityToModify.getId().equals(spellAbilityId) && abilityToModify instanceof SpellAbility) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
package mage.game;
|
package mage.game;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import mage.MageItem;
|
import mage.MageItem;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
|
@ -41,10 +44,6 @@ import mage.players.Players;
|
||||||
import mage.util.MessageToClient;
|
import mage.util.MessageToClient;
|
||||||
import mage.util.functions.ApplyToPermanent;
|
import mage.util.functions.ApplyToPermanent;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public interface Game extends MageItem, Serializable {
|
public interface Game extends MageItem, Serializable {
|
||||||
|
|
||||||
MatchType getGameType();
|
MatchType getGameType();
|
||||||
|
|
@ -131,7 +130,6 @@ public interface Game extends MageItem, Serializable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
default boolean isActivePlayer(UUID playerId) {
|
default boolean isActivePlayer(UUID playerId) {
|
||||||
return getActivePlayerId() != null && getActivePlayerId().equals(playerId);
|
return getActivePlayerId() != null && getActivePlayerId().equals(playerId);
|
||||||
}
|
}
|
||||||
|
|
@ -202,7 +200,11 @@ public interface Game extends MageItem, Serializable {
|
||||||
|
|
||||||
boolean isSimulation();
|
boolean isSimulation();
|
||||||
|
|
||||||
void setSimulation(boolean simulation);
|
void setSimulation(boolean checkPlayableState);
|
||||||
|
|
||||||
|
boolean inCheckPlayableState();
|
||||||
|
|
||||||
|
void setCheckPlayableState(boolean checkPlayableState);
|
||||||
|
|
||||||
MageObject getLastKnownInformation(UUID objectId, Zone zone);
|
MageObject getLastKnownInformation(UUID objectId, Zone zone);
|
||||||
|
|
||||||
|
|
@ -296,9 +298,9 @@ public interface Game extends MageItem, Serializable {
|
||||||
/**
|
/**
|
||||||
* Creates and fires an damage prevention event
|
* Creates and fires an damage prevention event
|
||||||
*
|
*
|
||||||
* @param damageEvent damage event that will be replaced (instanceof check
|
* @param damageEvent damage event that will be replaced (instanceof check
|
||||||
* will be done)
|
* will be done)
|
||||||
* @param source ability that's the source of the prevention effect
|
* @param source ability that's the source of the prevention effect
|
||||||
* @param game
|
* @param game
|
||||||
* @param amountToPrevent max preventable amount
|
* @param amountToPrevent max preventable amount
|
||||||
* @return true prevention was successfull / false prevention was replaced
|
* @return true prevention was successfull / false prevention was replaced
|
||||||
|
|
@ -308,12 +310,12 @@ public interface Game extends MageItem, Serializable {
|
||||||
/**
|
/**
|
||||||
* Creates and fires an damage prevention event
|
* Creates and fires an damage prevention event
|
||||||
*
|
*
|
||||||
* @param event damage event that will be replaced (instanceof check will be
|
* @param event damage event that will be replaced (instanceof check will be
|
||||||
* done)
|
* done)
|
||||||
* @param source ability that's the source of the prevention effect
|
* @param source ability that's the source of the prevention effect
|
||||||
* @param game
|
* @param game
|
||||||
* @param preventAllDamage true if there is no limit to the damage that can
|
* @param preventAllDamage true if there is no limit to the damage that can
|
||||||
* be prevented
|
* be prevented
|
||||||
* @return true prevention was successfull / false prevention was replaced
|
* @return true prevention was successfull / false prevention was replaced
|
||||||
*/
|
*/
|
||||||
PreventionEffectData preventDamage(GameEvent event, Ability source, Game game, boolean preventAllDamage);
|
PreventionEffectData preventDamage(GameEvent event, Ability source, Game game, boolean preventAllDamage);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
package mage.game;
|
package mage.game;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import mage.MageException;
|
import mage.MageException;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.*;
|
import mage.abilities.*;
|
||||||
|
|
@ -65,11 +69,6 @@ import mage.util.functions.ApplyToPermanent;
|
||||||
import mage.watchers.common.*;
|
import mage.watchers.common.*;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
public abstract class GameImpl implements Game, Serializable {
|
public abstract class GameImpl implements Game, Serializable {
|
||||||
|
|
||||||
private static final int ROLLBACK_TURNS_MAX = 4;
|
private static final int ROLLBACK_TURNS_MAX = 4;
|
||||||
|
|
@ -77,7 +76,9 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
private static final Logger logger = Logger.getLogger(GameImpl.class);
|
private static final Logger logger = Logger.getLogger(GameImpl.class);
|
||||||
|
|
||||||
private transient Object customData;
|
private transient Object customData;
|
||||||
|
|
||||||
protected boolean simulation = false;
|
protected boolean simulation = false;
|
||||||
|
protected boolean checkPlayableState = false;
|
||||||
|
|
||||||
protected final UUID id;
|
protected final UUID id;
|
||||||
|
|
||||||
|
|
@ -163,6 +164,7 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
this.state = game.state.copy();
|
this.state = game.state.copy();
|
||||||
this.gameCards = game.gameCards;
|
this.gameCards = game.gameCards;
|
||||||
this.simulation = game.simulation;
|
this.simulation = game.simulation;
|
||||||
|
this.checkPlayableState = game.checkPlayableState;
|
||||||
this.gameOptions = game.gameOptions;
|
this.gameOptions = game.gameOptions;
|
||||||
this.lki.putAll(game.lki);
|
this.lki.putAll(game.lki);
|
||||||
this.lkiExtended.putAll(game.lkiExtended);
|
this.lkiExtended.putAll(game.lkiExtended);
|
||||||
|
|
@ -188,6 +190,16 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
this.simulation = simulation;
|
this.simulation = simulation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCheckPlayableState(boolean checkPlayableState) {
|
||||||
|
this.checkPlayableState = checkPlayableState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean inCheckPlayableState() {
|
||||||
|
return checkPlayableState;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UUID getId() {
|
public UUID getId() {
|
||||||
return id;
|
return id;
|
||||||
|
|
@ -1480,7 +1492,7 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
/**
|
/**
|
||||||
* @param emblem
|
* @param emblem
|
||||||
* @param sourceObject
|
* @param sourceObject
|
||||||
* @param toPlayerId controller and owner of the emblem
|
* @param toPlayerId controller and owner of the emblem
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void addEmblem(Emblem emblem, MageObject sourceObject, UUID toPlayerId) {
|
public void addEmblem(Emblem emblem, MageObject sourceObject, UUID toPlayerId) {
|
||||||
|
|
@ -1498,8 +1510,8 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
/**
|
/**
|
||||||
* @param plane
|
* @param plane
|
||||||
* @param sourceObject
|
* @param sourceObject
|
||||||
* @param toPlayerId controller and owner of the plane (may only be one per
|
* @param toPlayerId controller and owner of the plane (may only be one per
|
||||||
* game..)
|
* game..)
|
||||||
* @return boolean - whether the plane was added successfully or not
|
* @return boolean - whether the plane was added successfully or not
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -1728,7 +1740,7 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// triggered abilities that don't use the stack have to be executed first (e.g. Banisher Priest Return exiled creature
|
// triggered abilities that don't use the stack have to be executed first (e.g. Banisher Priest Return exiled creature
|
||||||
for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext(); ) {
|
for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext();) {
|
||||||
TriggeredAbility triggeredAbility = it.next();
|
TriggeredAbility triggeredAbility = it.next();
|
||||||
if (!triggeredAbility.isUsesStack()) {
|
if (!triggeredAbility.isUsesStack()) {
|
||||||
state.removeTriggeredAbility(triggeredAbility);
|
state.removeTriggeredAbility(triggeredAbility);
|
||||||
|
|
@ -2444,8 +2456,8 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
* exist. Then, if there are any objects still controlled by that player,
|
* exist. Then, if there are any objects still controlled by that player,
|
||||||
* those objects are exiled. This is not a state-based action. It happens as
|
* those objects are exiled. This is not a state-based action. It happens as
|
||||||
* soon as the player leaves the game. If the player who left the game had
|
* soon as the player leaves the game. If the player who left the game had
|
||||||
* priority at the time they left, priority passes to the next player
|
* priority at the time they left, priority passes to the next player in
|
||||||
* in turn order who's still in the game. #
|
* turn order who's still in the game. #
|
||||||
*
|
*
|
||||||
* @param playerId
|
* @param playerId
|
||||||
*/
|
*/
|
||||||
|
|
@ -2463,7 +2475,7 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
}
|
}
|
||||||
//20100423 - 800.4a
|
//20100423 - 800.4a
|
||||||
Set<Card> toOutside = new HashSet<>();
|
Set<Card> toOutside = new HashSet<>();
|
||||||
for (Iterator<Permanent> it = getBattlefield().getAllPermanents().iterator(); it.hasNext(); ) {
|
for (Iterator<Permanent> it = getBattlefield().getAllPermanents().iterator(); it.hasNext();) {
|
||||||
Permanent perm = it.next();
|
Permanent perm = it.next();
|
||||||
if (perm.isOwnedBy(playerId)) {
|
if (perm.isOwnedBy(playerId)) {
|
||||||
if (perm.getAttachedTo() != null) {
|
if (perm.getAttachedTo() != null) {
|
||||||
|
|
@ -2506,7 +2518,7 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
player.moveCards(toOutside, Zone.OUTSIDE, null, this);
|
player.moveCards(toOutside, Zone.OUTSIDE, null, this);
|
||||||
// triggered abilities that don't use the stack have to be executed
|
// triggered abilities that don't use the stack have to be executed
|
||||||
List<TriggeredAbility> abilities = state.getTriggered(player.getId());
|
List<TriggeredAbility> abilities = state.getTriggered(player.getId());
|
||||||
for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext(); ) {
|
for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext();) {
|
||||||
TriggeredAbility triggeredAbility = it.next();
|
TriggeredAbility triggeredAbility = it.next();
|
||||||
if (!triggeredAbility.isUsesStack()) {
|
if (!triggeredAbility.isUsesStack()) {
|
||||||
state.removeTriggeredAbility(triggeredAbility);
|
state.removeTriggeredAbility(triggeredAbility);
|
||||||
|
|
@ -2526,7 +2538,7 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
|
|
||||||
// Remove cards from the player in all exile zones
|
// Remove cards from the player in all exile zones
|
||||||
for (ExileZone exile : this.getExile().getExileZones()) {
|
for (ExileZone exile : this.getExile().getExileZones()) {
|
||||||
for (Iterator<UUID> it = exile.iterator(); it.hasNext(); ) {
|
for (Iterator<UUID> it = exile.iterator(); it.hasNext();) {
|
||||||
Card card = this.getCard(it.next());
|
Card card = this.getCard(it.next());
|
||||||
if (card != null && card.isOwnedBy(playerId)) {
|
if (card != null && card.isOwnedBy(playerId)) {
|
||||||
it.remove();
|
it.remove();
|
||||||
|
|
@ -2536,7 +2548,7 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
|
|
||||||
//Remove all commander/emblems/plane the player controls
|
//Remove all commander/emblems/plane the player controls
|
||||||
boolean addPlaneAgain = false;
|
boolean addPlaneAgain = false;
|
||||||
for (Iterator<CommandObject> it = this.getState().getCommand().iterator(); it.hasNext(); ) {
|
for (Iterator<CommandObject> it = this.getState().getCommand().iterator(); it.hasNext();) {
|
||||||
CommandObject obj = it.next();
|
CommandObject obj = it.next();
|
||||||
if (obj.isControlledBy(playerId)) {
|
if (obj.isControlledBy(playerId)) {
|
||||||
if (obj instanceof Emblem) {
|
if (obj instanceof Emblem) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package mage.players;
|
package mage.players;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import mage.ConditionalMana;
|
import mage.ConditionalMana;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.MageObjectReference;
|
import mage.MageObjectReference;
|
||||||
|
|
@ -65,10 +68,6 @@ import mage.util.GameLog;
|
||||||
import mage.util.RandomUtil;
|
import mage.util.RandomUtil;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
public abstract class PlayerImpl implements Player, Serializable {
|
public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(PlayerImpl.class);
|
private static final Logger logger = Logger.getLogger(PlayerImpl.class);
|
||||||
|
|
@ -619,7 +618,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (abilities.containsKey(HexproofAbility.getInstance().getId())) {
|
if (abilities.containsKey(HexproofAbility.getInstance().getId())) {
|
||||||
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
||||||
&& null == game.getContinuousEffects().asThough(this.getId(),
|
&& null == game.getContinuousEffects().asThough(this.getId(),
|
||||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)) {
|
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -627,7 +626,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (abilities.containsKey(HexproofFromWhiteAbility.getInstance().getId())) {
|
if (abilities.containsKey(HexproofFromWhiteAbility.getInstance().getId())) {
|
||||||
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
||||||
&& null == game.getContinuousEffects().asThough(this.getId(),
|
&& null == game.getContinuousEffects().asThough(this.getId(),
|
||||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
||||||
&& source.getColor(game).isWhite()) {
|
&& source.getColor(game).isWhite()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -636,7 +635,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (abilities.containsKey(HexproofFromBlueAbility.getInstance().getId())) {
|
if (abilities.containsKey(HexproofFromBlueAbility.getInstance().getId())) {
|
||||||
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
||||||
&& null == game.getContinuousEffects().asThough(this.getId(),
|
&& null == game.getContinuousEffects().asThough(this.getId(),
|
||||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
||||||
&& source.getColor(game).isBlue()) {
|
&& source.getColor(game).isBlue()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -645,7 +644,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (abilities.containsKey(HexproofFromBlackAbility.getInstance().getId())) {
|
if (abilities.containsKey(HexproofFromBlackAbility.getInstance().getId())) {
|
||||||
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
||||||
&& null == game.getContinuousEffects().asThough(this.getId(),
|
&& null == game.getContinuousEffects().asThough(this.getId(),
|
||||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
||||||
&& source.getColor(game).isBlack()) {
|
&& source.getColor(game).isBlack()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -654,7 +653,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (abilities.containsKey(HexproofFromMonocoloredAbility.getInstance().getId())) {
|
if (abilities.containsKey(HexproofFromMonocoloredAbility.getInstance().getId())) {
|
||||||
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
|
||||||
&& null == game.getContinuousEffects().asThough(this.getId(),
|
&& null == game.getContinuousEffects().asThough(this.getId(),
|
||||||
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)
|
||||||
&& !source.getColor(game).isColorless()
|
&& !source.getColor(game).isColorless()
|
||||||
&& !source.getColor(game).isMulticolored()) {
|
&& !source.getColor(game).isMulticolored()) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -697,7 +696,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
game.informPlayers(getLogName() + " discards down to "
|
game.informPlayers(getLogName() + " discards down to "
|
||||||
+ this.maxHandSize
|
+ this.maxHandSize
|
||||||
+ (this.maxHandSize == 1
|
+ (this.maxHandSize == 1
|
||||||
? " hand card" : " hand cards"));
|
? " hand card" : " hand cards"));
|
||||||
}
|
}
|
||||||
discard(hand.size() - this.maxHandSize, null, game);
|
discard(hand.size() - this.maxHandSize, null, game);
|
||||||
}
|
}
|
||||||
|
|
@ -815,7 +814,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD,
|
GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD,
|
||||||
card.getId(), source == null
|
card.getId(), source == null
|
||||||
? null : source.getSourceId(), playerId);
|
? null : source.getSourceId(), playerId);
|
||||||
gameEvent.setFlag(source != null); // event from effect or from cost (source == null)
|
gameEvent.setFlag(source != null); // event from effect or from cost (source == null)
|
||||||
if (!game.replaceEvent(gameEvent, source)) {
|
if (!game.replaceEvent(gameEvent, source)) {
|
||||||
// write info to game log first so game log infos from triggered or replacement effects follow in the game log
|
// write info to game log first so game log infos from triggered or replacement effects follow in the game log
|
||||||
|
|
@ -830,7 +829,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
// So discard is also successful if card is moved to another zone by replacement effect!
|
// So discard is also successful if card is moved to another zone by replacement effect!
|
||||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARD,
|
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARD,
|
||||||
card.getId(), source == null
|
card.getId(), source == null
|
||||||
? null : source.getSourceId(), playerId));
|
? null : source.getSourceId(), playerId));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1518,7 +1517,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
// Also called on the whole split card but only passing the fuse ability and other whole-split-card shared abilities
|
// Also called on the whole split card but only passing the fuse ability and other whole-split-card shared abilities
|
||||||
// as candidates.
|
// as candidates.
|
||||||
private void getUseableActivatedAbilitiesHalfImpl(MageObject object, Zone zone, Game game, Abilities<Ability> candidateAbilites,
|
private void getUseableActivatedAbilitiesHalfImpl(MageObject object, Zone zone, Game game, Abilities<Ability> candidateAbilites,
|
||||||
LinkedHashMap<UUID, ActivatedAbility> output) {
|
LinkedHashMap<UUID, ActivatedAbility> output) {
|
||||||
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
|
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
|
||||||
ManaOptions availableMana = null;
|
ManaOptions availableMana = null;
|
||||||
// ManaOptions availableMana = getManaAvailable(game); // can only be activated if mana calculation works flawless otherwise player can't play spells they could play if calculation would work correctly
|
// ManaOptions availableMana = getManaAvailable(game); // can only be activated if mana calculation works flawless otherwise player can't play spells they could play if calculation would work correctly
|
||||||
|
|
@ -1567,10 +1566,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
!= null
|
!= null
|
||||||
// if anyone sees an issue with this code, please report it. Worked in my testing.
|
// if anyone sees an issue with this code, please report it. Worked in my testing.
|
||||||
|| game.getContinuousEffects().asThough(object.getId(),
|
|| game.getContinuousEffects().asThough(object.getId(),
|
||||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE,
|
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE,
|
||||||
ability,
|
ability,
|
||||||
this.getId(),
|
this.getId(),
|
||||||
game)
|
game)
|
||||||
!= null) {
|
!= null) {
|
||||||
if (canUse
|
if (canUse
|
||||||
|| ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
|
|| ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
|
||||||
|
|
@ -1589,6 +1588,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
|
public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
|
||||||
|
game.setCheckPlayableState(true);
|
||||||
LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>();
|
LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>();
|
||||||
if (object instanceof StackAbility) { // It may not be possible to activate abilities of stack abilities
|
if (object instanceof StackAbility) { // It may not be possible to activate abilities of stack abilities
|
||||||
return useable;
|
return useable;
|
||||||
|
|
@ -1606,7 +1606,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
zone, game, object.getAbilities(), useable);
|
zone, game, object.getAbilities(), useable);
|
||||||
}
|
}
|
||||||
getOtherUseableActivatedAbilities(object, zone, game, useable);
|
getOtherUseableActivatedAbilities(object, zone, game, useable);
|
||||||
|
game.setCheckPlayableState(false);
|
||||||
return useable;
|
return useable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1925,9 +1925,9 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Permanent> getPermanentsThatCanBeUntapped(Game game,
|
private List<Permanent> getPermanentsThatCanBeUntapped(Game game,
|
||||||
List<Permanent> canBeUntapped,
|
List<Permanent> canBeUntapped,
|
||||||
RestrictionUntapNotMoreThanEffect handledEffect,
|
RestrictionUntapNotMoreThanEffect handledEffect,
|
||||||
Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
|
Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
|
||||||
List<Permanent> leftForUntap = new ArrayList<>();
|
List<Permanent> leftForUntap = new ArrayList<>();
|
||||||
// select permanents that can still be untapped
|
// select permanents that can still be untapped
|
||||||
for (Permanent permanent : canBeUntapped) {
|
for (Permanent permanent : canBeUntapped) {
|
||||||
|
|
@ -2635,7 +2635,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId,
|
public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId,
|
||||||
boolean triggerEvents) {
|
boolean triggerEvents) {
|
||||||
//20091005 - 701.14c
|
//20091005 - 701.14c
|
||||||
Library searchedLibrary = null;
|
Library searchedLibrary = null;
|
||||||
String searchInfo = null;
|
String searchInfo = null;
|
||||||
|
|
@ -2839,7 +2839,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
/**
|
/**
|
||||||
* @param game
|
* @param game
|
||||||
* @param appliedEffects
|
* @param appliedEffects
|
||||||
* @param numSides Number of sides the dice has
|
* @param numSides Number of sides the dice has
|
||||||
* @return the number that the player rolled
|
* @return the number that the player rolled
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -2876,16 +2876,16 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
/**
|
/**
|
||||||
* @param game
|
* @param game
|
||||||
* @param appliedEffects
|
* @param appliedEffects
|
||||||
* @param numberChaosSides The number of chaos sides the planar die
|
* @param numberChaosSides The number of chaos sides the planar die
|
||||||
* currently has (normally 1 but can be 5)
|
* currently has (normally 1 but can be 5)
|
||||||
* @param numberPlanarSides The number of chaos sides the planar die
|
* @param numberPlanarSides The number of chaos sides the planar die
|
||||||
* currently has (normally 1)
|
* currently has (normally 1)
|
||||||
* @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
|
* @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
|
||||||
* or NilRoll
|
* or NilRoll
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public PlanarDieRoll rollPlanarDie(Game game, ArrayList<UUID> appliedEffects, int numberChaosSides,
|
public PlanarDieRoll rollPlanarDie(Game game, ArrayList<UUID> appliedEffects, int numberChaosSides,
|
||||||
int numberPlanarSides) {
|
int numberPlanarSides) {
|
||||||
int result = RandomUtil.nextInt(9) + 1;
|
int result = RandomUtil.nextInt(9) + 1;
|
||||||
PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL;
|
PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL;
|
||||||
if (numberChaosSides + numberPlanarSides > 9) {
|
if (numberChaosSides + numberPlanarSides > 9) {
|
||||||
|
|
@ -3042,15 +3042,14 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ability
|
* @param ability
|
||||||
* @param available if null, it won't be checked if enough mana is available
|
* @param available if null, it won't be checked if enough mana is available
|
||||||
* @param sourceObject
|
* @param sourceObject
|
||||||
* @param game
|
* @param game
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected boolean canPlay(ActivatedAbility ability, ManaOptions available, MageObject sourceObject, Game game) {
|
protected boolean canPlay(ActivatedAbility ability, ManaOptions available, MageObject sourceObject, Game game) {
|
||||||
if (!(ability instanceof ActivatedManaAbilityImpl)) {
|
if (!(ability instanceof ActivatedManaAbilityImpl)) {
|
||||||
ActivatedAbility copy = ability.copy();
|
ActivatedAbility copy = ability.copy(); // Copy is needed because cost reduction effects modify e.g. the mana to activate/cast the ability
|
||||||
copy.setCheckPlayableMode(); // prevents from endless loops for asking player to use effects by checking this mode
|
|
||||||
if (!copy.canActivate(playerId, game).canActivate()) {
|
if (!copy.canActivate(playerId, game).canActivate()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -3330,7 +3329,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
public List<Ability> getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) {
|
public List<Ability> getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) {
|
||||||
List<Ability> playable = new ArrayList<>();
|
List<Ability> playable = new ArrayList<>();
|
||||||
|
game.setCheckPlayableState(true);
|
||||||
if (!shouldSkipGettingPlayable(game)) {
|
if (!shouldSkipGettingPlayable(game)) {
|
||||||
ManaOptions availableMana = getManaAvailable(game);
|
ManaOptions availableMana = getManaAvailable(game);
|
||||||
availableMana.addMana(manaPool.getMana());
|
availableMana.addMana(manaPool.getMana());
|
||||||
|
|
@ -3347,8 +3346,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (ability instanceof ActivatedAbility) {
|
if (ability instanceof ActivatedAbility) {
|
||||||
if (!(ability instanceof PlayLandAbility)
|
if (!(ability instanceof PlayLandAbility)
|
||||||
|| !game.getContinuousEffects().preventedByRuleModification(
|
|| !game.getContinuousEffects().preventedByRuleModification(
|
||||||
GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, ability.getSourceId(),
|
GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, ability.getSourceId(),
|
||||||
ability.getSourceId(), playerId), ability, game, true)) {
|
ability.getSourceId(), playerId), ability, game, true)) {
|
||||||
if (canPlay((ActivatedAbility) ability, availableMana, card, game)) {
|
if (canPlay((ActivatedAbility) ability, availableMana, card, game)) {
|
||||||
playable.add(ability);
|
playable.add(ability);
|
||||||
}
|
}
|
||||||
|
|
@ -3455,17 +3454,19 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
playable.addAll(activatedAll);
|
playable.addAll(activatedAll);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
game.setCheckPlayableState(false);
|
||||||
return playable;
|
return playable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a list of card ids that are currently playable.<br>
|
* Creates a list of card ids that are currently playable.<br>
|
||||||
* Used to mark the playable cards in GameView
|
* Used to mark the playable cards in GameView Also contains number of
|
||||||
* Also contains number of playable abilities for that object (it's just info, server decides to show choose dialog or not)
|
* playable abilities for that object (it's just info, server decides to
|
||||||
|
* show choose dialog or not)
|
||||||
*
|
*
|
||||||
* @param game
|
* @param game
|
||||||
* @return A Set of cardIds that are playable and amount of playable abilities
|
* @return A Set of cardIds that are playable and amount of playable
|
||||||
|
* abilities
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Map<UUID, Integer> getPlayableObjects(Game game, Zone zone) {
|
public Map<UUID, Integer> getPlayableObjects(Game game, Zone zone) {
|
||||||
|
|
@ -3669,7 +3670,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId,
|
public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId,
|
||||||
UUID controllerId, Game game
|
UUID controllerId, Game game
|
||||||
) {
|
) {
|
||||||
return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game);
|
return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game);
|
||||||
}
|
}
|
||||||
|
|
@ -3747,11 +3748,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
public boolean lookAtFaceDownCard(Card card, Game game, int abilitiesToActivate) {
|
public boolean lookAtFaceDownCard(Card card, Game game, int abilitiesToActivate) {
|
||||||
if (null != game.getContinuousEffects().asThough(card.getId(),
|
if (null != game.getContinuousEffects().asThough(card.getId(),
|
||||||
AsThoughEffectType.LOOK_AT_FACE_DOWN, card.getSpellAbility(), this.getId(), game)) {
|
AsThoughEffectType.LOOK_AT_FACE_DOWN, card.getSpellAbility(), this.getId(), game)) {
|
||||||
// two modes: look at card or not to look and activate other abilities
|
// two modes: look at the card or do not look and activate other abilities
|
||||||
String lookMessage = abilitiesToActivate > 0 ? "Look at that card (it's have "
|
String lookMessage = "Look at " + card.getIdName();
|
||||||
+ abilitiesToActivate + " abilities to activate)?" : "Look at that card?";
|
String lookYes = "Yes, look at the card";
|
||||||
String lookYes = "Yes, look at card";
|
String lookNo = "No, play/activate the card/ability";
|
||||||
String lookNo = abilitiesToActivate > 0 ? "No, activate ability" : "No";
|
|
||||||
if (chooseUse(Outcome.Benefit, lookMessage, "", lookYes, lookNo, null, game)) {
|
if (chooseUse(Outcome.Benefit, lookMessage, "", lookYes, lookNo, null, game)) {
|
||||||
Cards cards = new CardsImpl(card);
|
Cards cards = new CardsImpl(card);
|
||||||
this.lookAtCards(getName() + " - " + card.getIdName() + " - "
|
this.lookAtCards(getName() + " - " + card.getIdName() + " - "
|
||||||
|
|
@ -3823,8 +3823,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCards(Card card, Zone toZone,
|
public boolean moveCards(Card card, Zone toZone,
|
||||||
Ability source, Game game,
|
Ability source, Game game,
|
||||||
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
||||||
) {
|
) {
|
||||||
Set<Card> cardList = new HashSet<>();
|
Set<Card> cardList = new HashSet<>();
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
|
|
@ -3835,22 +3835,22 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCards(Cards cards, Zone toZone,
|
public boolean moveCards(Cards cards, Zone toZone,
|
||||||
Ability source, Game game
|
Ability source, Game game
|
||||||
) {
|
) {
|
||||||
return moveCards(cards.getCards(game), toZone, source, game);
|
return moveCards(cards.getCards(game), toZone, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCards(Set<Card> cards, Zone toZone,
|
public boolean moveCards(Set<Card> cards, Zone toZone,
|
||||||
Ability source, Game game
|
Ability source, Game game
|
||||||
) {
|
) {
|
||||||
return moveCards(cards, toZone, source, game, false, false, false, null);
|
return moveCards(cards, toZone, source, game, false, false, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCards(Set<Card> cards, Zone toZone,
|
public boolean moveCards(Set<Card> cards, Zone toZone,
|
||||||
Ability source, Game game,
|
Ability source, Game game,
|
||||||
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
||||||
) {
|
) {
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -3945,8 +3945,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCardsToExile(Card card, Ability source,
|
public boolean moveCardsToExile(Card card, Ability source,
|
||||||
Game game, boolean withName, UUID exileId,
|
Game game, boolean withName, UUID exileId,
|
||||||
String exileZoneName
|
String exileZoneName
|
||||||
) {
|
) {
|
||||||
Set<Card> cards = new HashSet<>();
|
Set<Card> cards = new HashSet<>();
|
||||||
cards.add(card);
|
cards.add(card);
|
||||||
|
|
@ -3955,8 +3955,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCardsToExile(Set<Card> cards, Ability source,
|
public boolean moveCardsToExile(Set<Card> cards, Ability source,
|
||||||
Game game, boolean withName, UUID exileId,
|
Game game, boolean withName, UUID exileId,
|
||||||
String exileZoneName
|
String exileZoneName
|
||||||
) {
|
) {
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -3972,14 +3972,14 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
|
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
|
||||||
Game game
|
Game game
|
||||||
) {
|
) {
|
||||||
return this.moveCardToHandWithInfo(card, sourceId, game, true);
|
return this.moveCardToHandWithInfo(card, sourceId, game, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
|
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
|
||||||
Game game, boolean withName
|
Game game, boolean withName
|
||||||
) {
|
) {
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
Zone fromZone = game.getState().getZone(card.getId());
|
Zone fromZone = game.getState().getZone(card.getId());
|
||||||
|
|
@ -4004,7 +4004,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source,
|
public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source,
|
||||||
Game game, Zone fromZone
|
Game game, Zone fromZone
|
||||||
) {
|
) {
|
||||||
UUID sourceId = source == null ? null : source.getSourceId();
|
UUID sourceId = source == null ? null : source.getSourceId();
|
||||||
Set<Card> movedCards = new LinkedHashSet<>();
|
Set<Card> movedCards = new LinkedHashSet<>();
|
||||||
|
|
@ -4012,7 +4012,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
// identify cards from one owner
|
// identify cards from one owner
|
||||||
Cards cards = new CardsImpl();
|
Cards cards = new CardsImpl();
|
||||||
UUID ownerId = null;
|
UUID ownerId = null;
|
||||||
for (Iterator<Card> it = allCards.iterator(); it.hasNext(); ) {
|
for (Iterator<Card> it = allCards.iterator(); it.hasNext();) {
|
||||||
Card card = it.next();
|
Card card = it.next();
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
ownerId = card.getOwnerId();
|
ownerId = card.getOwnerId();
|
||||||
|
|
@ -4075,7 +4075,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId,
|
public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId,
|
||||||
Game game, Zone fromZone
|
Game game, Zone fromZone
|
||||||
) {
|
) {
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -4104,8 +4104,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId,
|
public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId,
|
||||||
Game game, Zone fromZone,
|
Game game, Zone fromZone,
|
||||||
boolean toTop, boolean withName
|
boolean toTop, boolean withName
|
||||||
) {
|
) {
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -4139,7 +4139,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId,
|
public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId,
|
||||||
Game game, Zone fromZone, boolean withName) {
|
Game game, Zone fromZone, boolean withName) {
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -4162,7 +4162,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName()
|
game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName()
|
||||||
+ (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '
|
+ (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '
|
||||||
+ (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH)
|
+ (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH)
|
||||||
+ ' ' : "") + "to the exile zone");
|
+ ' ' : "") + "to the exile zone");
|
||||||
|
|
||||||
}
|
}
|
||||||
result = true;
|
result = true;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package mage.util;
|
package mage.util;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.Mana;
|
import mage.Mana;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.ActivatedAbility;
|
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
import mage.abilities.costs.VariableCost;
|
import mage.abilities.costs.VariableCost;
|
||||||
import mage.abilities.costs.mana.*;
|
import mage.abilities.costs.mana.*;
|
||||||
|
|
@ -15,10 +17,6 @@ import mage.game.permanent.Permanent;
|
||||||
import mage.game.permanent.token.Token;
|
import mage.game.permanent.token.Token;
|
||||||
import mage.util.functions.CopyTokenFunction;
|
import mage.util.functions.CopyTokenFunction;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author nantuko
|
* @author nantuko
|
||||||
*/
|
*/
|
||||||
|
|
@ -27,10 +25,10 @@ public final class CardUtil {
|
||||||
private static final String SOURCE_EXILE_ZONE_TEXT = "SourceExileZone";
|
private static final String SOURCE_EXILE_ZONE_TEXT = "SourceExileZone";
|
||||||
|
|
||||||
static final String[] numberStrings = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
static final String[] numberStrings = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||||
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"};
|
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"};
|
||||||
|
|
||||||
static final String[] ordinalStrings = {"first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eightth", "ninth",
|
static final String[] ordinalStrings = {"first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eightth", "ninth",
|
||||||
"tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth", "twentieth"};
|
"tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth", "twentieth"};
|
||||||
|
|
||||||
public static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
|
public static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
|
||||||
|
|
||||||
|
|
@ -145,8 +143,8 @@ public final class CardUtil {
|
||||||
*
|
*
|
||||||
* @param spellAbility
|
* @param spellAbility
|
||||||
* @param manaCostsToReduce costs to reduce
|
* @param manaCostsToReduce costs to reduce
|
||||||
* @param convertToGeneric colored mana does reduce generic mana if no
|
* @param convertToGeneric colored mana does reduce generic mana if no
|
||||||
* appropriate colored mana is in the costs included
|
* appropriate colored mana is in the costs included
|
||||||
*/
|
*/
|
||||||
public static void adjustCost(SpellAbility spellAbility, ManaCosts<ManaCost> manaCostsToReduce, boolean convertToGeneric) {
|
public static void adjustCost(SpellAbility spellAbility, ManaCosts<ManaCost> manaCostsToReduce, boolean convertToGeneric) {
|
||||||
ManaCosts<ManaCost> previousCost = spellAbility.getManaCostsToPay();
|
ManaCosts<ManaCost> previousCost = spellAbility.getManaCostsToPay();
|
||||||
|
|
@ -331,7 +329,7 @@ public final class CardUtil {
|
||||||
*
|
*
|
||||||
* @param number number to convert to text
|
* @param number number to convert to text
|
||||||
* @param forOne if the number is 1, this string will be returnedinstead of
|
* @param forOne if the number is 1, this string will be returnedinstead of
|
||||||
* "one".
|
* "one".
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String numberToText(int number, String forOne) {
|
public static String numberToText(int number, String forOne) {
|
||||||
|
|
@ -416,7 +414,7 @@ public final class CardUtil {
|
||||||
/**
|
/**
|
||||||
* Creates and saves a (card + zoneChangeCounter) specific exileId.
|
* Creates and saves a (card + zoneChangeCounter) specific exileId.
|
||||||
*
|
*
|
||||||
* @param game the current game
|
* @param game the current game
|
||||||
* @param source source ability
|
* @param source source ability
|
||||||
* @return the specific UUID
|
* @return the specific UUID
|
||||||
*/
|
*/
|
||||||
|
|
@ -451,9 +449,9 @@ public final class CardUtil {
|
||||||
* be specific to a permanent instance. So they won't match, if a permanent
|
* be specific to a permanent instance. So they won't match, if a permanent
|
||||||
* was e.g. exiled and came back immediately.
|
* was e.g. exiled and came back immediately.
|
||||||
*
|
*
|
||||||
* @param text short value to describe the value
|
* @param text short value to describe the value
|
||||||
* @param cardId id of the card
|
* @param cardId id of the card
|
||||||
* @param game the game
|
* @param game the game
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String getCardZoneString(String text, UUID cardId, Game game) {
|
public static String getCardZoneString(String text, UUID cardId, Game game) {
|
||||||
|
|
@ -489,20 +487,6 @@ public final class CardUtil {
|
||||||
return uniqueString.toString();
|
return uniqueString.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns if the ability is used to check which cards are playable on hand.
|
|
||||||
* (Issue #457)
|
|
||||||
*
|
|
||||||
* @param ability - ability to check
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static boolean isCheckPlayableMode(Ability ability) {
|
|
||||||
if (ability instanceof ActivatedAbility) {
|
|
||||||
return ((ActivatedAbility) ability).isCheckPlayableMode();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds tags to mark the additional info of a card (e.g. blue font color)
|
* Adds tags to mark the additional info of a card (e.g. blue font color)
|
||||||
*
|
*
|
||||||
|
|
@ -560,7 +544,8 @@ public final class CardUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Face down cards and their copy tokens don't have names and that's "empty" names is not equals
|
* Face down cards and their copy tokens don't have names and that's "empty"
|
||||||
|
* names is not equals
|
||||||
*/
|
*/
|
||||||
public static boolean haveSameNames(String name1, String name2, Boolean ignoreMtgRuleForEmptyNames) {
|
public static boolean haveSameNames(String name1, String name2, Boolean ignoreMtgRuleForEmptyNames) {
|
||||||
if (ignoreMtgRuleForEmptyNames) {
|
if (ignoreMtgRuleForEmptyNames) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue