mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
Turn under control reworked:
- game: added support for human games (cards like Emrakul, the Promised End, #12878); - game: added support of 720.1. to reset control in the turn beginning instead cleanup step (related to #12115); - game: added game logs for priorities in cleanup step; - game: fixed game freezes and wrong skip settings usages (related to #12878); - gui: added playable and choose-able marks for controlling player's cards and permanents, including switched hands; - gui: added controlling player name in all choice dialogs; - info: control of computer players is it not yet supported;
This commit is contained in:
parent
75d241d541
commit
c076f4925f
17 changed files with 177 additions and 140 deletions
|
|
@ -30,8 +30,10 @@ import mage.constants.*;
|
||||||
import mage.game.events.PlayerQueryEvent;
|
import mage.game.events.PlayerQueryEvent;
|
||||||
import mage.players.PlayableObjectStats;
|
import mage.players.PlayableObjectStats;
|
||||||
import mage.players.PlayableObjectsList;
|
import mage.players.PlayableObjectsList;
|
||||||
|
import mage.util.CardUtil;
|
||||||
import mage.util.DebugUtil;
|
import mage.util.DebugUtil;
|
||||||
import mage.util.MultiAmountMessage;
|
import mage.util.MultiAmountMessage;
|
||||||
|
import mage.util.StreamUtils;
|
||||||
import mage.view.*;
|
import mage.view.*;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
|
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
|
||||||
|
|
@ -53,6 +55,7 @@ import java.util.*;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static mage.client.dialog.PreferencesDialog.*;
|
import static mage.client.dialog.PreferencesDialog.*;
|
||||||
import static mage.constants.PlayerAction.*;
|
import static mage.constants.PlayerAction.*;
|
||||||
|
|
@ -1810,6 +1813,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
||||||
|
|
||||||
// hand
|
// hand
|
||||||
if (needZone == Zone.HAND || needZone == Zone.ALL) {
|
if (needZone == Zone.HAND || needZone == Zone.ALL) {
|
||||||
|
// my hand
|
||||||
for (CardView card : lastGameData.game.getMyHand().values()) {
|
for (CardView card : lastGameData.game.getMyHand().values()) {
|
||||||
if (needSelectable.contains(card.getId())) {
|
if (needSelectable.contains(card.getId())) {
|
||||||
card.setChoosable(true);
|
card.setChoosable(true);
|
||||||
|
|
@ -1821,6 +1825,34 @@ public final class GamePanel extends javax.swing.JPanel {
|
||||||
card.setPlayableStats(needPlayable.getStats(card.getId()));
|
card.setPlayableStats(needPlayable.getStats(card.getId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// opponent hands (switching by GUI's button with my hand)
|
||||||
|
List<SimpleCardView> list = lastGameData.game.getOpponentHands().values().stream().flatMap(s -> s.values().stream()).collect(Collectors.toList());
|
||||||
|
for (SimpleCardView card : list) {
|
||||||
|
if (needSelectable.contains(card.getId())) {
|
||||||
|
card.setChoosable(true);
|
||||||
|
}
|
||||||
|
if (needChosen.contains(card.getId())) {
|
||||||
|
card.setSelected(true);
|
||||||
|
}
|
||||||
|
if (needPlayable.containsObject(card.getId())) {
|
||||||
|
card.setPlayableStats(needPlayable.getStats(card.getId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// watched hands (switching by GUI's button with my hand)
|
||||||
|
list = lastGameData.game.getWatchedHands().values().stream().flatMap(s -> s.values().stream()).collect(Collectors.toList());
|
||||||
|
for (SimpleCardView card : list) {
|
||||||
|
if (needSelectable.contains(card.getId())) {
|
||||||
|
card.setChoosable(true);
|
||||||
|
}
|
||||||
|
if (needChosen.contains(card.getId())) {
|
||||||
|
card.setSelected(true);
|
||||||
|
}
|
||||||
|
if (needPlayable.containsObject(card.getId())) {
|
||||||
|
card.setPlayableStats(needPlayable.getStats(card.getId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stack
|
// stack
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import mage.game.tournament.Tournament;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.players.PlayerImpl;
|
import mage.players.PlayerImpl;
|
||||||
import mage.players.PlayerList;
|
import mage.players.PlayerList;
|
||||||
|
import mage.players.net.UserData;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
import mage.target.TargetAmount;
|
import mage.target.TargetAmount;
|
||||||
import mage.target.TargetCard;
|
import mage.target.TargetCard;
|
||||||
|
|
@ -93,7 +94,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
// * - GAME thread: open response for income command and wait (go to sleep by response.wait)
|
// * - GAME thread: open response for income command and wait (go to sleep by response.wait)
|
||||||
// * - CALL thread: on closed response - waiting open status of player's response object (if it's too long then cancel the answer)
|
// * - CALL thread: on closed response - waiting open status of player's response object (if it's too long then cancel the answer)
|
||||||
// * - CALL thread: on opened response - save answer to player's response object and notify GAME thread about it by response.notifyAll
|
// * - CALL thread: on opened response - save answer to player's response object and notify GAME thread about it by response.notifyAll
|
||||||
// * - GAME thread: on nofify from response - check new answer value and process it (if it bad then repeat and wait the next one);
|
// * - GAME thread: on notify from response - check new answer value and process it (if it bad then repeat and wait the next one);
|
||||||
private transient Boolean responseOpenedForAnswer = false; // GAME thread waiting new answer
|
private transient Boolean responseOpenedForAnswer = false; // GAME thread waiting new answer
|
||||||
private transient long responseLastWaitingThreadId = 0;
|
private transient long responseLastWaitingThreadId = 0;
|
||||||
private final transient PlayerResponse response = new PlayerResponse();
|
private final transient PlayerResponse response = new PlayerResponse();
|
||||||
|
|
@ -1159,25 +1160,27 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
// TODO: change pass and other states like passedUntilStackResolved for controlling player, not for "this"
|
// TODO: change pass and other states like passedUntilStackResolved for controlling player, not for "this"
|
||||||
// TODO: check and change all "this" to controling player calls, many bugs with hand, mana, skips - https://github.com/magefree/mage/issues/2088
|
// TODO: check and change all "this" to controling player calls, many bugs with hand, mana, skips - https://github.com/magefree/mage/issues/2088
|
||||||
// TODO: use controlling player in all choose dialogs (and canRespond too, what's with take control of player AI?!)
|
// TODO: use controlling player in all choose dialogs (and canRespond too, what's with take control of player AI?!)
|
||||||
|
UserData controllingUserData = this.userData;
|
||||||
if (canRespond()) {
|
if (canRespond()) {
|
||||||
HumanPlayer controllingPlayer = this;
|
if (!isGameUnderControl()) {
|
||||||
if (isGameUnderControl()) { // TODO: must be ! to get real controlling player
|
|
||||||
Player player = game.getPlayer(getTurnControlledBy());
|
Player player = game.getPlayer(getTurnControlledBy());
|
||||||
if (player instanceof HumanPlayer) {
|
if (player instanceof HumanPlayer) {
|
||||||
controllingPlayer = (HumanPlayer) player;
|
controllingUserData = player.getUserData();
|
||||||
|
} else {
|
||||||
|
// TODO: add computer opponent here?!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check that all skips and stops used from real controlling player
|
// TODO: check that all skips and stops used from real controlling player
|
||||||
// like holdingPriority (is it a bug here?)
|
// like holdingPriority (is it a bug here?)
|
||||||
if (getJustActivatedType() != null && !holdingPriority) {
|
if (getJustActivatedType() != null && !holdingPriority) {
|
||||||
if (controllingPlayer.getUserData().isPassPriorityCast()
|
if (controllingUserData.isPassPriorityCast()
|
||||||
&& getJustActivatedType() == AbilityType.SPELL) {
|
&& getJustActivatedType() == AbilityType.SPELL) {
|
||||||
setJustActivatedType(null);
|
setJustActivatedType(null);
|
||||||
pass(game);
|
pass(game);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (controllingPlayer.getUserData().isPassPriorityActivation()
|
if (controllingUserData.isPassPriorityActivation()
|
||||||
&& getJustActivatedType().isNonManaActivatedAbility()) {
|
&& getJustActivatedType().isNonManaActivatedAbility()) {
|
||||||
setJustActivatedType(null);
|
setJustActivatedType(null);
|
||||||
pass(game);
|
pass(game);
|
||||||
|
|
@ -1252,7 +1255,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
// it's main step
|
// it's main step
|
||||||
if (!skippedAtLeastOnce
|
if (!skippedAtLeastOnce
|
||||||
|| (!playerId.equals(game.getActivePlayerId())
|
|| (!playerId.equals(game.getActivePlayerId())
|
||||||
&& !controllingPlayer.getUserData().getUserSkipPrioritySteps().isStopOnAllMainPhases())) {
|
&& !controllingUserData.getUserSkipPrioritySteps().isStopOnAllMainPhases())) {
|
||||||
skippedAtLeastOnce = true;
|
skippedAtLeastOnce = true;
|
||||||
if (passWithManaPoolCheck(game)) {
|
if (passWithManaPoolCheck(game)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1274,8 +1277,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
// it's end of turn step
|
// it's end of turn step
|
||||||
if (!skippedAtLeastOnce
|
if (!skippedAtLeastOnce
|
||||||
|| (playerId.equals(game.getActivePlayerId())
|
|| (playerId.equals(game.getActivePlayerId())
|
||||||
&& !controllingPlayer
|
&& !controllingUserData
|
||||||
.getUserData()
|
|
||||||
.getUserSkipPrioritySteps()
|
.getUserSkipPrioritySteps()
|
||||||
.isStopOnAllEndPhases())) {
|
.isStopOnAllEndPhases())) {
|
||||||
skippedAtLeastOnce = true;
|
skippedAtLeastOnce = true;
|
||||||
|
|
@ -1295,7 +1297,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dontCheckPassStep
|
if (!dontCheckPassStep
|
||||||
&& checkPassStep(game, controllingPlayer)) {
|
&& checkPassStep(game, controllingUserData)) {
|
||||||
if (passWithManaPoolCheck(game)) {
|
if (passWithManaPoolCheck(game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -1308,8 +1310,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
if (passedUntilStackResolved) {
|
if (passedUntilStackResolved) {
|
||||||
if (haveNewObjectsOnStack
|
if (haveNewObjectsOnStack
|
||||||
&& (playerId.equals(game.getActivePlayerId())
|
&& (playerId.equals(game.getActivePlayerId())
|
||||||
&& controllingPlayer
|
&& controllingUserData
|
||||||
.getUserData()
|
|
||||||
.getUserSkipPrioritySteps()
|
.getUserSkipPrioritySteps()
|
||||||
.isStopOnStackNewObjects())) {
|
.isStopOnStackNewObjects())) {
|
||||||
// new objects on stack -- disable "pass until stack resolved"
|
// new objects on stack -- disable "pass until stack resolved"
|
||||||
|
|
@ -1433,17 +1434,17 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
return response.getUUID();
|
return response.getUUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkPassStep(Game game, HumanPlayer controllingPlayer) {
|
private boolean checkPassStep(Game game, UserData controllingUserData) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (playerId.equals(game.getActivePlayerId())) {
|
if (playerId.equals(game.getActivePlayerId())) {
|
||||||
return !controllingPlayer.getUserData().getUserSkipPrioritySteps().getYourTurn().isPhaseStepSet(game.getTurnStepType());
|
return !controllingUserData.getUserSkipPrioritySteps().getYourTurn().isPhaseStepSet(game.getTurnStepType());
|
||||||
} else {
|
} else {
|
||||||
return !controllingPlayer.getUserData().getUserSkipPrioritySteps().getOpponentTurn().isPhaseStepSet(game.getTurnStepType());
|
return !controllingUserData.getUserSkipPrioritySteps().getOpponentTurn().isPhaseStepSet(game.getTurnStepType());
|
||||||
}
|
}
|
||||||
} catch (NullPointerException ex) {
|
} catch (NullPointerException ex) {
|
||||||
if (controllingPlayer.getUserData() != null) {
|
if (controllingUserData != null) {
|
||||||
if (controllingPlayer.getUserData().getUserSkipPrioritySteps() != null) {
|
if (controllingUserData.getUserSkipPrioritySteps() != null) {
|
||||||
if (game.getStep() != null) {
|
if (game.getStep() != null) {
|
||||||
if (game.getTurnStepType() == null) {
|
if (game.getTurnStepType() == null) {
|
||||||
logger.error("game.getTurnStepType() == null");
|
logger.error("game.getTurnStepType() == null");
|
||||||
|
|
@ -2929,19 +2930,8 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
protected boolean passWithManaPoolCheck(Game game) {
|
protected boolean passWithManaPoolCheck(Game game) {
|
||||||
if (userData.confirmEmptyManaPool()
|
if (userData.confirmEmptyManaPool()
|
||||||
&& game.getStack().isEmpty() && getManaPool().count() > 0 && getManaPool().canLostManaOnEmpty()) {
|
&& game.getStack().isEmpty() && getManaPool().count() > 0 && getManaPool().canLostManaOnEmpty()) {
|
||||||
String activePlayerText;
|
String message = GameLog.getPlayerConfirmColoredText("You still have mana in your mana pool and it will be lose. Pass anyway?");
|
||||||
if (game.isActivePlayer(playerId)) {
|
if (!chooseUse(Outcome.Detriment, message, null, game)) {
|
||||||
activePlayerText = "Your turn";
|
|
||||||
} else {
|
|
||||||
activePlayerText = game.getPlayer(game.getActivePlayerId()).getName() + "'s turn";
|
|
||||||
}
|
|
||||||
String priorityPlayerText = "";
|
|
||||||
if (!isGameUnderControl()) {
|
|
||||||
priorityPlayerText = " / priority " + game.getPlayer(game.getPriorityPlayerId()).getName();
|
|
||||||
}
|
|
||||||
// TODO: chooseUse and other dialogs must be under controlling player
|
|
||||||
if (!chooseUse(Outcome.Detriment, GameLog.getPlayerConfirmColoredText("You still have mana in your mana pool and it will be lose. Pass anyway?")
|
|
||||||
+ GameLog.getSmallSecondLineText(activePlayerText + " / " + game.getTurnStepType().toString() + priorityPlayerText), null, game)) {
|
|
||||||
sendPlayerAction(PlayerAction.PASS_PRIORITY_CANCEL_ALL_ACTIONS, game, null);
|
sendPlayerAction(PlayerAction.PASS_PRIORITY_CANCEL_ALL_ACTIONS, game, null);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -906,14 +906,14 @@ public class GameController implements GameCallback {
|
||||||
perform(playerId, playerId1 -> getGameSession(playerId1).getMultiAmount(messages, min, max, options));
|
perform(playerId, playerId1 -> getGameSession(playerId1).getMultiAmount(messages, min, max, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void informOthers(UUID playerId) {
|
private void informOthers(UUID waitingPlayerId) {
|
||||||
StringBuilder message = new StringBuilder();
|
StringBuilder message = new StringBuilder();
|
||||||
if (game.getStep() != null) {
|
if (game.getStep() != null) {
|
||||||
message.append(game.getTurnStepType().toString()).append(" - ");
|
message.append(game.getTurnStepType().toString()).append(" - ");
|
||||||
}
|
}
|
||||||
message.append("Waiting for ").append(game.getPlayer(playerId).getLogName());
|
message.append("Waiting for ").append(game.getPlayer(waitingPlayerId).getLogName());
|
||||||
for (final Entry<UUID, GameSessionPlayer> entry : getGameSessionsMap().entrySet()) {
|
for (final Entry<UUID, GameSessionPlayer> entry : getGameSessionsMap().entrySet()) {
|
||||||
if (!entry.getKey().equals(playerId)) {
|
if (!entry.getKey().equals(waitingPlayerId)) {
|
||||||
entry.getValue().inform(message.toString());
|
entry.getValue().inform(message.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1030,7 +1030,7 @@ public class GameController implements GameCallback {
|
||||||
// TODO: if watcher disconnects then game freezes with active timer, must be fix for such use case
|
// TODO: if watcher disconnects then game freezes with active timer, must be fix for such use case
|
||||||
// same for another player (can be fixed by super-duper connection)
|
// same for another player (can be fixed by super-duper connection)
|
||||||
if (informOthers) {
|
if (informOthers) {
|
||||||
informOthers(playerId);
|
informOthers(realPlayerController.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -212,13 +212,14 @@ public class GameSessionPlayer extends GameSessionWatcher {
|
||||||
// game view calculation can take some time and can be called from non-game thread,
|
// game view calculation can take some time and can be called from non-game thread,
|
||||||
// so use copy for thread save (protection from ConcurrentModificationException)
|
// so use copy for thread save (protection from ConcurrentModificationException)
|
||||||
Game sourceGame = game.copy();
|
Game sourceGame = game.copy();
|
||||||
|
|
||||||
Player player = sourceGame.getPlayer(playerId); // null for watcher
|
|
||||||
GameView gameView = new GameView(sourceGame.getState(), sourceGame, playerId, null);
|
GameView gameView = new GameView(sourceGame.getState(), sourceGame, playerId, null);
|
||||||
if (player != null) {
|
|
||||||
if (gameView.getPriorityPlayerName().equals(player.getName())) {
|
// playable info (if opponent under control then show opponent's playable)
|
||||||
gameView.setCanPlayObjects(player.getPlayableObjects(sourceGame, Zone.ALL));
|
Player player = sourceGame.getPlayer(playerId); // null for watcher
|
||||||
}
|
Player priorityPlayer = sourceGame.getPlayer(sourceGame.getPriorityPlayerId());
|
||||||
|
Player controllingPlayer = priorityPlayer == null ? null : sourceGame.getPlayer(priorityPlayer.getTurnControlledBy());
|
||||||
|
if (controllingPlayer != null && player == controllingPlayer) {
|
||||||
|
gameView.setCanPlayObjects(priorityPlayer.getPlayableObjects(sourceGame, Zone.ALL));
|
||||||
}
|
}
|
||||||
|
|
||||||
processControlledPlayers(sourceGame, player, gameView);
|
processControlledPlayers(sourceGame, player, gameView);
|
||||||
|
|
|
||||||
|
|
@ -104,18 +104,17 @@ public class LoadCallbackClient implements CallbackClient {
|
||||||
GameClientMessage message = (GameClientMessage) callback.getData();
|
GameClientMessage message = (GameClientMessage) callback.getData();
|
||||||
this.gameView = message.getGameView();
|
this.gameView = message.getGameView();
|
||||||
log.info(getLogStartInfo() + " target: " + message.getMessage());
|
log.info(getLogStartInfo() + " target: " + message.getMessage());
|
||||||
switch (message.getMessage()) {
|
if (message.getMessage().startsWith("Select a starting player")) {
|
||||||
case "Select a starting player":
|
session.sendPlayerUUID(gameId, playerId);
|
||||||
session.sendPlayerUUID(gameId, playerId);
|
return;
|
||||||
return;
|
} else if (message.getMessage().startsWith("Select a card to discard")) {
|
||||||
case "Select a card to discard":
|
log.info(getLogStartInfo() + "hand size: " + gameView.getMyHand().size());
|
||||||
log.info(getLogStartInfo() + "hand size: " + gameView.getMyHand().size());
|
SimpleCardView card = gameView.getMyHand().values().iterator().next();
|
||||||
SimpleCardView card = gameView.getMyHand().values().iterator().next();
|
session.sendPlayerUUID(gameId, card.getId());
|
||||||
session.sendPlayerUUID(gameId, card.getId());
|
return;
|
||||||
return;
|
} else {
|
||||||
default:
|
log.error(getLogStartInfo() + "unknown GAME_TARGET message: " + message.toString());
|
||||||
log.error(getLogStartInfo() + "unknown GAME_TARGET message: " + message.toString());
|
return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
|
|
||||||
package mage.abilities.common.delayed;
|
|
||||||
|
|
||||||
import mage.abilities.DelayedTriggeredAbility;
|
|
||||||
import mage.abilities.effects.Effect;
|
|
||||||
import mage.game.Game;
|
|
||||||
import mage.game.events.GameEvent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nantuko
|
|
||||||
*/
|
|
||||||
public class AtTheEndOfTurnStepPostDelayedTriggeredAbility extends DelayedTriggeredAbility {
|
|
||||||
|
|
||||||
public AtTheEndOfTurnStepPostDelayedTriggeredAbility(Effect effect) {
|
|
||||||
this(effect, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AtTheEndOfTurnStepPostDelayedTriggeredAbility(Effect effect, boolean usesStack) {
|
|
||||||
super(effect);
|
|
||||||
this.usesStack = usesStack;
|
|
||||||
setTriggerPhrase("At end of turn ");
|
|
||||||
}
|
|
||||||
|
|
||||||
public AtTheEndOfTurnStepPostDelayedTriggeredAbility(AtTheEndOfTurnStepPostDelayedTriggeredAbility ability) {
|
|
||||||
super(ability);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AtTheEndOfTurnStepPostDelayedTriggeredAbility copy() {
|
|
||||||
return new AtTheEndOfTurnStepPostDelayedTriggeredAbility(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean checkEventType(GameEvent event, Game game) {
|
|
||||||
return event.getType() == GameEvent.EventType.END_TURN_STEP_POST;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean checkTrigger(GameEvent event, Game game) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -550,6 +550,12 @@ public interface Game extends MageItem, Serializable, Copyable<Game> {
|
||||||
@Deprecated // TODO: must research usage and remove it from all non engine code (example: Bestow ability, ProcessActions must be used instead)
|
@Deprecated // TODO: must research usage and remove it from all non engine code (example: Bestow ability, ProcessActions must be used instead)
|
||||||
boolean checkStateAndTriggered();
|
boolean checkStateAndTriggered();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play priority by all players
|
||||||
|
*
|
||||||
|
* @param activePlayerId starting priority player
|
||||||
|
* @param resuming false to reset passed priority and ask it again
|
||||||
|
*/
|
||||||
void playPriority(UUID activePlayerId, boolean resuming);
|
void playPriority(UUID activePlayerId, boolean resuming);
|
||||||
|
|
||||||
void resetControlAfterSpellResolve(UUID topId);
|
void resetControlAfterSpellResolve(UUID topId);
|
||||||
|
|
|
||||||
|
|
@ -2987,21 +2987,35 @@ public abstract class GameImpl implements Game {
|
||||||
}
|
}
|
||||||
String message;
|
String message;
|
||||||
if (this.canPlaySorcery(playerId)) {
|
if (this.canPlaySorcery(playerId)) {
|
||||||
message = "Play spells and abilities.";
|
message = "Play spells and abilities";
|
||||||
} else {
|
} else {
|
||||||
message = "Play instants and activated abilities.";
|
message = "Play instants and activated abilities";
|
||||||
}
|
}
|
||||||
playerQueryEventSource.select(playerId, message);
|
|
||||||
|
message += getControllingPlayerHint(playerId);
|
||||||
|
|
||||||
|
Player player = this.getPlayer(playerId);
|
||||||
|
playerQueryEventSource.select(player.getTurnControlledBy(), message);
|
||||||
getState().clearLookedAt();
|
getState().clearLookedAt();
|
||||||
getState().clearRevealed();
|
getState().clearRevealed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getControllingPlayerHint(UUID playerId) {
|
||||||
|
Player player = this.getPlayer(playerId);
|
||||||
|
Player controllingPlayer = this.getPlayer(player.getTurnControlledBy());
|
||||||
|
if (player != controllingPlayer) {
|
||||||
|
return " (as " + player.getLogName() + ")";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void fireSelectEvent(UUID playerId, String message) {
|
public synchronized void fireSelectEvent(UUID playerId, String message) {
|
||||||
if (simulation) {
|
if (simulation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerQueryEventSource.select(playerId, message);
|
playerQueryEventSource.select(playerId, message + getControllingPlayerHint(playerId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -3009,7 +3023,7 @@ public abstract class GameImpl implements Game {
|
||||||
if (simulation) {
|
if (simulation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerQueryEventSource.select(playerId, message, options);
|
playerQueryEventSource.select(playerId, message + getControllingPlayerHint(playerId), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -3017,7 +3031,7 @@ public abstract class GameImpl implements Game {
|
||||||
if (simulation) {
|
if (simulation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerQueryEventSource.playMana(playerId, message, options);
|
playerQueryEventSource.playMana(playerId, message + getControllingPlayerHint(playerId), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -3025,7 +3039,7 @@ public abstract class GameImpl implements Game {
|
||||||
if (simulation) {
|
if (simulation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerQueryEventSource.playXMana(playerId, message);
|
playerQueryEventSource.playXMana(playerId, message + getControllingPlayerHint(playerId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -3038,7 +3052,7 @@ public abstract class GameImpl implements Game {
|
||||||
if (simulation) {
|
if (simulation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerQueryEventSource.ask(playerId, message.getMessage(), source, addMessageToOptions(message, options));
|
playerQueryEventSource.ask(playerId, message.getMessage() + getControllingPlayerHint(playerId), source, addMessageToOptions(message, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -3050,7 +3064,7 @@ public abstract class GameImpl implements Game {
|
||||||
if (object != null) {
|
if (object != null) {
|
||||||
objectName = object.getName();
|
objectName = object.getName();
|
||||||
}
|
}
|
||||||
playerQueryEventSource.chooseAbility(playerId, message, objectName, choices);
|
playerQueryEventSource.chooseAbility(playerId, message + getControllingPlayerHint(playerId), objectName, choices);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -3058,7 +3072,7 @@ public abstract class GameImpl implements Game {
|
||||||
if (simulation) {
|
if (simulation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerQueryEventSource.chooseMode(playerId, message, modes);
|
playerQueryEventSource.chooseMode(playerId, message + getControllingPlayerHint(playerId), modes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -3066,7 +3080,7 @@ public abstract class GameImpl implements Game {
|
||||||
if (simulation) {
|
if (simulation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerQueryEventSource.target(playerId, message.getMessage(), targets, required, addMessageToOptions(message, options));
|
playerQueryEventSource.target(playerId, message.getMessage() + getControllingPlayerHint(playerId), targets, required, addMessageToOptions(message, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -3074,7 +3088,7 @@ public abstract class GameImpl implements Game {
|
||||||
if (simulation) {
|
if (simulation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerQueryEventSource.target(playerId, message.getMessage(), cards, required, addMessageToOptions(message, options));
|
playerQueryEventSource.target(playerId, message.getMessage() + getControllingPlayerHint(playerId), cards, required, addMessageToOptions(message, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -3087,7 +3101,7 @@ public abstract class GameImpl implements Game {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void fireSelectTargetTriggeredAbilityEvent(UUID playerId, String message, List<TriggeredAbility> abilities) {
|
public void fireSelectTargetTriggeredAbilityEvent(UUID playerId, String message, List<TriggeredAbility> abilities) {
|
||||||
playerQueryEventSource.target(playerId, message, abilities);
|
playerQueryEventSource.target(playerId, message + getControllingPlayerHint(playerId), abilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -3095,7 +3109,7 @@ public abstract class GameImpl implements Game {
|
||||||
if (simulation) {
|
if (simulation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerQueryEventSource.target(playerId, message, perms, required);
|
playerQueryEventSource.target(playerId, message + getControllingPlayerHint(playerId), perms, required);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -3103,7 +3117,7 @@ public abstract class GameImpl implements Game {
|
||||||
if (simulation) {
|
if (simulation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerQueryEventSource.amount(playerId, message, min, max);
|
playerQueryEventSource.amount(playerId, message + getControllingPlayerHint(playerId), min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -3128,7 +3142,7 @@ public abstract class GameImpl implements Game {
|
||||||
if (simulation) {
|
if (simulation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playerQueryEventSource.choosePile(playerId, message, pile1, pile2);
|
playerQueryEventSource.choosePile(playerId, message + getControllingPlayerHint(playerId), pile1, pile2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
package mage.game.turn;
|
package mage.game.turn;
|
||||||
|
|
||||||
import mage.constants.TurnPhase;
|
import mage.constants.TurnPhase;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
|
|
||||||
|
|
||||||
package mage.game.turn;
|
package mage.game.turn;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent.EventType;
|
import mage.game.events.GameEvent.EventType;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
|
|
@ -30,19 +28,31 @@ public class CleanupStep extends Step {
|
||||||
super.beginStep(game, activePlayerId);
|
super.beginStep(game, activePlayerId);
|
||||||
Player activePlayer = game.getPlayer(activePlayerId);
|
Player activePlayer = game.getPlayer(activePlayerId);
|
||||||
game.getState().setPriorityPlayerId(activePlayer.getId());
|
game.getState().setPriorityPlayerId(activePlayer.getId());
|
||||||
//20091005 - 514.1
|
|
||||||
|
// 514.1
|
||||||
|
// First, if the active player’s hand contains more cards than his or her maximum hand size
|
||||||
|
// (normally seven), he or she discards enough cards to reduce his or her hand size to that number.
|
||||||
|
// This turn-based action doesn’t use the stack.
|
||||||
if (activePlayer.isInGame()) {
|
if (activePlayer.isInGame()) {
|
||||||
activePlayer.discardToMax(game);
|
activePlayer.discardToMax(game);
|
||||||
}
|
}
|
||||||
//20100423 - 514.2
|
|
||||||
|
// 514.2
|
||||||
|
// Second, the following actions happen simultaneously: all damage marked on permanents
|
||||||
|
// (including phased-out permanents) is removed and all "until end of turn" and "this turn"
|
||||||
|
// effects end. This turn-based action doesn’t use the stack.
|
||||||
game.getBattlefield().endOfTurn(game);
|
game.getBattlefield().endOfTurn(game);
|
||||||
game.getState().removeEotEffects(game);
|
game.getState().removeEotEffects(game);
|
||||||
|
|
||||||
|
// 514.3
|
||||||
|
// Normally, no player receives priority during the cleanup step, so no spells can be cast
|
||||||
|
// and no abilities can be activated. However, this rule is subject to the following exception: 514.3a
|
||||||
|
//
|
||||||
|
// Look at EndPhase code to process 514.3
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endStep(Game game, UUID activePlayerId) {
|
public void endStep(Game game, UUID activePlayerId) {
|
||||||
Player activePlayer = game.getPlayer(activePlayerId);
|
|
||||||
activePlayer.setGameUnderYourControl(true);
|
|
||||||
super.endStep(game, activePlayerId);
|
super.endStep(game, activePlayerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,21 @@ public class EndPhase extends Phase {
|
||||||
game.getState().increaseStepNum();
|
game.getState().increaseStepNum();
|
||||||
game.getTurn().setEndTurnRequested(false); // so triggers trigger again
|
game.getTurn().setEndTurnRequested(false); // so triggers trigger again
|
||||||
prePriority(game, activePlayerId);
|
prePriority(game, activePlayerId);
|
||||||
// 514.3a At this point, the game checks to see if any state-based actions would be performed
|
|
||||||
|
// 514.3.
|
||||||
|
// Normally, no player receives priority during the cleanup step, so no spells can be cast and
|
||||||
|
// no abilities can be activated. However, this rule is subject to the following exception:
|
||||||
|
// 514.3a
|
||||||
|
// At this point, the game checks to see if any state-based actions would be performed
|
||||||
// and/or any triggered abilities are waiting to be put onto the stack (including those that
|
// and/or any triggered abilities are waiting to be put onto the stack (including those that
|
||||||
// trigger "at the beginning of the next cleanup step"). If so, those state-based actions are
|
// trigger "at the beginning of the next cleanup step"). If so, those state-based actions are
|
||||||
// performed, then those triggered abilities are put on the stack, then the active player gets
|
// performed, then those triggered abilities are put on the stack, then the active player gets
|
||||||
// priority. Players may cast spells and activate abilities. Once the stack is empty and all players
|
// priority. Players may cast spells and activate abilities. Once the stack is empty and all players
|
||||||
// pass in succession, another cleanup step begins
|
// pass in succession, another cleanup step begins
|
||||||
if (game.checkStateAndTriggered()) {
|
if (game.checkStateAndTriggered()) {
|
||||||
// Queues a new cleanup step
|
game.informPlayers("State-based actions or triggers happened on cleanup step, so players get priority due 514.3a");
|
||||||
|
// queues a new cleanup step and request new priorities
|
||||||
game.getState().getTurnMods().add(new TurnMod(activePlayerId).withExtraStep(new CleanupStep()));
|
game.getState().getTurnMods().add(new TurnMod(activePlayerId).withExtraStep(new CleanupStep()));
|
||||||
// resume priority
|
|
||||||
if (!game.isPaused() && !game.checkIfGameIsOver() && !game.executingRollback()) {
|
if (!game.isPaused() && !game.checkIfGameIsOver() && !game.executingRollback()) {
|
||||||
currentStep.priority(game, activePlayerId, false);
|
currentStep.priority(game, activePlayerId, false);
|
||||||
if (game.executingRollback()) {
|
if (game.executingRollback()) {
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,12 @@ public abstract class Step implements Serializable, Copyable<Step> {
|
||||||
stepPart = StepPart.PRE;
|
stepPart = StepPart.PRE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play priority by all players
|
||||||
|
*
|
||||||
|
* @param activePlayerId starting priority player
|
||||||
|
* @param resuming false to reset passed priority and ask it again
|
||||||
|
*/
|
||||||
public void priority(Game game, UUID activePlayerId, boolean resuming) {
|
public void priority(Game game, UUID activePlayerId, boolean resuming) {
|
||||||
if (hasPriority) {
|
if (hasPriority) {
|
||||||
stepPart = StepPart.PRIORITY;
|
stepPart = StepPart.PRIORITY;
|
||||||
|
|
|
||||||
|
|
@ -107,12 +107,16 @@ public class Turn implements Serializable {
|
||||||
));
|
));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
logStartOfTurn(game, activePlayer);
|
|
||||||
|
|
||||||
checkTurnIsControlledByOtherPlayer(game, activePlayer.getId());
|
logStartOfTurn(game, activePlayer);
|
||||||
|
resetCounts();
|
||||||
|
|
||||||
this.activePlayerId = activePlayer.getId();
|
this.activePlayerId = activePlayer.getId();
|
||||||
resetCounts();
|
this.currentPhase = null;
|
||||||
|
|
||||||
|
// turn control must be called after potential turn skip due 720.1.
|
||||||
|
checkTurnIsControlledByOtherPlayer(game, activePlayer.getId());
|
||||||
|
|
||||||
game.getPlayer(activePlayer.getId()).beginTurn(game);
|
game.getPlayer(activePlayer.getId()).beginTurn(game);
|
||||||
for (Phase phase : phases) {
|
for (Phase phase : phases) {
|
||||||
if (game.isPaused() || game.checkIfGameIsOver()) {
|
if (game.isPaused() || game.checkIfGameIsOver()) {
|
||||||
|
|
@ -220,8 +224,31 @@ public class Turn implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkTurnIsControlledByOtherPlayer(Game game, UUID activePlayerId) {
|
private void checkTurnIsControlledByOtherPlayer(Game game, UUID activePlayerId) {
|
||||||
|
// 720.1.
|
||||||
|
// Some cards allow a player to control another player during that player’s next turn.
|
||||||
|
// This effect applies to the next turn that the affected player actually takes.
|
||||||
|
// The affected player is controlled during the entire turn; the effect doesn’t end until
|
||||||
|
// the beginning of the next turn.
|
||||||
|
//
|
||||||
|
// 720.1b
|
||||||
|
// If a turn is skipped, any pending player-controlling effects wait until the player who would be
|
||||||
|
// affected actually takes a turn.
|
||||||
|
|
||||||
|
// remove old under control
|
||||||
|
game.getPlayers().values().forEach(player -> {
|
||||||
|
if (player.isInGame() && !player.isGameUnderControl()) {
|
||||||
|
Player controllingPlayer = game.getPlayer(player.getTurnControlledBy());
|
||||||
|
if (player != controllingPlayer && controllingPlayer != null) {
|
||||||
|
game.informPlayers(controllingPlayer.getLogName() + " lost control over " + player.getLogName());
|
||||||
|
}
|
||||||
|
player.setGameUnderYourControl(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// add new under control
|
||||||
TurnMod newControllerMod = game.getState().getTurnMods().useNextNewController(activePlayerId);
|
TurnMod newControllerMod = game.getState().getTurnMods().useNextNewController(activePlayerId);
|
||||||
if (newControllerMod != null && !newControllerMod.getNewControllerId().equals(activePlayerId)) {
|
if (newControllerMod != null && !newControllerMod.getNewControllerId().equals(activePlayerId)) {
|
||||||
|
// set player under new control
|
||||||
// game logs added in child's call (controlPlayersTurn)
|
// game logs added in child's call (controlPlayersTurn)
|
||||||
game.getPlayer(newControllerMod.getNewControllerId()).controlPlayersTurn(game, activePlayerId, newControllerMod.getInfo());
|
game.getPlayer(newControllerMod.getNewControllerId()).controlPlayersTurn(game, activePlayerId, newControllerMod.getInfo());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -366,7 +366,7 @@ public interface Player extends MageItem, Copyable<Player> {
|
||||||
boolean isGameUnderControl();
|
boolean isGameUnderControl();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns false in case you don't control the game.
|
* False in case you don't control the game.
|
||||||
* <p>
|
* <p>
|
||||||
* Note: For effects like "You control target player during that player's
|
* Note: For effects like "You control target player during that player's
|
||||||
* next turn".
|
* next turn".
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import mage.abilities.ActivatedAbility.ActivationStatus;
|
||||||
import mage.abilities.common.PassAbility;
|
import mage.abilities.common.PassAbility;
|
||||||
import mage.abilities.common.PlayLandAsCommanderAbility;
|
import mage.abilities.common.PlayLandAsCommanderAbility;
|
||||||
import mage.abilities.common.WhileSearchingPlayFromLibraryAbility;
|
import mage.abilities.common.WhileSearchingPlayFromLibraryAbility;
|
||||||
import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility;
|
|
||||||
import mage.abilities.costs.*;
|
import mage.abilities.costs.*;
|
||||||
import mage.abilities.costs.mana.AlternateManaPaymentAbility;
|
import mage.abilities.costs.mana.AlternateManaPaymentAbility;
|
||||||
import mage.abilities.costs.mana.ManaCost;
|
import mage.abilities.costs.mana.ManaCost;
|
||||||
|
|
@ -15,7 +14,6 @@ import mage.abilities.costs.mana.ManaCosts;
|
||||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
import mage.abilities.effects.RestrictionEffect;
|
import mage.abilities.effects.RestrictionEffect;
|
||||||
import mage.abilities.effects.RestrictionUntapNotMoreThanEffect;
|
import mage.abilities.effects.RestrictionUntapNotMoreThanEffect;
|
||||||
import mage.abilities.effects.common.LoseControlOnOtherPlayersControllerEffect;
|
|
||||||
import mage.abilities.keyword.*;
|
import mage.abilities.keyword.*;
|
||||||
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
||||||
import mage.abilities.mana.ManaOptions;
|
import mage.abilities.mana.ManaOptions;
|
||||||
|
|
@ -609,11 +607,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (!playerUnderControl.hasLeft() && !playerUnderControl.hasLost()) {
|
if (!playerUnderControl.hasLeft() && !playerUnderControl.hasLost()) {
|
||||||
playerUnderControl.setGameUnderYourControl(false);
|
playerUnderControl.setGameUnderYourControl(false);
|
||||||
}
|
}
|
||||||
DelayedTriggeredAbility ability = new AtTheEndOfTurnStepPostDelayedTriggeredAbility(
|
// control will reset on start of the turn
|
||||||
new LoseControlOnOtherPlayersControllerEffect(this.getLogName(), playerUnderControl.getLogName()));
|
|
||||||
ability.setSourceId(getId());
|
|
||||||
ability.setControllerId(getId());
|
|
||||||
game.addDelayedTriggeredAbility(ability, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2127,9 +2127,10 @@ public final class CardUtil {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not started game
|
// T0 - for not started game
|
||||||
|
// T2 - for starting of the turn
|
||||||
if (gameState.getTurn().getStep() == null) {
|
if (gameState.getTurn().getStep() == null) {
|
||||||
return "T0";
|
return "T" + gameState.getTurnNum();
|
||||||
}
|
}
|
||||||
|
|
||||||
// normal game
|
// normal game
|
||||||
|
|
|
||||||
|
|
@ -160,10 +160,6 @@ public final class GameLog {
|
||||||
return "<font color='" + LOG_COLOR_PLAYER_CONFIRM + "'>" + name + "</font>";
|
return "<font color='" + LOG_COLOR_PLAYER_CONFIRM + "'>" + name + "</font>";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getSmallSecondLineText(String text) {
|
|
||||||
return "<div style='font-size:11pt'>" + text + "</div>";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getColorName(ObjectColor objectColor) {
|
private static String getColorName(ObjectColor objectColor) {
|
||||||
if (objectColor.isMulticolored()) {
|
if (objectColor.isMulticolored()) {
|
||||||
return LOG_COLOR_MULTI;
|
return LOG_COLOR_MULTI;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue