forked from External/mage
Adding dice rolling trigger/replacement (ready for review) (#7989)
* [AFR] Implemented DiceRolledTriggeredAbility * [AFR] Implemented Brazen Dwarf * [AFR] Implemented Feywild Trickster * [AFC] Implemented Reckless Endeavor * [AFR] Implemented Pixie Guide * [AFR] Implemented Critical Hit * [AFR] Implemented Netherese Puzzle Ward * [AFC] Implemented Neverwinter Hydra * [AFR] Implemented Farideh, Devil's Chosen * [AFR] Implemented Barbarian Class * [AFC] Implemented Vrondiss, Rage of Ancients * [AFC] Implemented Arcane Endeavor * Test framework: added planar die rolls support * Test framework: added random results set up support in AI simulated games; * AI: improved roll die results chooses in computer games; * Roll die: improved combo support for planar die and roll die effects; Co-authored-by: Daniel Bomar <dbdaniel42@gmail.com> Co-authored-by: Oleg Agafonov <jaydi85@gmail.com>
This commit is contained in:
parent
12219cff01
commit
f8d030bef4
97 changed files with 2641 additions and 553 deletions
|
|
@ -22,6 +22,8 @@ import mage.abilities.mana.ManaOptions;
|
|||
import mage.actions.MageDrawAction;
|
||||
import mage.cards.*;
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.choices.Choice;
|
||||
import mage.choices.ChoiceImpl;
|
||||
import mage.constants.*;
|
||||
import mage.counters.Counter;
|
||||
import mage.counters.CounterType;
|
||||
|
|
@ -2438,7 +2440,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
userData.resetRequestedHandPlayersList(game.getId()); // users can send request again
|
||||
break;
|
||||
}
|
||||
logger.trace("PASS Priority: " + playerAction.toString());
|
||||
logger.trace("PASS Priority: " + playerAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -2786,21 +2788,15 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return casted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flipCoin(Ability source, Game game, boolean winnable) {
|
||||
return this.flipCoin(source, game, winnable, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param source
|
||||
* @param game
|
||||
* @param winnable
|
||||
* @param appliedEffects
|
||||
* @return if winnable, true if player won the toss, if not winnable, true
|
||||
* for heads and false for tails
|
||||
*/
|
||||
@Override
|
||||
public boolean flipCoin(Ability source, Game game, boolean winnable, List<UUID> appliedEffects) {
|
||||
public boolean flipCoin(Ability source, Game game, boolean winnable) {
|
||||
boolean chosen = false;
|
||||
if (winnable) {
|
||||
chosen = this.chooseUse(Outcome.Benefit, "Heads or tails?", "", "Heads", "Tails", source, game);
|
||||
|
|
@ -2808,7 +2804,6 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
boolean result = this.flipCoinResult(game);
|
||||
FlipCoinEvent event = new FlipCoinEvent(playerId, source, result, chosen, winnable);
|
||||
event.addAppliedEffects(appliedEffects);
|
||||
game.replaceEvent(event);
|
||||
game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(event.getResult())
|
||||
+ CardUtil.getSourceLogName(game, source));
|
||||
|
|
@ -2835,7 +2830,6 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
game.informPlayers(getLogName() + " " + (event.getResult() == event.getChosen() ? "won" : "lost") + " the flip"
|
||||
+ CardUtil.getSourceLogName(game, source));
|
||||
}
|
||||
event.setAppliedEffects(appliedEffects);
|
||||
game.fireEvent(event.createFlippedEvent());
|
||||
if (event.isWinnable()) {
|
||||
return event.getResult() == event.getChosen();
|
||||
|
|
@ -2853,85 +2847,334 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return RandomUtil.nextBoolean();
|
||||
}
|
||||
|
||||
private static final class RollDieResult {
|
||||
|
||||
// 706.2.
|
||||
// After the roll, the number indicated on the top face of the die before any modifiers is
|
||||
// the natural result. The instruction may include modifiers to the roll which add to or
|
||||
// subtract from the natural result. Modifiers may also come from other sources. After
|
||||
// considering all applicable modifiers, the final number is the result of the die roll.
|
||||
private final int naturalResult;
|
||||
private final int modifier;
|
||||
private final PlanarDieRollResult planarResult;
|
||||
|
||||
RollDieResult(int naturalResult, int modifier, PlanarDieRollResult planarResult) {
|
||||
this.naturalResult = naturalResult;
|
||||
this.modifier = modifier;
|
||||
this.planarResult = planarResult;
|
||||
}
|
||||
|
||||
public int getResult() {
|
||||
return this.naturalResult + this.modifier;
|
||||
}
|
||||
|
||||
public PlanarDieRollResult getPlanarResult() {
|
||||
return this.planarResult;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rollDice(Ability source, Game game, int numSides) {
|
||||
return this.rollDice(source, game, null, numSides);
|
||||
public int rollDieResult(int sides, Game game) {
|
||||
return RandomUtil.nextInt(sides) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll single die. Support both die types: planar and numerical.
|
||||
*
|
||||
* @param outcome
|
||||
* @param game
|
||||
* @param source
|
||||
* @param rollDieType
|
||||
* @param sidesAmount
|
||||
* @param chaosSidesAmount
|
||||
* @param planarSidesAmount
|
||||
* @param rollsAmount
|
||||
* @return
|
||||
*/
|
||||
private Object rollDieInner(Outcome outcome, Game game, Ability source, RollDieType rollDieType,
|
||||
int sidesAmount, int chaosSidesAmount, int planarSidesAmount, int rollsAmount) {
|
||||
if (rollsAmount == 1) {
|
||||
return rollDieInnerWithReplacement(game, source, rollDieType, sidesAmount, chaosSidesAmount, planarSidesAmount);
|
||||
}
|
||||
Set<Object> choices = new HashSet<>();
|
||||
for (int j = 0; j < rollsAmount; j++) {
|
||||
choices.add(rollDieInnerWithReplacement(game, source, rollDieType, sidesAmount, chaosSidesAmount, planarSidesAmount));
|
||||
}
|
||||
if (choices.size() == 1) {
|
||||
return choices.stream().findFirst().orElse(0);
|
||||
}
|
||||
|
||||
// AI hint - use max/min values
|
||||
if (this.isComputer()) {
|
||||
if (rollDieType == RollDieType.NUMERICAL) {
|
||||
// numerical
|
||||
if (outcome.isGood()) {
|
||||
return choices.stream()
|
||||
.map(Integer.class::cast)
|
||||
.max(Comparator.naturalOrder())
|
||||
.orElse(null);
|
||||
} else {
|
||||
return choices.stream()
|
||||
.map(Integer.class::cast)
|
||||
.min(Comparator.naturalOrder())
|
||||
.orElse(null);
|
||||
}
|
||||
} else {
|
||||
// planar
|
||||
// priority: chaos -> planar -> blank
|
||||
return choices.stream()
|
||||
.map(PlanarDieRollResult.class::cast)
|
||||
.max(Comparator.comparingInt(PlanarDieRollResult::getAIPriority))
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
Choice choice = new ChoiceImpl(true);
|
||||
choice.setMessage("Choose which die roll result to keep (the rest will be ignored)");
|
||||
choice.setChoices(choices.stream().sorted().map(Object::toString).collect(Collectors.toSet()));
|
||||
|
||||
this.choose(Outcome.Neutral, choice, game);
|
||||
Object defaultChoice = choices.iterator().next();
|
||||
return choices.stream()
|
||||
.filter(o -> o.toString().equals(choice.getChoice()))
|
||||
.findFirst()
|
||||
.orElse(defaultChoice);
|
||||
}
|
||||
|
||||
private Object rollDieInnerWithReplacement(Game game, Ability source, RollDieType rollDieType, int numSides, int numChaosSides, int numPlanarSides) {
|
||||
switch (rollDieType) {
|
||||
|
||||
case NUMERICAL: {
|
||||
int result = rollDieResult(numSides, game);
|
||||
// Clam-I-Am workaround:
|
||||
// If you roll a 3 on a six-sided die, you may reroll that die.
|
||||
if (numSides == 6
|
||||
&& result == 3
|
||||
&& game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.REPLACE_ROLLED_DIE, source.getControllerId(), source, source.getControllerId()))
|
||||
&& chooseUse(Outcome.Neutral, "Re-roll the 3?", source, game)) {
|
||||
result = rollDieResult(numSides, game);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
case PLANAR: {
|
||||
if (numChaosSides + numPlanarSides > numSides) {
|
||||
numChaosSides = GameOptions.PLANECHASE_PLANAR_DIE_CHAOS_SIDES;
|
||||
numPlanarSides = GameOptions.PLANECHASE_PLANAR_DIE_PLANAR_SIDES;
|
||||
}
|
||||
// for 9 sides:
|
||||
// 1..2 - chaos
|
||||
// 3..7 - blank
|
||||
// 8..9 - planar
|
||||
int result = this.rollDieResult(numSides, game);
|
||||
PlanarDieRollResult roll;
|
||||
if (result <= numChaosSides) {
|
||||
roll = PlanarDieRollResult.CHAOS_ROLL;
|
||||
} else if (result > numSides - numPlanarSides) {
|
||||
roll = PlanarDieRollResult.PLANAR_ROLL;
|
||||
} else {
|
||||
roll = PlanarDieRollResult.BLANK_ROLL;
|
||||
}
|
||||
return roll;
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new IllegalArgumentException("Unknown roll die type " + rollDieType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param outcome
|
||||
* @param source
|
||||
* @param game
|
||||
* @param sidesAmount number of sides the dice has
|
||||
* @param rollsAmount number of tries to roll the dice
|
||||
* @param ignoreLowestAmount remove the lowest rolls from the results
|
||||
* @return the number that the player rolled
|
||||
*/
|
||||
@Override
|
||||
public List<Integer> rollDice(Outcome outcome, Ability source, Game game, int sidesAmount, int rollsAmount, int ignoreLowestAmount) {
|
||||
return rollDiceInner(outcome, source, game, RollDieType.NUMERICAL, sidesAmount, 0, 0, rollsAmount, ignoreLowestAmount)
|
||||
.stream()
|
||||
.map(Integer.class::cast)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner code to roll a dice. Support normal and planar types.
|
||||
*
|
||||
* @param outcome
|
||||
* @param source
|
||||
* @param game
|
||||
* @param rollDieType die type to roll, e.g. planar or numerical
|
||||
* @param sidesAmount sides per die
|
||||
* @param chaosSidesAmount for planar die: chaos sides
|
||||
* @param planarSidesAmount for planar die: planar sides
|
||||
* @param rollsAmount rolls
|
||||
* @param ignoreLowestAmount for numerical die: ignore multiple rolls with the lowest values
|
||||
* @return
|
||||
*/
|
||||
private List<Object> rollDiceInner(Outcome outcome, Ability source, Game game, RollDieType rollDieType,
|
||||
int sidesAmount, int chaosSidesAmount, int planarSidesAmount,
|
||||
int rollsAmount, int ignoreLowestAmount) {
|
||||
RollDiceEvent rollDiceEvent = new RollDiceEvent(source, rollDieType, sidesAmount, rollsAmount);
|
||||
if (ignoreLowestAmount > 0) {
|
||||
rollDiceEvent.incIgnoreLowestAmount(ignoreLowestAmount);
|
||||
}
|
||||
game.replaceEvent(rollDiceEvent);
|
||||
|
||||
// 706.6.
|
||||
// In a Planechase game, rolling the planar die will cause any ability that triggers whenever a
|
||||
// player rolls one or more dice to trigger. However, any effect that refers to a numerical
|
||||
// result of a die roll, including ones that compare the results of that roll to other rolls
|
||||
// or to a given number, ignores the rolling of the planar die. See rule 901, “Planechase.”
|
||||
|
||||
// ROLL MULTIPLE dies
|
||||
// results amount can be less than a rolls amount (example: The Big Idea allows rolling 2x instead 1x)
|
||||
List<Object> dieResults = new ArrayList<>();
|
||||
List<RollDieResult> dieRolls = new ArrayList<>();
|
||||
for (int i = 0; i < rollDiceEvent.getAmount(); i++) {
|
||||
// ROLL SINGLE die
|
||||
RollDieEvent rollDieEvent = new RollDieEvent(source, rollDiceEvent.getRollDieType(), rollDiceEvent.getSides());
|
||||
game.replaceEvent(rollDieEvent);
|
||||
|
||||
Object rollResult;
|
||||
// big idea logic for numerical rolls only
|
||||
if (rollDieEvent.getRollDieType() == RollDieType.NUMERICAL && rollDieEvent.getBigIdeaRollsAmount() > 0) {
|
||||
// rolls 2x + sum results
|
||||
// The Big Idea: roll two six-sided dice and use the total of those results
|
||||
int totalSum = 0;
|
||||
for (int j = 0; j < rollDieEvent.getBigIdeaRollsAmount() + 1; j++) {
|
||||
int singleResult = (Integer) rollDieInner(
|
||||
outcome,
|
||||
game,
|
||||
source,
|
||||
rollDieEvent.getRollDieType(),
|
||||
rollDieEvent.getSides(),
|
||||
chaosSidesAmount,
|
||||
planarSidesAmount,
|
||||
rollDieEvent.getRollsAmount());
|
||||
totalSum += singleResult;
|
||||
dieRolls.add(new RollDieResult(singleResult, rollDieEvent.getResultModifier(), null));
|
||||
}
|
||||
rollResult = totalSum;
|
||||
} else {
|
||||
// rolls 1x
|
||||
switch (rollDieEvent.getRollDieType()) {
|
||||
default:
|
||||
case NUMERICAL: {
|
||||
int naturalResult = (Integer) rollDieInner(
|
||||
outcome,
|
||||
game,
|
||||
source,
|
||||
rollDieEvent.getRollDieType(),
|
||||
rollDieEvent.getSides(),
|
||||
chaosSidesAmount,
|
||||
planarSidesAmount,
|
||||
rollDieEvent.getRollsAmount()
|
||||
);
|
||||
dieRolls.add(new RollDieResult(naturalResult, rollDieEvent.getResultModifier(), null));
|
||||
rollResult = naturalResult;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLANAR: {
|
||||
PlanarDieRollResult planarResult = (PlanarDieRollResult) rollDieInner(
|
||||
outcome,
|
||||
game,
|
||||
source,
|
||||
rollDieEvent.getRollDieType(),
|
||||
rollDieEvent.getSides(),
|
||||
chaosSidesAmount,
|
||||
planarSidesAmount,
|
||||
rollDieEvent.getRollsAmount()
|
||||
);
|
||||
dieRolls.add(new RollDieResult(0, 0, planarResult));
|
||||
rollResult = planarResult;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
dieResults.add(rollResult);
|
||||
}
|
||||
|
||||
// ignore the lowest results
|
||||
// planar dies: due to 706.6. planar die results must be fully ignored
|
||||
//
|
||||
// 706.5.
|
||||
// If a player is instructed to roll two or more dice and ignore the lowest roll, the roll
|
||||
// that yielded the lowest result is considered to have never happened. No abilities trigger
|
||||
// because of the ignored roll, and no effects apply to that roll. If multiple results are tied
|
||||
// for the lowest, the player chooses one of those rolls to be ignored.
|
||||
if (rollDiceEvent.getRollDieType() == RollDieType.NUMERICAL && rollDiceEvent.getIgnoreLowestAmount() > 0) {
|
||||
// find ignored values
|
||||
List<Integer> ignoredResults = new ArrayList<>();
|
||||
for (int i = 0; i < rollDiceEvent.getIgnoreLowestAmount(); i++) {
|
||||
int min = dieResults.stream().map(Integer.class::cast).mapToInt(Integer::intValue).min().orElse(0);
|
||||
dieResults.remove(Integer.valueOf(min));
|
||||
ignoredResults.add(min);
|
||||
}
|
||||
// remove ignored rolls (they not exist anymore)
|
||||
List<RollDieResult> newRolls = new ArrayList<>();
|
||||
for (RollDieResult rollDieResult : dieRolls) {
|
||||
if (ignoredResults.contains(rollDieResult.getResult())) {
|
||||
ignoredResults.remove((Integer) rollDieResult.getResult());
|
||||
} else {
|
||||
newRolls.add(rollDieResult);
|
||||
}
|
||||
}
|
||||
dieRolls.clear();
|
||||
dieRolls.addAll(newRolls);
|
||||
}
|
||||
|
||||
// raise affected roll events
|
||||
for (RollDieResult result : dieRolls) {
|
||||
game.fireEvent(new DieRolledEvent(source, rollDiceEvent.getRollDieType(), rollDiceEvent.getSides(), result.naturalResult, result.modifier, result.planarResult));
|
||||
}
|
||||
game.fireEvent(new DiceRolledEvent(rollDiceEvent.getSides(), dieResults, source));
|
||||
|
||||
String message;
|
||||
switch (rollDiceEvent.getRollDieType()) {
|
||||
default:
|
||||
case NUMERICAL:
|
||||
// [Roll a die] user rolled 2x d6 and got [1, 4] (source: xxx)
|
||||
message = String.format("[Roll a die] %s rolled %s %s and got [%s]%s",
|
||||
getLogName(),
|
||||
(dieResults.size() > 1 ? dieResults.size() + "x" : "a"),
|
||||
"d" + rollDiceEvent.getSides(),
|
||||
dieResults.stream().map(Object::toString).collect(Collectors.joining(", ")),
|
||||
CardUtil.getSourceLogName(game, source));
|
||||
break;
|
||||
case PLANAR:
|
||||
// [Roll a planar die] user rolled CHAOS (source: xxx)
|
||||
message = String.format("[Roll a planar die] %s rolled [%s]%s",
|
||||
getLogName(),
|
||||
dieResults.stream().map(Object::toString).collect(Collectors.joining(", ")),
|
||||
CardUtil.getSourceLogName(game, source));
|
||||
break;
|
||||
}
|
||||
game.informPlayers(message);
|
||||
return dieResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param source
|
||||
* @param game
|
||||
* @param appliedEffects
|
||||
* @param numSides Number of sides the dice has
|
||||
* @return the number that the player rolled
|
||||
*/
|
||||
@Override
|
||||
public int rollDice(Ability source, Game game, List<UUID> appliedEffects, int numSides) {
|
||||
int result = RandomUtil.nextInt(numSides) + 1;
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers("[Roll a die] " + getLogName() + " rolled a "
|
||||
+ result + " on a " + numSides + " sided die" + CardUtil.getSourceLogName(game, source));
|
||||
}
|
||||
GameEvent event = new GameEvent(GameEvent.EventType.ROLL_DICE, playerId, source, playerId, result, true);
|
||||
event.setAppliedEffects(appliedEffects);
|
||||
event.setAmount(result);
|
||||
event.setData(numSides + "");
|
||||
if (!game.replaceEvent(event)) {
|
||||
GameEvent ge = new GameEvent(GameEvent.EventType.DICE_ROLLED, playerId, source, playerId, event.getAmount(), event.getFlag());
|
||||
ge.setData(numSides + "");
|
||||
game.fireEvent(ge);
|
||||
}
|
||||
return event.getAmount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlanarDieRoll rollPlanarDie(Ability source, Game game) {
|
||||
return this.rollPlanarDie(source, game, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlanarDieRoll rollPlanarDie(Ability source, Game game, List<UUID> appliedEffects) {
|
||||
return rollPlanarDie(source, game, appliedEffects, 2, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param game
|
||||
* @param appliedEffects
|
||||
* @param numberChaosSides The number of chaos sides the planar die
|
||||
* @param chaosSidesAmount The number of chaos sides the planar die
|
||||
* currently has (normally 1 but can be 5)
|
||||
* @param numberPlanarSides The number of chaos sides the planar die
|
||||
* @param planarSidesAmount The number of chaos sides the planar die
|
||||
* currently has (normally 1)
|
||||
* @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
|
||||
* or NilRoll
|
||||
* or BlankRoll
|
||||
*/
|
||||
@Override
|
||||
public PlanarDieRoll rollPlanarDie(Ability source, Game game, List<UUID> appliedEffects, int numberChaosSides, int numberPlanarSides) {
|
||||
int result = RandomUtil.nextInt(9) + 1;
|
||||
PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL;
|
||||
if (numberChaosSides + numberPlanarSides > 9) {
|
||||
numberChaosSides = 2;
|
||||
numberPlanarSides = 2;
|
||||
}
|
||||
if (result <= numberChaosSides) {
|
||||
roll = PlanarDieRoll.CHAOS_ROLL;
|
||||
} else if (result > 9 - numberPlanarSides) {
|
||||
roll = PlanarDieRoll.PLANAR_ROLL;
|
||||
}
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers("[Roll the planar die] " + getLogName()
|
||||
+ " rolled a " + roll + " on the planar die" + CardUtil.getSourceLogName(game, source));
|
||||
}
|
||||
GameEvent event = new GameEvent(GameEvent.EventType.ROLL_PLANAR_DIE,
|
||||
playerId, source, playerId, result, true);
|
||||
event.setAppliedEffects(appliedEffects);
|
||||
event.setData(roll + "");
|
||||
if (!game.replaceEvent(event)) {
|
||||
GameEvent ge = new GameEvent(GameEvent.EventType.PLANAR_DIE_ROLLED,
|
||||
playerId, source, playerId, event.getAmount(), event.getFlag());
|
||||
ge.setData(roll + "");
|
||||
game.fireEvent(ge);
|
||||
}
|
||||
return roll;
|
||||
public PlanarDieRollResult rollPlanarDie(Outcome outcome, Ability source, Game game, int chaosSidesAmount, int planarSidesAmount) {
|
||||
return rollDiceInner(outcome, source, game, RollDieType.PLANAR, GameOptions.PLANECHASE_PLANAR_DIE_TOTAL_SIDES, chaosSidesAmount, planarSidesAmount, 1, 0)
|
||||
.stream()
|
||||
.map(o -> (PlanarDieRollResult) o)
|
||||
.findFirst()
|
||||
.orElse(PlanarDieRollResult.BLANK_ROLL);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -3539,15 +3782,12 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
boolean canActivateAsHandZone = approvingObject != null
|
||||
|| (fromZone == Zone.GRAVEYARD && canPlayCardsFromGraveyard());
|
||||
boolean possibleToPlay = false;
|
||||
boolean possibleToPlay = canActivateAsHandZone
|
||||
&& ability.getZone().match(Zone.HAND)
|
||||
&& (isPlaySpell || isPlayLand);
|
||||
|
||||
// spell/hand abilities (play from all zones)
|
||||
// need permitingObject or canPlayCardsFromGraveyard
|
||||
if (canActivateAsHandZone
|
||||
&& ability.getZone().match(Zone.HAND)
|
||||
&& (isPlaySpell || isPlayLand)) {
|
||||
possibleToPlay = true;
|
||||
}
|
||||
|
||||
// zone's abilities (play from specific zone)
|
||||
// no need in permitingObject
|
||||
|
|
@ -4275,7 +4515,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("to Zone" + toZone.toString() + " not supported yet");
|
||||
throw new UnsupportedOperationException("to Zone" + toZone + " not supported yet");
|
||||
}
|
||||
return !successfulMovedCards.isEmpty();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue